feat(api): standardize REST contracts and auth
This commit is contained in:
@@ -13,6 +13,25 @@ TJWater metadata stores only business snapshots and authorization data:
|
|||||||
The backend does not accept passwords, does not issue local JWTs, and does not
|
The backend does not accept passwords, does not issue local JWTs, and does not
|
||||||
trust frontend-supplied user IDs.
|
trust frontend-supplied user IDs.
|
||||||
|
|
||||||
|
## Fixed Project RBAC
|
||||||
|
|
||||||
|
Project roles are stored directly in
|
||||||
|
`user_project_membership.project_role`; there is no separate role table or
|
||||||
|
user-defined permission editor in this delivery.
|
||||||
|
|
||||||
|
| Role | Main access |
|
||||||
|
| --- | --- |
|
||||||
|
| `modeler` | Model upload/import, simulation, burst, risk, and optimization analysis |
|
||||||
|
| `dispatcher` | SCADA cleaning, simulation and burst analysis |
|
||||||
|
| `auditor` | Project read access and project-scoped audit logs |
|
||||||
|
| `viewer` | WebGIS and read-only risk results |
|
||||||
|
|
||||||
|
Legacy `owner`, `admin`, and `member` values remain supported for existing
|
||||||
|
records. The backend is the authorization boundary; the frontend uses
|
||||||
|
`GET /api/v1/access/context` only to hide unavailable menus and guard routes.
|
||||||
|
System admins receive environment, membership, and global-audit permissions,
|
||||||
|
but still need a project membership for project business APIs.
|
||||||
|
|
||||||
## Login Snapshot Refresh
|
## Login Snapshot Refresh
|
||||||
|
|
||||||
Every authenticated metadata-user resolution validates the Keycloak access token
|
Every authenticated metadata-user resolution validates the Keycloak access token
|
||||||
@@ -77,14 +96,22 @@ Apply metadata patches in order:
|
|||||||
|
|
||||||
1. `resources/sql/004_metadata_auth_management.sql`
|
1. `resources/sql/004_metadata_auth_management.sql`
|
||||||
2. `resources/sql/005_metadata_project_configuration.sql`
|
2. `resources/sql/005_metadata_project_configuration.sql`
|
||||||
|
3. `resources/sql/006_metadata_rbac_roles.sql`
|
||||||
|
|
||||||
`004` creates Keycloak-backed metadata users and project memberships. `005`
|
`004` creates Keycloak-backed metadata users and project memberships. `005`
|
||||||
creates project and project database routing tables with uniqueness, role/type,
|
creates project and project database routing tables with uniqueness, role/type,
|
||||||
and pool-size constraints.
|
and pool-size constraints. `006` extends existing membership constraints with
|
||||||
|
the fixed delivery roles.
|
||||||
|
|
||||||
## Frontend System Management
|
## Frontend System Management
|
||||||
|
|
||||||
`/system-admin` is shown only after `GET /api/v1/admin/me` confirms metadata
|
`/system-admin` is shown only when `GET /api/v1/access/context` returns
|
||||||
admin access. The page lets admins maintain metadata users, project members,
|
`environment.manage`. The page lets admins maintain metadata users, project
|
||||||
projects, project database routing for `biz_data` and `iot_data`, connection
|
members, projects, project database routing for `biz_data` and `iot_data`, and
|
||||||
health checks. This replaces direct SQL editing for normal project onboarding.
|
connection health checks. This replaces direct SQL editing for normal project
|
||||||
|
onboarding.
|
||||||
|
|
||||||
|
Hydraulic model authoring is outside the Web application. Models are prepared
|
||||||
|
in the desktop modeling client and uploaded/imported by an authorized modeler;
|
||||||
|
the system administrator configures the project environment and database
|
||||||
|
routing.
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from fastapi import FastAPI, HTTPException, Request
|
||||||
|
from fastapi.exceptions import RequestValidationError
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class ProblemDetails(BaseModel):
|
||||||
|
"""RFC 9457 compatible error response used by the REST contract."""
|
||||||
|
|
||||||
|
type: str
|
||||||
|
title: str
|
||||||
|
status: int
|
||||||
|
detail: str
|
||||||
|
instance: str
|
||||||
|
code: str
|
||||||
|
trace_id: str
|
||||||
|
errors: list[dict[str, Any]] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
def _trace_id(request: Request) -> str:
|
||||||
|
return request.headers.get("X-Request-Id") or str(uuid4())
|
||||||
|
|
||||||
|
|
||||||
|
def _problem_response(
|
||||||
|
request: Request,
|
||||||
|
*,
|
||||||
|
status_code: int,
|
||||||
|
title: str,
|
||||||
|
detail: str,
|
||||||
|
code: str,
|
||||||
|
errors: list[dict[str, Any]] | None = None,
|
||||||
|
) -> JSONResponse:
|
||||||
|
problem = ProblemDetails(
|
||||||
|
type=f"https://tjwater.example/problems/{code.replace('_', '-')}",
|
||||||
|
title=title,
|
||||||
|
status=status_code,
|
||||||
|
detail=detail,
|
||||||
|
instance=request.url.path,
|
||||||
|
code=code,
|
||||||
|
trace_id=_trace_id(request),
|
||||||
|
errors=errors or [],
|
||||||
|
)
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=status_code,
|
||||||
|
content=problem.model_dump(mode="json"),
|
||||||
|
media_type="application/problem+json",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def install_problem_details_handlers(app: FastAPI) -> None:
|
||||||
|
@app.exception_handler(RequestValidationError)
|
||||||
|
async def validation_error_handler(
|
||||||
|
request: Request,
|
||||||
|
exc: RequestValidationError,
|
||||||
|
) -> JSONResponse:
|
||||||
|
return _problem_response(
|
||||||
|
request,
|
||||||
|
status_code=422,
|
||||||
|
title="Validation error",
|
||||||
|
detail="Request validation failed",
|
||||||
|
code="validation_error",
|
||||||
|
errors=exc.errors(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.exception_handler(HTTPException)
|
||||||
|
async def http_error_handler(request: Request, exc: HTTPException) -> JSONResponse:
|
||||||
|
detail = exc.detail if isinstance(exc.detail, str) else str(exc.detail)
|
||||||
|
code_by_status = {
|
||||||
|
401: "unauthenticated",
|
||||||
|
403: "forbidden",
|
||||||
|
404: "not_found",
|
||||||
|
409: "conflict",
|
||||||
|
422: "validation_error",
|
||||||
|
503: "dependency_unavailable",
|
||||||
|
}
|
||||||
|
return _problem_response(
|
||||||
|
request,
|
||||||
|
status_code=exc.status_code,
|
||||||
|
title=code_by_status.get(exc.status_code, "request_error")
|
||||||
|
.replace("_", " ")
|
||||||
|
.title(),
|
||||||
|
detail=detail,
|
||||||
|
code=code_by_status.get(exc.status_code, "request_error"),
|
||||||
|
)
|
||||||
@@ -12,7 +12,7 @@ from app.infra.db.metadb.repositories.metadata_repository import MetadataReposit
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/access/context", response_model=AccessContextResponse)
|
@router.get("/access-context", response_model=AccessContextResponse)
|
||||||
async def get_access_context(
|
async def get_access_context(
|
||||||
x_project_id: str | None = Header(default=None, alias="X-Project-Id"),
|
x_project_id: str | None = Header(default=None, alias="X-Project-Id"),
|
||||||
current_user=Depends(get_current_metadata_user),
|
current_user=Depends(get_current_metadata_user),
|
||||||
|
|||||||
@@ -151,14 +151,14 @@ async def _upsert_and_audit_metadata_user(
|
|||||||
return MetadataUserResponse.model_validate(user)
|
return MetadataUserResponse.model_validate(user)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/me", response_model=MetadataUserResponse)
|
@router.get("/admin/users/me", response_model=MetadataUserResponse)
|
||||||
async def get_metadata_admin_me(
|
async def get_metadata_admin_me(
|
||||||
current_user=Depends(get_current_metadata_admin),
|
current_user=Depends(get_current_metadata_admin),
|
||||||
) -> MetadataUserResponse:
|
) -> MetadataUserResponse:
|
||||||
return MetadataUserResponse.model_validate(current_user)
|
return MetadataUserResponse.model_validate(current_user)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/users/sync", response_model=MetadataUserResponse)
|
@router.post("/admin/user-syncs", response_model=MetadataUserResponse)
|
||||||
async def sync_metadata_user(
|
async def sync_metadata_user(
|
||||||
payload: MetadataUserSyncRequest,
|
payload: MetadataUserSyncRequest,
|
||||||
current_user=Depends(get_current_metadata_admin),
|
current_user=Depends(get_current_metadata_admin),
|
||||||
@@ -184,7 +184,7 @@ async def sync_metadata_user(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/users/sync/batch", response_model=List[MetadataUserSyncResult])
|
@router.post("/admin/user-syncs/batches", response_model=List[MetadataUserSyncResult])
|
||||||
async def sync_metadata_users_batch(
|
async def sync_metadata_users_batch(
|
||||||
payload: MetadataUsersBatchSyncRequest,
|
payload: MetadataUsersBatchSyncRequest,
|
||||||
current_user=Depends(get_current_metadata_admin),
|
current_user=Depends(get_current_metadata_admin),
|
||||||
@@ -228,7 +228,7 @@ async def sync_metadata_users_batch(
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@router.get("/users", response_model=List[MetadataUserResponse])
|
@router.get("/admin/users", response_model=List[MetadataUserResponse])
|
||||||
async def list_metadata_users(
|
async def list_metadata_users(
|
||||||
skip: int = Query(0, ge=0),
|
skip: int = Query(0, ge=0),
|
||||||
limit: int = Query(100, ge=1, le=1000),
|
limit: int = Query(100, ge=1, le=1000),
|
||||||
@@ -239,7 +239,7 @@ async def list_metadata_users(
|
|||||||
return [MetadataUserResponse.model_validate(user) for user in users]
|
return [MetadataUserResponse.model_validate(user) for user in users]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/projects", response_model=List[AdminProjectResponse])
|
@router.get("/admin/projects", response_model=List[AdminProjectResponse])
|
||||||
async def list_admin_projects(
|
async def list_admin_projects(
|
||||||
current_user=Depends(get_current_metadata_admin),
|
current_user=Depends(get_current_metadata_admin),
|
||||||
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
||||||
@@ -249,7 +249,7 @@ async def list_admin_projects(
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/projects",
|
"/admin/projects",
|
||||||
response_model=AdminProjectResponse,
|
response_model=AdminProjectResponse,
|
||||||
status_code=status.HTTP_201_CREATED,
|
status_code=status.HTTP_201_CREATED,
|
||||||
)
|
)
|
||||||
@@ -293,7 +293,7 @@ async def create_admin_project(
|
|||||||
|
|
||||||
|
|
||||||
@router.patch(
|
@router.patch(
|
||||||
"/projects/{project_id}",
|
"/admin/projects/{project_id}",
|
||||||
response_model=AdminProjectResponse,
|
response_model=AdminProjectResponse,
|
||||||
)
|
)
|
||||||
async def update_admin_project(
|
async def update_admin_project(
|
||||||
@@ -332,7 +332,7 @@ async def update_admin_project(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/projects/{project_id}/databases",
|
"/admin/projects/{project_id}/databases",
|
||||||
response_model=List[ProjectDatabaseResponse],
|
response_model=List[ProjectDatabaseResponse],
|
||||||
)
|
)
|
||||||
async def list_project_databases(
|
async def list_project_databases(
|
||||||
@@ -348,7 +348,7 @@ async def list_project_databases(
|
|||||||
|
|
||||||
|
|
||||||
@router.put(
|
@router.put(
|
||||||
"/projects/{project_id}/databases",
|
"/admin/projects/{project_id}/databases",
|
||||||
response_model=ProjectDatabaseResponse,
|
response_model=ProjectDatabaseResponse,
|
||||||
)
|
)
|
||||||
async def upsert_project_database(
|
async def upsert_project_database(
|
||||||
@@ -421,7 +421,7 @@ async def upsert_project_database(
|
|||||||
|
|
||||||
|
|
||||||
@router.delete(
|
@router.delete(
|
||||||
"/projects/{project_id}/databases/{db_role}",
|
"/admin/projects/{project_id}/databases/{db_role}",
|
||||||
status_code=status.HTTP_204_NO_CONTENT,
|
status_code=status.HTTP_204_NO_CONTENT,
|
||||||
)
|
)
|
||||||
async def delete_project_database(
|
async def delete_project_database(
|
||||||
@@ -449,7 +449,7 @@ async def delete_project_database(
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/projects/{project_id}/databases/{db_role}/health",
|
"/admin/projects/{project_id}/databases/{db_role}/health-checks",
|
||||||
response_model=ProjectDatabaseHealthResponse,
|
response_model=ProjectDatabaseHealthResponse,
|
||||||
)
|
)
|
||||||
async def check_project_database_health(
|
async def check_project_database_health(
|
||||||
@@ -504,7 +504,7 @@ async def check_project_database_health(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/users/{user_id}", response_model=MetadataUserResponse)
|
@router.get("/admin/users/{user_id}", response_model=MetadataUserResponse)
|
||||||
async def get_metadata_user(
|
async def get_metadata_user(
|
||||||
user_id: UUID = Path(...),
|
user_id: UUID = Path(...),
|
||||||
current_user=Depends(get_current_metadata_admin),
|
current_user=Depends(get_current_metadata_admin),
|
||||||
@@ -516,7 +516,7 @@ async def get_metadata_user(
|
|||||||
return MetadataUserResponse.model_validate(user)
|
return MetadataUserResponse.model_validate(user)
|
||||||
|
|
||||||
|
|
||||||
@router.patch("/users/{user_id}", response_model=MetadataUserResponse)
|
@router.patch("/admin/users/{user_id}", response_model=MetadataUserResponse)
|
||||||
async def update_metadata_user(
|
async def update_metadata_user(
|
||||||
payload: MetadataUserUpdateRequest,
|
payload: MetadataUserUpdateRequest,
|
||||||
user_id: UUID = Path(...),
|
user_id: UUID = Path(...),
|
||||||
@@ -549,7 +549,7 @@ async def update_metadata_user(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/projects/{project_id}/members",
|
"/admin/projects/{project_id}/members",
|
||||||
response_model=List[ProjectMemberResponse],
|
response_model=List[ProjectMemberResponse],
|
||||||
)
|
)
|
||||||
async def list_project_members(
|
async def list_project_members(
|
||||||
@@ -567,7 +567,7 @@ async def list_project_members(
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/projects/{project_id}/members",
|
"/admin/projects/{project_id}/members",
|
||||||
response_model=ProjectMemberResponse,
|
response_model=ProjectMemberResponse,
|
||||||
status_code=status.HTTP_201_CREATED,
|
status_code=status.HTTP_201_CREATED,
|
||||||
)
|
)
|
||||||
@@ -622,7 +622,7 @@ async def add_project_member(
|
|||||||
|
|
||||||
|
|
||||||
@router.patch(
|
@router.patch(
|
||||||
"/projects/{project_id}/members/{user_id}",
|
"/admin/projects/{project_id}/members/{user_id}",
|
||||||
response_model=ProjectMemberResponse,
|
response_model=ProjectMemberResponse,
|
||||||
)
|
)
|
||||||
async def update_project_member(
|
async def update_project_member(
|
||||||
@@ -668,7 +668,7 @@ async def update_project_member(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/projects/{project_id}/members/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
@router.delete("/admin/projects/{project_id}/members/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def remove_project_member(
|
async def remove_project_member(
|
||||||
project_id: UUID = Path(...),
|
project_id: UUID = Path(...),
|
||||||
user_id: UUID = Path(...),
|
user_id: UUID = Path(...),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class AgentAuthContextResponse(BaseModel):
|
|||||||
token_expires_at: str | None = None
|
token_expires_at: str | None = None
|
||||||
|
|
||||||
|
|
||||||
@router.get("/agent/auth/context", response_model=AgentAuthContextResponse)
|
@router.get("/agent-auth-context", response_model=AgentAuthContextResponse)
|
||||||
async def get_agent_auth_context(
|
async def get_agent_auth_context(
|
||||||
ctx: ProjectContext = Depends(get_project_context),
|
ctx: ProjectContext = Depends(get_project_context),
|
||||||
current_user=Depends(get_current_metadata_user),
|
current_user=Depends(get_current_metadata_user),
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ async def get_audit_repository(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/logs",
|
"/audit-logs",
|
||||||
summary="查询审计日志",
|
summary="查询审计日志",
|
||||||
description="查询审计日志(仅管理员)",
|
description="查询审计日志(仅管理员)",
|
||||||
response_model=list[AuditLogResponse],
|
response_model=list[AuditLogResponse],
|
||||||
@@ -59,7 +59,7 @@ async def get_audit_logs(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/logs/count",
|
"/audit-logs/count",
|
||||||
summary="获取审计日志总数",
|
summary="获取审计日志总数",
|
||||||
description="获取审计日志总数(仅管理员)",
|
description="获取审计日志总数(仅管理员)",
|
||||||
)
|
)
|
||||||
@@ -84,7 +84,7 @@ async def get_audit_logs_count(
|
|||||||
return {"count": count}
|
return {"count": count}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/session-events", status_code=status.HTTP_204_NO_CONTENT)
|
@router.post("/audit-events", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def record_session_event(
|
async def record_session_event(
|
||||||
payload: SessionAuditEventRequest,
|
payload: SessionAuditEventRequest,
|
||||||
request: Request,
|
request: Request,
|
||||||
@@ -105,7 +105,7 @@ async def record_session_event(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/logs/my",
|
"/audit-logs/mine",
|
||||||
summary="查询我的审计日志",
|
summary="查询我的审计日志",
|
||||||
description="查询当前用户的审计日志",
|
description="查询当前用户的审计日志",
|
||||||
response_model=list[AuditLogResponse],
|
response_model=list[AuditLogResponse],
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class BurstDetectionRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/detect/",
|
"/burst-detections",
|
||||||
summary="执行爆管检测",
|
summary="执行爆管检测",
|
||||||
description="基于压力观测数据和其他参数执行爆管检测分析"
|
description="基于压力观测数据和其他参数执行爆管检测分析"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class BurstLocationRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/locate/",
|
"/burst-locations",
|
||||||
summary="执行爆管定位",
|
summary="执行爆管定位",
|
||||||
description="基于压力和流量数据定位管网中的爆管位置"
|
description="基于压力和流量数据定位管网中的爆管位置"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from app.infra.cache.redis_client import redis_client
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.post("/clearrediskey/", summary="清除单个缓存键", description="根据键名清除单个Redis缓存")
|
@router.delete("/redis-keys/detail", summary="清除单个缓存键", description="根据键名清除单个Redis缓存")
|
||||||
async def fastapi_clear_redis_key(key: str = Query(..., description="缓存键名")):
|
async def fastapi_clear_redis_key(key: str = Query(..., description="缓存键名")):
|
||||||
"""
|
"""
|
||||||
清除单个缓存键
|
清除单个缓存键
|
||||||
@@ -14,7 +14,7 @@ async def fastapi_clear_redis_key(key: str = Query(..., description="缓存键
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@router.post("/clearrediskeys/", summary="清除匹配的缓存键", description="根据模式清除匹配的Redis缓存键")
|
@router.delete("/redis-keys", summary="清除匹配的缓存键", description="根据模式清除匹配的Redis缓存键")
|
||||||
async def fastapi_clear_redis_keys(keys: str = Query(..., description="缓存键模式(支持通配符)")):
|
async def fastapi_clear_redis_keys(keys: str = Query(..., description="缓存键模式(支持通配符)")):
|
||||||
"""
|
"""
|
||||||
清除匹配的缓存键
|
清除匹配的缓存键
|
||||||
@@ -29,7 +29,7 @@ async def fastapi_clear_redis_keys(keys: str = Query(..., description="缓存键
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@router.post("/clearallredis/", summary="清除所有缓存", description="清空整个Redis数据库的所有缓存")
|
@router.delete("/all-redis", summary="清除所有缓存", description="清空整个Redis数据库的所有缓存")
|
||||||
async def fastapi_clear_all_redis():
|
async def fastapi_clear_all_redis():
|
||||||
"""
|
"""
|
||||||
清除所有缓存
|
清除所有缓存
|
||||||
@@ -40,7 +40,7 @@ async def fastapi_clear_all_redis():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@router.get("/queryredis/", summary="查询缓存键列表", description="获取Redis中所有的缓存键")
|
@router.get("/redis", summary="查询缓存键列表", description="获取Redis中所有的缓存键")
|
||||||
async def fastapi_query_redis():
|
async def fastapi_query_redis():
|
||||||
"""
|
"""
|
||||||
查询缓存键列表
|
查询缓存键列表
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getcontrolschema/", summary="获取控制架构", description="获取网络中控制对象的架构定义")
|
@router.get("/network-schemas/control", summary="获取控制架构", description="获取网络中控制对象的架构定义")
|
||||||
async def fastapi_get_control_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_control_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取控制架构。
|
"""获取控制架构。
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ async def fastapi_get_control_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_control_schema(network)
|
return get_control_schema(network)
|
||||||
|
|
||||||
@router.get("/getcontrolproperties/", summary="获取控制属性", description="获取指定网络中的控制属性信息")
|
@router.get("/controls/properties", summary="获取控制属性", description="获取指定网络中的控制属性信息")
|
||||||
async def fastapi_get_control_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
async def fastapi_get_control_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
||||||
"""获取控制属性。
|
"""获取控制属性。
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ async def fastapi_get_control_properties(network: str = Query(..., description="
|
|||||||
"""
|
"""
|
||||||
return get_control(network)
|
return get_control(network)
|
||||||
|
|
||||||
@router.post("/setcontrolproperties/", response_model=None, summary="设置控制属性", description="更新指定网络中的控制属性")
|
@router.patch("/controls/properties", response_model=None, summary="设置控制属性", description="更新指定网络中的控制属性")
|
||||||
async def fastapi_set_control_properties(
|
async def fastapi_set_control_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -41,7 +41,7 @@ async def fastapi_set_control_properties(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_control(network, ChangeSet(props))
|
return set_control(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/getruleschema/", summary="获取规则架构", description="获取网络中规则对象的架构定义")
|
@router.get("/rule-schemas", summary="获取规则架构", description="获取网络中规则对象的架构定义")
|
||||||
async def fastapi_get_rule_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_rule_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取规则架构。
|
"""获取规则架构。
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ async def fastapi_get_rule_schema(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return get_rule_schema(network)
|
return get_rule_schema(network)
|
||||||
|
|
||||||
@router.get("/getruleproperties/", summary="获取规则属性", description="获取指定网络中的规则属性信息")
|
@router.get("/rule-properties", summary="获取规则属性", description="获取指定网络中的规则属性信息")
|
||||||
async def fastapi_get_rule_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
async def fastapi_get_rule_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
||||||
"""获取规则属性。
|
"""获取规则属性。
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ async def fastapi_get_rule_properties(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_rule(network)
|
return get_rule(network)
|
||||||
|
|
||||||
@router.post("/setruleproperties/", response_model=None, summary="设置规则属性", description="更新指定网络中的规则属性")
|
@router.patch("/rule-properties", response_model=None, summary="设置规则属性", description="更新指定网络中的规则属性")
|
||||||
async def fastapi_set_rule_properties(
|
async def fastapi_set_rule_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getcurveschema", summary="获取曲线架构", description="获取网络中曲线对象的架构定义")
|
@router.get("/network-schemas/curve", summary="获取曲线架构", description="获取网络中曲线对象的架构定义")
|
||||||
async def fastapi_get_curve_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_curve_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取曲线架构。
|
"""获取曲线架构。
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ async def fastapi_get_curve_schema(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return get_curve_schema(network)
|
return get_curve_schema(network)
|
||||||
|
|
||||||
@router.post("/addcurve/", response_model=None, summary="添加曲线", description="在网络中添加一条新的曲线")
|
@router.post("/curves", response_model=None, summary="添加曲线", description="在网络中添加一条新的曲线")
|
||||||
async def fastapi_add_curve(
|
async def fastapi_add_curve(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
curve: str = Query(..., description="曲线ID"),
|
curve: str = Query(..., description="曲线ID"),
|
||||||
@@ -38,7 +38,7 @@ async def fastapi_add_curve(
|
|||||||
} | props
|
} | props
|
||||||
return add_curve(network, ChangeSet(ps))
|
return add_curve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/deletecurve/", response_model=None, summary="删除曲线", description="从网络中删除指定的曲线")
|
@router.delete("/curves", response_model=None, summary="删除曲线", description="从网络中删除指定的曲线")
|
||||||
async def fastapi_delete_curve(
|
async def fastapi_delete_curve(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
curve: str = Query(..., description="曲线ID")
|
curve: str = Query(..., description="曲线ID")
|
||||||
@@ -50,7 +50,7 @@ async def fastapi_delete_curve(
|
|||||||
ps = {"id": curve}
|
ps = {"id": curve}
|
||||||
return delete_curve(network, ChangeSet(ps))
|
return delete_curve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getcurveproperties/", summary="获取曲线属性", description="获取指定曲线的属性信息")
|
@router.get("/curves/properties", summary="获取曲线属性", description="获取指定曲线的属性信息")
|
||||||
async def fastapi_get_curve_properties(
|
async def fastapi_get_curve_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
curve: str = Query(..., description="曲线ID")
|
curve: str = Query(..., description="曲线ID")
|
||||||
@@ -61,7 +61,7 @@ async def fastapi_get_curve_properties(
|
|||||||
"""
|
"""
|
||||||
return get_curve(network, curve)
|
return get_curve(network, curve)
|
||||||
|
|
||||||
@router.post("/setcurveproperties/", response_model=None, summary="设置曲线属性", description="更新指定曲线的属性")
|
@router.patch("/curves/properties", response_model=None, summary="设置曲线属性", description="更新指定曲线的属性")
|
||||||
async def fastapi_set_curve_properties(
|
async def fastapi_set_curve_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
curve: str = Query(..., description="曲线ID"),
|
curve: str = Query(..., description="曲线ID"),
|
||||||
@@ -75,7 +75,7 @@ async def fastapi_set_curve_properties(
|
|||||||
ps = {"id": curve} | props
|
ps = {"id": curve} | props
|
||||||
return set_curve(network, ChangeSet(ps))
|
return set_curve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getcurves/", summary="获取所有曲线", description="获取网络中的所有曲线列表")
|
@router.get("/curves", summary="获取所有曲线", description="获取网络中的所有曲线列表")
|
||||||
async def fastapi_get_curves(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]:
|
async def fastapi_get_curves(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]:
|
||||||
"""获取所有曲线。
|
"""获取所有曲线。
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ async def fastapi_get_curves(network: str = Query(..., description="管网名称
|
|||||||
"""
|
"""
|
||||||
return get_curves(network)
|
return get_curves(network)
|
||||||
|
|
||||||
@router.get("/iscurve/", summary="检查曲线存在性", description="检查指定的曲线是否存在")
|
@router.get("/curves/existence", summary="检查曲线存在性", description="检查指定的曲线是否存在")
|
||||||
async def fastapi_is_curve(
|
async def fastapi_is_curve(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
curve: str = Query(..., description="曲线ID")
|
curve: str = Query(..., description="曲线ID")
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/gettimeschema", summary="获取时间选项架构", description="获取网络中时间选项的架构定义")
|
@router.get("/network-schemas/time", summary="获取时间选项架构", description="获取网络中时间选项的架构定义")
|
||||||
async def fastapi_get_time_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_time_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取时间选项架构。
|
"""获取时间选项架构。
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ async def fastapi_get_time_schema(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return get_time_schema(network)
|
return get_time_schema(network)
|
||||||
|
|
||||||
@router.get("/gettimeproperties/", summary="获取时间选项属性", description="获取指定网络中的时间选项属性信息")
|
@router.get("/network-options/time", summary="获取时间选项属性", description="获取指定网络中的时间选项属性信息")
|
||||||
async def fastapi_get_time_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
async def fastapi_get_time_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
||||||
"""获取时间选项属性。
|
"""获取时间选项属性。
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ async def fastapi_get_time_properties(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_time(network)
|
return get_time(network)
|
||||||
|
|
||||||
@router.post("/settimeproperties/", response_model=None, summary="设置时间选项属性", description="更新指定网络中的时间选项属性")
|
@router.patch("/time-properties", response_model=None, summary="设置时间选项属性", description="更新指定网络中的时间选项属性")
|
||||||
async def fastapi_set_time_properties(
|
async def fastapi_set_time_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -47,7 +47,7 @@ async def fastapi_set_time_properties(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_time(network, ChangeSet(props))
|
return set_time(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/getenergyschema/", summary="获取能耗选项架构", description="获取网络中能耗选项的架构定义")
|
@router.get("/network-schemas/energy", summary="获取能耗选项架构", description="获取网络中能耗选项的架构定义")
|
||||||
async def fastapi_get_energy_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_energy_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取能耗选项架构。
|
"""获取能耗选项架构。
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ async def fastapi_get_energy_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_energy_schema(network)
|
return get_energy_schema(network)
|
||||||
|
|
||||||
@router.get("/getenergyproperties/", summary="获取能耗选项属性", description="获取指定网络中的能耗选项属性信息")
|
@router.get("/network-options/energy", summary="获取能耗选项属性", description="获取指定网络中的能耗选项属性信息")
|
||||||
async def fastapi_get_energy_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
async def fastapi_get_energy_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
||||||
"""获取能耗选项属性。
|
"""获取能耗选项属性。
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ async def fastapi_get_energy_properties(network: str = Query(..., description="
|
|||||||
"""
|
"""
|
||||||
return get_energy(network)
|
return get_energy(network)
|
||||||
|
|
||||||
@router.post("/setenergyproperties/", response_model=None, summary="设置能耗选项属性", description="更新指定网络中的能耗选项属性")
|
@router.patch("/energy-properties", response_model=None, summary="设置能耗选项属性", description="更新指定网络中的能耗选项属性")
|
||||||
async def fastapi_set_energy_properties(
|
async def fastapi_set_energy_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -75,7 +75,7 @@ async def fastapi_set_energy_properties(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_energy(network, ChangeSet(props))
|
return set_energy(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/getpumpenergyschema/", summary="获取泵能耗选项架构", description="获取网络中泵能耗选项的架构定义")
|
@router.get("/network-schemas/pump-energy", summary="获取泵能耗选项架构", description="获取网络中泵能耗选项的架构定义")
|
||||||
async def fastapi_get_pump_energy_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_pump_energy_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取泵能耗选项架构。
|
"""获取泵能耗选项架构。
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ async def fastapi_get_pump_energy_schema(network: str = Query(..., description="
|
|||||||
"""
|
"""
|
||||||
return get_pump_energy_schema(network)
|
return get_pump_energy_schema(network)
|
||||||
|
|
||||||
@router.get("/getpumpenergyproperties//", summary="获取泵能耗属性", description="获取指定泵的能耗属性信息")
|
@router.get("/network-options/pump-energy", summary="获取泵能耗属性", description="获取指定泵的能耗属性信息")
|
||||||
async def fastapi_get_pump_energy_proeprties(
|
async def fastapi_get_pump_energy_proeprties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="泵ID")
|
pump: str = Query(..., description="泵ID")
|
||||||
@@ -94,7 +94,7 @@ async def fastapi_get_pump_energy_proeprties(
|
|||||||
"""
|
"""
|
||||||
return get_pump_energy(network, pump)
|
return get_pump_energy(network, pump)
|
||||||
|
|
||||||
@router.get("/setpumpenergyproperties//", response_model=None, summary="设置泵能耗属性", description="更新指定泵的能耗属性")
|
@router.patch("/network-options/pump-energy", response_model=None, summary="设置泵能耗属性", description="更新指定泵的能耗属性")
|
||||||
async def fastapi_set_pump_energy_properties(
|
async def fastapi_set_pump_energy_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="泵ID"),
|
pump: str = Query(..., description="泵ID"),
|
||||||
@@ -108,7 +108,7 @@ async def fastapi_set_pump_energy_properties(
|
|||||||
ps = {"id": pump} | props
|
ps = {"id": pump} | props
|
||||||
return set_pump_energy(network, ChangeSet(ps))
|
return set_pump_energy(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getoptionschema/", summary="获取选项架构", description="获取网络中选项对象的架构定义")
|
@router.get("/network-schemas/option", summary="获取选项架构", description="获取网络中选项对象的架构定义")
|
||||||
async def fastapi_get_option_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_option_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取选项架构。
|
"""获取选项架构。
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ async def fastapi_get_option_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_option_v3_schema(network)
|
return get_option_v3_schema(network)
|
||||||
|
|
||||||
@router.get("/getoptionproperties/", summary="获取选项属性", description="获取指定网络中的选项属性信息")
|
@router.get("/network-options", summary="获取选项属性", description="获取指定网络中的选项属性信息")
|
||||||
async def fastapi_get_option_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
async def fastapi_get_option_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
||||||
"""获取选项属性。
|
"""获取选项属性。
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ async def fastapi_get_option_properties(network: str = Query(..., description="
|
|||||||
"""
|
"""
|
||||||
return get_option_v3(network)
|
return get_option_v3(network)
|
||||||
|
|
||||||
@router.post("/setoptionproperties/", response_model=None, summary="设置选项属性", description="更新指定网络中的选项属性")
|
@router.patch("/network-options", response_model=None, summary="设置选项属性", description="更新指定网络中的选项属性")
|
||||||
async def fastapi_set_option_properties(
|
async def fastapi_set_option_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getpatternschema", summary="获取模式架构", description="获取网络中模式对象的架构定义")
|
@router.get("/network-schemas/pattern", summary="获取模式架构", description="获取网络中模式对象的架构定义")
|
||||||
async def fastapi_get_pattern_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_pattern_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取模式架构。
|
"""获取模式架构。
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ async def fastapi_get_pattern_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_pattern_schema(network)
|
return get_pattern_schema(network)
|
||||||
|
|
||||||
@router.post("/addpattern/", response_model=None, summary="添加模式", description="在网络中添加一个新的模式")
|
@router.post("/patterns", response_model=None, summary="添加模式", description="在网络中添加一个新的模式")
|
||||||
async def fastapi_add_pattern(
|
async def fastapi_add_pattern(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pattern: str = Query(..., description="模式ID"),
|
pattern: str = Query(..., description="模式ID"),
|
||||||
@@ -38,7 +38,7 @@ async def fastapi_add_pattern(
|
|||||||
} | props
|
} | props
|
||||||
return add_pattern(network, ChangeSet(ps))
|
return add_pattern(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/deletepattern/", response_model=None, summary="删除模式", description="从网络中删除指定的模式")
|
@router.delete("/patterns", response_model=None, summary="删除模式", description="从网络中删除指定的模式")
|
||||||
async def fastapi_delete_pattern(
|
async def fastapi_delete_pattern(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pattern: str = Query(..., description="模式ID")
|
pattern: str = Query(..., description="模式ID")
|
||||||
@@ -50,7 +50,7 @@ async def fastapi_delete_pattern(
|
|||||||
ps = {"id": pattern}
|
ps = {"id": pattern}
|
||||||
return delete_pattern(network, ChangeSet(ps))
|
return delete_pattern(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getpatternproperties/", summary="获取模式属性", description="获取指定模式的属性信息")
|
@router.get("/patterns/properties", summary="获取模式属性", description="获取指定模式的属性信息")
|
||||||
async def fastapi_get_pattern_properties(
|
async def fastapi_get_pattern_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pattern: str = Query(..., description="模式ID")
|
pattern: str = Query(..., description="模式ID")
|
||||||
@@ -61,7 +61,7 @@ async def fastapi_get_pattern_properties(
|
|||||||
"""
|
"""
|
||||||
return get_pattern(network, pattern)
|
return get_pattern(network, pattern)
|
||||||
|
|
||||||
@router.post("/setpatternproperties/", response_model=None, summary="设置模式属性", description="更新指定模式的属性")
|
@router.patch("/patterns/properties", response_model=None, summary="设置模式属性", description="更新指定模式的属性")
|
||||||
async def fastapi_set_pattern_properties(
|
async def fastapi_set_pattern_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pattern: str = Query(..., description="模式ID"),
|
pattern: str = Query(..., description="模式ID"),
|
||||||
@@ -75,7 +75,7 @@ async def fastapi_set_pattern_properties(
|
|||||||
ps = {"id": pattern} | props
|
ps = {"id": pattern} | props
|
||||||
return set_pattern(network, ChangeSet(ps))
|
return set_pattern(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/ispattern/", summary="检查模式存在性", description="检查指定的模式是否存在")
|
@router.get("/patterns/existence", summary="检查模式存在性", description="检查指定的模式是否存在")
|
||||||
async def fastapi_is_pattern(
|
async def fastapi_is_pattern(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pattern: str = Query(..., description="模式ID")
|
pattern: str = Query(..., description="模式ID")
|
||||||
@@ -86,7 +86,7 @@ async def fastapi_is_pattern(
|
|||||||
"""
|
"""
|
||||||
return is_pattern(network, pattern)
|
return is_pattern(network, pattern)
|
||||||
|
|
||||||
@router.get("/getpatterns/", summary="获取所有模式", description="获取网络中的所有模式列表")
|
@router.get("/patterns", summary="获取所有模式", description="获取网络中的所有模式列表")
|
||||||
async def fastapi_get_patterns(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]:
|
async def fastapi_get_patterns(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]:
|
||||||
"""获取所有模式。
|
"""获取所有模式。
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getqualityschema/", summary="获取水质架构", description="获取网络中水质对象的架构定义")
|
@router.get("/network-schemas/quality", summary="获取水质架构", description="获取网络中水质对象的架构定义")
|
||||||
async def fastapi_get_quality_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_quality_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取水质架构。
|
"""获取水质架构。
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ async def fastapi_get_quality_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_quality_schema(network)
|
return get_quality_schema(network)
|
||||||
|
|
||||||
@router.get("/getqualityproperties/", summary="获取水质属性", description="获取指定节点的水质属性信息")
|
@router.get("/quality-configurations/properties", summary="获取水质属性", description="获取指定节点的水质属性信息")
|
||||||
async def fastapi_get_quality_properties(
|
async def fastapi_get_quality_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
node: str = Query(..., description="节点ID")
|
node: str = Query(..., description="节点ID")
|
||||||
@@ -51,7 +51,7 @@ async def fastapi_get_quality_properties(
|
|||||||
"""
|
"""
|
||||||
return get_quality(network, node)
|
return get_quality(network, node)
|
||||||
|
|
||||||
@router.post("/setqualityproperties/", response_model=None, summary="设置水质属性", description="更新指定节点的水质属性")
|
@router.patch("/quality-configurations/properties", response_model=None, summary="设置水质属性", description="更新指定节点的水质属性")
|
||||||
async def fastapi_set_quality_properties(
|
async def fastapi_set_quality_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -63,7 +63,7 @@ async def fastapi_set_quality_properties(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_quality(network, ChangeSet(props))
|
return set_quality(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/getemitterschema", summary="获取发射器架构", description="获取网络中发射器对象的架构定义")
|
@router.get("/network-schemas/emitter", summary="获取发射器架构", description="获取网络中发射器对象的架构定义")
|
||||||
async def fastapi_get_emitter_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_emitter_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取发射器架构。
|
"""获取发射器架构。
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ async def fastapi_get_emitter_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_emitter_schema(network)
|
return get_emitter_schema(network)
|
||||||
|
|
||||||
@router.get("/getemitterproperties/", summary="获取发射器属性", description="获取指定连接点的发射器属性信息")
|
@router.get("/emitters/properties", summary="获取发射器属性", description="获取指定连接点的发射器属性信息")
|
||||||
async def fastapi_get_emitter_properties(
|
async def fastapi_get_emitter_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="连接点ID")
|
junction: str = Query(..., description="连接点ID")
|
||||||
@@ -82,7 +82,7 @@ async def fastapi_get_emitter_properties(
|
|||||||
"""
|
"""
|
||||||
return get_emitter(network, junction)
|
return get_emitter(network, junction)
|
||||||
|
|
||||||
@router.post("/setemitterproperties/", response_model=None, summary="设置发射器属性", description="更新指定连接点的发射器属性")
|
@router.patch("/emitters/properties", response_model=None, summary="设置发射器属性", description="更新指定连接点的发射器属性")
|
||||||
async def fastapi_set_emitter_properties(
|
async def fastapi_set_emitter_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="连接点ID"),
|
junction: str = Query(..., description="连接点ID"),
|
||||||
@@ -96,7 +96,7 @@ async def fastapi_set_emitter_properties(
|
|||||||
ps = {"junction": junction} | props
|
ps = {"junction": junction} | props
|
||||||
return set_emitter(network, ChangeSet(ps))
|
return set_emitter(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getsourcechema/", summary="获取水源架构", description="获取网络中水源对象的架构定义")
|
@router.get("/network-schemas/source", summary="获取水源架构", description="获取网络中水源对象的架构定义")
|
||||||
async def fastapi_get_source_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_source_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取水源架构。
|
"""获取水源架构。
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ async def fastapi_get_source_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_source_schema(network)
|
return get_source_schema(network)
|
||||||
|
|
||||||
@router.get("/getsource/", summary="获取水源属性", description="获取指定节点的水源属性信息")
|
@router.get("/sources/detail", summary="获取水源属性", description="获取指定节点的水源属性信息")
|
||||||
async def fastapi_get_source(
|
async def fastapi_get_source(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
node: str = Query(..., description="节点ID")
|
node: str = Query(..., description="节点ID")
|
||||||
@@ -115,7 +115,7 @@ async def fastapi_get_source(
|
|||||||
"""
|
"""
|
||||||
return get_source(network, node)
|
return get_source(network, node)
|
||||||
|
|
||||||
@router.post("/setsource/", response_model=None, summary="设置水源属性", description="更新指定节点的水源属性")
|
@router.patch("/sources", response_model=None, summary="设置水源属性", description="更新指定节点的水源属性")
|
||||||
async def fastapi_set_source(
|
async def fastapi_set_source(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -127,7 +127,7 @@ async def fastapi_set_source(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_source(network, ChangeSet(props))
|
return set_source(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/addsource/", response_model=None, summary="添加水源", description="在网络中添加一个新的水源")
|
@router.post("/sources", response_model=None, summary="添加水源", description="在网络中添加一个新的水源")
|
||||||
async def fastapi_add_source(
|
async def fastapi_add_source(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -139,7 +139,7 @@ async def fastapi_add_source(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_source(network, ChangeSet(props))
|
return add_source(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/deletesource/", response_model=None, summary="删除水源", description="从网络中删除指定节点的水源")
|
@router.delete("/sources", response_model=None, summary="删除水源", description="从网络中删除指定节点的水源")
|
||||||
async def fastapi_delete_source(
|
async def fastapi_delete_source(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
node: str = Query(..., description="节点ID")
|
node: str = Query(..., description="节点ID")
|
||||||
@@ -151,7 +151,7 @@ async def fastapi_delete_source(
|
|||||||
props = {"node": node}
|
props = {"node": node}
|
||||||
return delete_source(network, ChangeSet(props))
|
return delete_source(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/getreactionschema/", summary="获取反应架构", description="获取网络中反应对象的架构定义")
|
@router.get("/network-schemas/reaction", summary="获取反应架构", description="获取网络中反应对象的架构定义")
|
||||||
async def fastapi_get_reaction_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_reaction_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取反应架构。
|
"""获取反应架构。
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ async def fastapi_get_reaction_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_reaction_schema(network)
|
return get_reaction_schema(network)
|
||||||
|
|
||||||
@router.get("/getreaction/", summary="获取反应属性", description="获取指定网络中的反应属性信息")
|
@router.get("/reactions/detail", summary="获取反应属性", description="获取指定网络中的反应属性信息")
|
||||||
async def fastapi_get_reaction(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
async def fastapi_get_reaction(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
||||||
"""获取反应属性。
|
"""获取反应属性。
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ async def fastapi_get_reaction(network: str = Query(..., description="管网名
|
|||||||
"""
|
"""
|
||||||
return get_reaction(network)
|
return get_reaction(network)
|
||||||
|
|
||||||
@router.post("/setreaction/", response_model=None, summary="设置反应属性", description="更新指定网络中的反应属性")
|
@router.patch("/reactions", response_model=None, summary="设置反应属性", description="更新指定网络中的反应属性")
|
||||||
async def fastapi_set_reaction(
|
async def fastapi_set_reaction(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -179,7 +179,7 @@ async def fastapi_set_reaction(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_reaction(network, ChangeSet(props))
|
return set_reaction(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/getpipereactionschema/", summary="获取管道反应架构", description="获取网络中管道反应对象的架构定义")
|
@router.get("/network-schemas/pipe-reaction", summary="获取管道反应架构", description="获取网络中管道反应对象的架构定义")
|
||||||
async def fastapi_get_pipe_reaction_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_pipe_reaction_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取管道反应架构。
|
"""获取管道反应架构。
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ async def fastapi_get_pipe_reaction_schema(network: str = Query(..., description
|
|||||||
"""
|
"""
|
||||||
return get_pipe_reaction_schema(network)
|
return get_pipe_reaction_schema(network)
|
||||||
|
|
||||||
@router.get("/getpipereaction/", summary="获取管道反应属性", description="获取指定管道的反应属性信息")
|
@router.get("/pipe-reactions/detail", summary="获取管道反应属性", description="获取指定管道的反应属性信息")
|
||||||
async def fastapi_get_pipe_reaction(
|
async def fastapi_get_pipe_reaction(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID")
|
pipe: str = Query(..., description="管道ID")
|
||||||
@@ -198,7 +198,7 @@ async def fastapi_get_pipe_reaction(
|
|||||||
"""
|
"""
|
||||||
return get_pipe_reaction(network, pipe)
|
return get_pipe_reaction(network, pipe)
|
||||||
|
|
||||||
@router.post("/setpipereaction/", response_model=None, summary="设置管道反应属性", description="更新指定管道的反应属性")
|
@router.patch("/pipe-reactions", response_model=None, summary="设置管道反应属性", description="更新指定管道的反应属性")
|
||||||
async def fastapi_set_pipe_reaction(
|
async def fastapi_set_pipe_reaction(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -210,7 +210,7 @@ async def fastapi_set_pipe_reaction(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_pipe_reaction(network, ChangeSet(props))
|
return set_pipe_reaction(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/gettankreactionschema/", summary="获取水池反应架构", description="获取网络中水池反应对象的架构定义")
|
@router.get("/network-schemas/tank-reaction", summary="获取水池反应架构", description="获取网络中水池反应对象的架构定义")
|
||||||
async def fastapi_get_tank_reaction_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_tank_reaction_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取水池反应架构。
|
"""获取水池反应架构。
|
||||||
|
|
||||||
@@ -218,7 +218,7 @@ async def fastapi_get_tank_reaction_schema(network: str = Query(..., description
|
|||||||
"""
|
"""
|
||||||
return get_tank_reaction_schema(network)
|
return get_tank_reaction_schema(network)
|
||||||
|
|
||||||
@router.get("/gettankreaction/", summary="获取水池反应属性", description="获取指定水池的反应属性信息")
|
@router.get("/tank-reactions/detail", summary="获取水池反应属性", description="获取指定水池的反应属性信息")
|
||||||
async def fastapi_get_tank_reaction(
|
async def fastapi_get_tank_reaction(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水池ID")
|
tank: str = Query(..., description="水池ID")
|
||||||
@@ -229,7 +229,7 @@ async def fastapi_get_tank_reaction(
|
|||||||
"""
|
"""
|
||||||
return get_tank_reaction(network, tank)
|
return get_tank_reaction(network, tank)
|
||||||
|
|
||||||
@router.post("/settankreaction/", response_model=None, summary="设置水池反应属性", description="更新指定水池的反应属性")
|
@router.patch("/tank-reactions", response_model=None, summary="设置水池反应属性", description="更新指定水池的反应属性")
|
||||||
async def fastapi_set_tank_reaction(
|
async def fastapi_set_tank_reaction(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -241,7 +241,7 @@ async def fastapi_set_tank_reaction(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_tank_reaction(network, ChangeSet(props))
|
return set_tank_reaction(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/getmixingschema/", summary="获取混合架构", description="获取网络中混合对象的架构定义")
|
@router.get("/network-schemas/mixing", summary="获取混合架构", description="获取网络中混合对象的架构定义")
|
||||||
async def fastapi_get_mixing_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_mixing_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取混合架构。
|
"""获取混合架构。
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ async def fastapi_get_mixing_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_mixing_schema(network)
|
return get_mixing_schema(network)
|
||||||
|
|
||||||
@router.get("/getmixing/", summary="获取混合属性", description="获取指定水池的混合属性信息")
|
@router.get("/mixing-configurations/detail", summary="获取混合属性", description="获取指定水池的混合属性信息")
|
||||||
async def fastapi_get_mixing(
|
async def fastapi_get_mixing(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水池ID")
|
tank: str = Query(..., description="水池ID")
|
||||||
@@ -260,7 +260,7 @@ async def fastapi_get_mixing(
|
|||||||
"""
|
"""
|
||||||
return get_mixing(network, tank)
|
return get_mixing(network, tank)
|
||||||
|
|
||||||
@router.post("/setmixing/", response_model=None, summary="设置混合属性", description="更新指定水池的混合属性")
|
@router.patch("/mixing-configurations", response_model=None, summary="设置混合属性", description="更新指定水池的混合属性")
|
||||||
async def fastapi_set_mixing(
|
async def fastapi_set_mixing(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -272,7 +272,7 @@ async def fastapi_set_mixing(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return api.set_mixing(network, ChangeSet(props))
|
return api.set_mixing(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/addmixing/", response_model=None, summary="添加混合", description="在网络中添加一个新的混合")
|
@router.post("/mixing-configurations", response_model=None, summary="添加混合", description="在网络中添加一个新的混合")
|
||||||
async def fastapi_add_mixing(
|
async def fastapi_add_mixing(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -284,7 +284,7 @@ async def fastapi_add_mixing(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_mixing(network, ChangeSet(props))
|
return add_mixing(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/deletemixing/", response_model=None, summary="删除混合", description="从网络中删除指定的混合")
|
@router.delete("/mixing-configurations", response_model=None, summary="删除混合", description="从网络中删除指定的混合")
|
||||||
async def fastapi_delete_mixing(
|
async def fastapi_delete_mixing(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import json
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getvertexschema/", summary="获取图形元素架构", description="获取网络中图形元素对象的架构定义")
|
@router.get("/network-schemas/vertex", summary="获取图形元素架构", description="获取网络中图形元素对象的架构定义")
|
||||||
async def fastapi_get_vertex_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_vertex_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取图形元素架构。
|
"""获取图形元素架构。
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ async def fastapi_get_vertex_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_vertex_schema(network)
|
return get_vertex_schema(network)
|
||||||
|
|
||||||
@router.get("/getvertexproperties/", summary="获取图形元素属性", description="获取指定图形元素的属性信息")
|
@router.get("/visual-elements/properties", summary="获取图形元素属性", description="获取指定图形元素的属性信息")
|
||||||
async def fastapi_get_vertex_properties(
|
async def fastapi_get_vertex_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
link: str = Query(..., description="图形元素链接")
|
link: str = Query(..., description="图形元素链接")
|
||||||
@@ -43,7 +43,7 @@ async def fastapi_get_vertex_properties(
|
|||||||
"""
|
"""
|
||||||
return get_vertex(network, link)
|
return get_vertex(network, link)
|
||||||
|
|
||||||
@router.post("/setvertexproperties/", response_model=None, summary="设置图形元素属性", description="更新指定图形元素的属性")
|
@router.patch("/visual-elements/properties", response_model=None, summary="设置图形元素属性", description="更新指定图形元素的属性")
|
||||||
async def fastapi_set_vertex_properties(
|
async def fastapi_set_vertex_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -55,7 +55,7 @@ async def fastapi_set_vertex_properties(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_vertex(network, ChangeSet(props))
|
return set_vertex(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/addvertex/", response_model=None, summary="添加图形元素", description="在网络中添加一个新的图形元素")
|
@router.post("/visual-elements", response_model=None, summary="添加图形元素", description="在网络中添加一个新的图形元素")
|
||||||
async def fastapi_add_vertex(
|
async def fastapi_add_vertex(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -67,7 +67,7 @@ async def fastapi_add_vertex(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_vertex(network, ChangeSet(props))
|
return add_vertex(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/deletevertex/", response_model=None, summary="删除图形元素", description="从网络中删除指定的图形元素")
|
@router.delete("/visual-elements", response_model=None, summary="删除图形元素", description="从网络中删除指定的图形元素")
|
||||||
async def fastapi_delete_vertex(
|
async def fastapi_delete_vertex(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -79,7 +79,7 @@ async def fastapi_delete_vertex(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return delete_vertex(network, ChangeSet(props))
|
return delete_vertex(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/getallvertexlinks/", response_class=PlainTextResponse, summary="获取所有图形元素链接", description="获取网络中的所有图形元素链接列表")
|
@router.get("/visual-elements/links", response_class=PlainTextResponse, summary="获取所有图形元素链接", description="获取网络中的所有图形元素链接列表")
|
||||||
async def fastapi_get_all_vertex_links(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]:
|
async def fastapi_get_all_vertex_links(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[str]:
|
||||||
"""获取所有图形元素链接。
|
"""获取所有图形元素链接。
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ async def fastapi_get_all_vertex_links(network: str = Query(..., description="
|
|||||||
"""
|
"""
|
||||||
return json.dumps(get_all_vertex_links(network))
|
return json.dumps(get_all_vertex_links(network))
|
||||||
|
|
||||||
@router.get("/getallvertices/", response_class=PlainTextResponse, summary="获取所有图形元素", description="获取网络中的所有图形元素详细信息")
|
@router.get("/all-vertices", response_class=PlainTextResponse, summary="获取所有图形元素", description="获取网络中的所有图形元素详细信息")
|
||||||
async def fastapi_get_all_vertices(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[str, Any]]:
|
async def fastapi_get_all_vertices(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[str, Any]]:
|
||||||
"""获取所有图形元素。
|
"""获取所有图形元素。
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ async def fastapi_get_all_vertices(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return json.dumps(get_all_vertices(network))
|
return json.dumps(get_all_vertices(network))
|
||||||
|
|
||||||
@router.get("/getlabelschema/", summary="获取标签架构", description="获取网络中标签对象的架构定义")
|
@router.get("/network-schemas/label", summary="获取标签架构", description="获取网络中标签对象的架构定义")
|
||||||
async def fastapi_get_label_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_label_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取标签架构。
|
"""获取标签架构。
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ async def fastapi_get_label_schema(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return get_label_schema(network)
|
return get_label_schema(network)
|
||||||
|
|
||||||
@router.get("/getlabelproperties/", summary="获取标签属性", description="获取指定坐标处的标签属性信息")
|
@router.get("/labels/properties", summary="获取标签属性", description="获取指定坐标处的标签属性信息")
|
||||||
async def fastapi_get_label_properties(
|
async def fastapi_get_label_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
x: float = Query(..., description="X坐标"),
|
x: float = Query(..., description="X坐标"),
|
||||||
@@ -115,7 +115,7 @@ async def fastapi_get_label_properties(
|
|||||||
"""
|
"""
|
||||||
return get_label(network, x, y)
|
return get_label(network, x, y)
|
||||||
|
|
||||||
@router.post("/setlabelproperties/", response_model=None, summary="设置标签属性", description="更新指定标签的属性")
|
@router.patch("/labels/properties", response_model=None, summary="设置标签属性", description="更新指定标签的属性")
|
||||||
async def fastapi_set_label_properties(
|
async def fastapi_set_label_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -127,7 +127,7 @@ async def fastapi_set_label_properties(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_label(network, ChangeSet(props))
|
return set_label(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/addlabel/", response_model=None, summary="添加标签", description="在网络中添加一个新的标签")
|
@router.post("/labels", response_model=None, summary="添加标签", description="在网络中添加一个新的标签")
|
||||||
async def fastapi_add_label(
|
async def fastapi_add_label(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -139,7 +139,7 @@ async def fastapi_add_label(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_label(network, ChangeSet(props))
|
return add_label(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/deletelabel/", response_model=None, summary="删除标签", description="从网络中删除指定的标签")
|
@router.delete("/labels", response_model=None, summary="删除标签", description="从网络中删除指定的标签")
|
||||||
async def fastapi_delete_label(
|
async def fastapi_delete_label(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -151,7 +151,7 @@ async def fastapi_delete_label(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return delete_label(network, ChangeSet(props))
|
return delete_label(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get("/getbackdropschema/", summary="获取背景架构", description="获取网络中背景对象的架构定义")
|
@router.get("/network-schemas/backdrop", summary="获取背景架构", description="获取网络中背景对象的架构定义")
|
||||||
async def fastapi_get_backdrop_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fastapi_get_backdrop_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""获取背景架构。
|
"""获取背景架构。
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ async def fastapi_get_backdrop_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_backdrop_schema(network)
|
return get_backdrop_schema(network)
|
||||||
|
|
||||||
@router.get("/getbackdropproperties/", summary="获取背景属性", description="获取指定网络的背景属性信息")
|
@router.get("/backdrops/properties", summary="获取背景属性", description="获取指定网络的背景属性信息")
|
||||||
async def fastapi_get_backdrop_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
async def fastapi_get_backdrop_properties(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
||||||
"""获取背景属性。
|
"""获取背景属性。
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ async def fastapi_get_backdrop_properties(network: str = Query(..., description=
|
|||||||
"""
|
"""
|
||||||
return get_backdrop(network)
|
return get_backdrop(network)
|
||||||
|
|
||||||
@router.post("/setbackdropproperties/", response_model=None, summary="设置背景属性", description="更新指定网络的背景属性")
|
@router.patch("/backdrops/properties", response_model=None, summary="设置背景属性", description="更新指定网络的背景属性")
|
||||||
async def fastapi_set_backdrop_properties(
|
async def fastapi_set_backdrop_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from app.services.tjnetwork import (
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getallextensiondatakeys/",
|
"/all-extension-data-keys",
|
||||||
summary="获取所有扩展数据键",
|
summary="获取所有扩展数据键",
|
||||||
description="获取指定网络的所有扩展数据的键列表"
|
description="获取指定网络的所有扩展数据的键列表"
|
||||||
)
|
)
|
||||||
@@ -32,7 +32,7 @@ async def get_all_extension_data_keys_endpoint(
|
|||||||
return get_all_extension_data_keys(network)
|
return get_all_extension_data_keys(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getallextensiondata/",
|
"/all-extension-datas",
|
||||||
summary="获取所有扩展数据",
|
summary="获取所有扩展数据",
|
||||||
description="获取指定网络的所有扩展数据"
|
description="获取指定网络的所有扩展数据"
|
||||||
)
|
)
|
||||||
@@ -53,7 +53,7 @@ async def get_all_extension_data_endpoint(
|
|||||||
return get_all_extension_data(network)
|
return get_all_extension_data(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getextensiondata/",
|
"/extension-datas",
|
||||||
summary="获取指定扩展数据",
|
summary="获取指定扩展数据",
|
||||||
description="获取指定网络中指定键的扩展数据值"
|
description="获取指定网络中指定键的扩展数据值"
|
||||||
)
|
)
|
||||||
@@ -75,8 +75,8 @@ async def get_extension_data_endpoint(
|
|||||||
"""
|
"""
|
||||||
return get_extension_data(network, key)
|
return get_extension_data(network, key)
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setextensiondata/",
|
"/extension-datas",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置扩展数据",
|
summary="设置扩展数据",
|
||||||
description="设置指定网络中的扩展数据"
|
description="设置指定网络中的扩展数据"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ router = APIRouter()
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/tianditu/geocode",
|
"/geocoding-requests",
|
||||||
summary="Tianditu Geocoding",
|
summary="Tianditu Geocoding",
|
||||||
description="调用天地图地理编码服务,将结构化地址转换为经纬度",
|
description="调用天地图地理编码服务,将结构化地址转换为经纬度",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class LeakageIdentifyRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/identify/",
|
"/leakage-identifications",
|
||||||
summary="执行漏损识别",
|
summary="执行漏损识别",
|
||||||
description="基于压力观测数据和遗传算法识别管网中的漏损位置和大小"
|
description="基于压力观测数据和遗传算法识别管网中的漏损位置和大小"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ router = APIRouter()
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/meta/project", summary="获取项目元数据", description="获取当前项目的元数据和配置信息", response_model=ProjectMetaResponse)
|
@router.get("/projects/current/metadata", summary="获取项目元数据", description="获取当前项目的元数据和配置信息", response_model=ProjectMetaResponse)
|
||||||
async def get_project_metadata(
|
async def get_project_metadata(
|
||||||
ctx: ProjectContext = Depends(get_project_context),
|
ctx: ProjectContext = Depends(get_project_context),
|
||||||
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
||||||
@@ -52,7 +52,7 @@ async def get_project_metadata(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/meta/projects", summary="列出用户项目", description="获取当前用户有权限的所有项目列表", response_model=list[ProjectSummaryResponse])
|
@router.get("/projects", summary="列出用户项目", description="获取当前用户有权限的所有项目列表", response_model=list[ProjectSummaryResponse])
|
||||||
async def list_user_projects(
|
async def list_user_projects(
|
||||||
current_user=Depends(get_current_metadata_user),
|
current_user=Depends(get_current_metadata_user),
|
||||||
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
||||||
@@ -88,7 +88,7 @@ async def list_user_projects(
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/meta/db/health", summary="检查数据库健康状态", description="检查项目数据库连接的健康状况")
|
@router.get("/projects/current/database-health", summary="检查数据库健康状态", description="检查项目数据库连接的健康状况")
|
||||||
async def project_db_health(
|
async def project_db_health(
|
||||||
pg_session: AsyncSession = Depends(get_project_pg_session),
|
pg_session: AsyncSession = Depends(get_project_pg_session),
|
||||||
ts_conn: AsyncConnection = Depends(get_project_timescale_connection),
|
ts_conn: AsyncConnection = Depends(get_project_timescale_connection),
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from app.services.tjnetwork import (
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/getjson/", summary="获取JSON示例", description="获取JSON格式响应示例")
|
|
||||||
async def fastapi_get_json():
|
async def fastapi_get_json():
|
||||||
"""
|
"""
|
||||||
获取JSON示例
|
获取JSON示例
|
||||||
@@ -29,7 +28,6 @@ async def fastapi_get_json():
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/sensor-placement-schemes", summary="获取所有传感器位置", description="获取网络中所有传感器的放置位置信息")
|
@router.get("/sensor-placement-schemes", summary="获取所有传感器位置", description="获取网络中所有传感器的放置位置信息")
|
||||||
@router.get("/getallsensorplacements/", summary="获取所有传感器位置(旧路径)", description="获取网络中所有传感器的放置位置信息", deprecated=True)
|
|
||||||
async def fastapi_get_all_sensor_placements(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]:
|
async def fastapi_get_all_sensor_placements(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]:
|
||||||
"""
|
"""
|
||||||
获取所有传感器位置
|
获取所有传感器位置
|
||||||
@@ -39,7 +37,7 @@ async def fastapi_get_all_sensor_placements(network: str = Query(..., descriptio
|
|||||||
return get_all_sensor_placements(network)
|
return get_all_sensor_placements(network)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/getallburstlocateresults/", summary="获取所有爆管定位结果", description="获取网络中所有爆管定位的分析结果")
|
@router.get("/burst-locations", summary="获取所有爆管定位结果", description="获取网络中所有爆管定位的分析结果")
|
||||||
async def fastapi_get_all_burst_locate_results(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]:
|
async def fastapi_get_all_burst_locate_results(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]:
|
||||||
"""
|
"""
|
||||||
获取所有爆管定位结果
|
获取所有爆管定位结果
|
||||||
@@ -54,7 +52,6 @@ class Item(BaseModel):
|
|||||||
str_info: str
|
str_info: str
|
||||||
|
|
||||||
|
|
||||||
@router.post("/test_dict/", summary="测试字典处理", description="测试处理字典类型数据")
|
|
||||||
async def fastapi_test_dict(data: Item) -> dict[str, str]:
|
async def fastapi_test_dict(data: Item) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
测试字典处理
|
测试字典处理
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
import json
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from uuid import UUID, uuid4
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
from fastapi import (
|
from fastapi import (
|
||||||
APIRouter,
|
APIRouter,
|
||||||
Body,
|
|
||||||
Depends,
|
Depends,
|
||||||
File,
|
File,
|
||||||
Header,
|
|
||||||
HTTPException,
|
HTTPException,
|
||||||
Path as ApiPath,
|
Path as ApiPath,
|
||||||
Query,
|
|
||||||
Request,
|
Request,
|
||||||
UploadFile,
|
UploadFile,
|
||||||
status,
|
status,
|
||||||
@@ -24,7 +20,7 @@ from app.auth.metadata_dependencies import (
|
|||||||
from app.core.audit import AuditAction, log_audit_event
|
from app.core.audit import AuditAction, log_audit_event
|
||||||
from app.infra.db.metadb.repositories.metadata_repository import MetadataRepository
|
from app.infra.db.metadb.repositories.metadata_repository import MetadataRepository
|
||||||
from app.services.network_import import network_update
|
from app.services.network_import import network_update
|
||||||
from app.services.tjnetwork import ChangeSet, import_inp, run_inp
|
from app.services.tjnetwork import run_inp
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -145,7 +141,7 @@ async def _apply_model_update(content: bytes) -> None:
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/admin/projects/{project_id}/model/import",
|
"/admin/projects/{project_id}/model-imports",
|
||||||
summary="导入桌面端水力模型",
|
summary="导入桌面端水力模型",
|
||||||
)
|
)
|
||||||
async def import_project_model(
|
async def import_project_model(
|
||||||
@@ -168,8 +164,8 @@ async def import_project_model(
|
|||||||
return {"project_id": str(project.id), "filename": filename, "result": result}
|
return {"project_id": str(project.id), "filename": filename, "result": result}
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/admin/projects/{project_id}/model/update",
|
"/admin/projects/{project_id}/model-imports",
|
||||||
summary="更新桌面端水力模型",
|
summary="更新桌面端水力模型",
|
||||||
)
|
)
|
||||||
async def update_project_model(
|
async def update_project_model(
|
||||||
@@ -190,108 +186,3 @@ async def update_project_model(
|
|||||||
action="update",
|
action="update",
|
||||||
)
|
)
|
||||||
return {"project_id": str(project.id), "filename": filename, "updated": True}
|
return {"project_id": str(project.id), "filename": filename, "updated": True}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/importinp/", deprecated=True)
|
|
||||||
async def legacy_import_inp(
|
|
||||||
request: Request,
|
|
||||||
network: str = Query(...),
|
|
||||||
x_project_id: UUID = Header(..., alias="X-Project-Id"),
|
|
||||||
current_user=Depends(get_current_metadata_admin),
|
|
||||||
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
|
||||||
):
|
|
||||||
project = await _get_active_project(x_project_id, metadata_repo)
|
|
||||||
if network != project.code:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
|
||||||
detail="Project scope denied",
|
|
||||||
)
|
|
||||||
payload = await request.json()
|
|
||||||
inp_text = payload.get("inp") if isinstance(payload, dict) else None
|
|
||||||
if not isinstance(inp_text, str):
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
|
||||||
detail="Missing INP content",
|
|
||||||
)
|
|
||||||
_validate_inp_bytes(inp_text.encode("utf-8"), "model.inp")
|
|
||||||
result = import_inp(network, ChangeSet({"inp": inp_text}))
|
|
||||||
await _audit_model_change(
|
|
||||||
request=request,
|
|
||||||
current_user=current_user,
|
|
||||||
metadata_repo=metadata_repo,
|
|
||||||
project_id=project.id,
|
|
||||||
action="import",
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/uploadinp/", deprecated=True)
|
|
||||||
async def legacy_upload_inp(
|
|
||||||
request: Request,
|
|
||||||
content: bytes = Body(...),
|
|
||||||
name: str = Query(...),
|
|
||||||
x_project_id: UUID = Header(..., alias="X-Project-Id"),
|
|
||||||
current_user=Depends(get_current_metadata_admin),
|
|
||||||
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
|
||||||
) -> bool:
|
|
||||||
project = await _get_active_project(x_project_id, metadata_repo)
|
|
||||||
safe_name = Path(name).name
|
|
||||||
if safe_name != name:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
|
||||||
detail="Invalid INP file name",
|
|
||||||
)
|
|
||||||
_validate_inp_bytes(content, safe_name)
|
|
||||||
target_dir = Path("data")
|
|
||||||
target_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
(target_dir / safe_name).write_bytes(content)
|
|
||||||
await _audit_model_change(
|
|
||||||
request=request,
|
|
||||||
current_user=current_user,
|
|
||||||
metadata_repo=metadata_repo,
|
|
||||||
project_id=project.id,
|
|
||||||
action="upload",
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/network_project/", deprecated=True)
|
|
||||||
async def legacy_network_project(
|
|
||||||
request: Request,
|
|
||||||
file: UploadFile = File(...),
|
|
||||||
x_project_id: UUID = Header(..., alias="X-Project-Id"),
|
|
||||||
current_user=Depends(get_current_metadata_admin),
|
|
||||||
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
|
||||||
):
|
|
||||||
project = await _get_active_project(x_project_id, metadata_repo)
|
|
||||||
content, _ = await _read_upload(file)
|
|
||||||
result = await _run_uploaded_inp(content)
|
|
||||||
await _audit_model_change(
|
|
||||||
request=request,
|
|
||||||
current_user=current_user,
|
|
||||||
metadata_repo=metadata_repo,
|
|
||||||
project_id=project.id,
|
|
||||||
action="import",
|
|
||||||
)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/network_update/", deprecated=True)
|
|
||||||
async def legacy_network_update(
|
|
||||||
request: Request,
|
|
||||||
file: UploadFile = File(...),
|
|
||||||
x_project_id: UUID = Header(..., alias="X-Project-Id"),
|
|
||||||
current_user=Depends(get_current_metadata_admin),
|
|
||||||
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
|
||||||
) -> str:
|
|
||||||
project = await _get_active_project(x_project_id, metadata_repo)
|
|
||||||
content, _ = await _read_upload(file)
|
|
||||||
await _apply_model_update(content)
|
|
||||||
await _audit_model_change(
|
|
||||||
request=request,
|
|
||||||
current_user=current_user,
|
|
||||||
metadata_repo=metadata_repo,
|
|
||||||
project_id=project.id,
|
|
||||||
action="update",
|
|
||||||
)
|
|
||||||
return json.dumps({"message": "管网更新成功"})
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ router = APIRouter()
|
|||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getdemandschema",
|
"/network-schemas/demand",
|
||||||
summary="获取需水量属性架构",
|
summary="获取需水量属性架构",
|
||||||
description="获取指定水网中需水量(Demand)的属性架构定义"
|
description="获取指定水网中需水量(Demand)的属性架构定义"
|
||||||
)
|
)
|
||||||
@@ -32,7 +32,7 @@ async def fastapi_get_demand_schema(network: str = Query(..., description="管
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getdemandproperties/",
|
"/demands/properties",
|
||||||
summary="获取需水量属性",
|
summary="获取需水量属性",
|
||||||
description="获取指定水网中节点的需水量属性信息"
|
description="获取指定水网中节点的需水量属性信息"
|
||||||
)
|
)
|
||||||
@@ -49,8 +49,8 @@ async def fastapi_get_demand_properties(
|
|||||||
|
|
||||||
|
|
||||||
# example: set_demand(p, ChangeSet({'junction': 'j1', 'demands': [{'demand': 10.0, 'pattern': None, 'category': 'x'}, {'demand': 20.0, 'pattern': None, 'category': None}]}))
|
# example: set_demand(p, ChangeSet({'junction': 'j1', 'demands': [{'demand': 10.0, 'pattern': None, 'category': 'x'}, {'demand': 20.0, 'pattern': None, 'category': None}]}))
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setdemandproperties/",
|
"/demands/properties",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置需水量属性",
|
summary="设置需水量属性",
|
||||||
description="设置指定水网中节点的需水量属性信息"
|
description="设置指定水网中节点的需水量属性信息"
|
||||||
@@ -72,8 +72,8 @@ async def fastapi_set_demand_properties(
|
|||||||
############################################################
|
############################################################
|
||||||
# water distribution 36.[Water Distribution]
|
# water distribution 36.[Water Distribution]
|
||||||
############################################################
|
############################################################
|
||||||
@router.get(
|
@router.post(
|
||||||
"/calculatedemandtonodes/",
|
"/demands/to-nodes",
|
||||||
summary="计算需水量到节点分配",
|
summary="计算需水量到节点分配",
|
||||||
description="将总需水量按指定方式分配到多个节点"
|
description="将总需水量按指定方式分配到多个节点"
|
||||||
)
|
)
|
||||||
@@ -97,8 +97,8 @@ async def fastapi_calculate_demand_to_nodes(
|
|||||||
nodes = props["nodes"]
|
nodes = props["nodes"]
|
||||||
return calculate_demand_to_nodes(network, demand, nodes)
|
return calculate_demand_to_nodes(network, demand, nodes)
|
||||||
|
|
||||||
@router.get(
|
@router.post(
|
||||||
"/calculatedemandtoregion/",
|
"/demands/to-region",
|
||||||
summary="计算需水量到区域分配",
|
summary="计算需水量到区域分配",
|
||||||
description="将总需水量按区域特征分配到该区域内的节点"
|
description="将总需水量按区域特征分配到该区域内的节点"
|
||||||
)
|
)
|
||||||
@@ -122,8 +122,8 @@ async def fastapi_calculate_demand_to_region(
|
|||||||
region = props["region"]
|
region = props["region"]
|
||||||
return calculate_demand_to_region(network, demand, region)
|
return calculate_demand_to_region(network, demand, region)
|
||||||
|
|
||||||
@router.get(
|
@router.post(
|
||||||
"/calculatedemandtonetwork/",
|
"/demands/to-network",
|
||||||
summary="计算需水量到整网分配",
|
summary="计算需水量到整网分配",
|
||||||
description="将需水量均匀分配到整个水网的所有需水节点"
|
description="将需水量均匀分配到整个水网的所有需水节点"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ router = APIRouter()
|
|||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/isnode/",
|
"/nodes/existence",
|
||||||
summary="检查节点有效性",
|
summary="检查节点有效性",
|
||||||
description="检查指定ID是否为水网中的有效节点"
|
description="检查指定ID是否为水网中的有效节点"
|
||||||
)
|
)
|
||||||
@@ -57,7 +57,7 @@ async def fastapi_is_node(
|
|||||||
return is_node(network, node)
|
return is_node(network, node)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/isjunction/",
|
"/junctions/existence",
|
||||||
summary="检查是否为接点",
|
summary="检查是否为接点",
|
||||||
description="检查指定ID是否为水网中的接点(需求点)"
|
description="检查指定ID是否为水网中的接点(需求点)"
|
||||||
)
|
)
|
||||||
@@ -69,7 +69,7 @@ async def fastapi_is_junction(
|
|||||||
return is_junction(network, node)
|
return is_junction(network, node)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/isreservoir/",
|
"/reservoirs/existence",
|
||||||
summary="检查是否为水源",
|
summary="检查是否为水源",
|
||||||
description="检查指定ID是否为水网中的水源(水库/河流)"
|
description="检查指定ID是否为水网中的水源(水库/河流)"
|
||||||
)
|
)
|
||||||
@@ -81,7 +81,7 @@ async def fastapi_is_reservoir(
|
|||||||
return is_reservoir(network, node)
|
return is_reservoir(network, node)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/istank/",
|
"/tanks/existence",
|
||||||
summary="检查是否为蓄水池",
|
summary="检查是否为蓄水池",
|
||||||
description="检查指定ID是否为水网中的蓄水池"
|
description="检查指定ID是否为水网中的蓄水池"
|
||||||
)
|
)
|
||||||
@@ -93,7 +93,7 @@ async def fastapi_is_tank(
|
|||||||
return is_tank(network, node)
|
return is_tank(network, node)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/islink/",
|
"/links/existence",
|
||||||
summary="检查管线有效性",
|
summary="检查管线有效性",
|
||||||
description="检查指定ID是否为水网中的有效管线"
|
description="检查指定ID是否为水网中的有效管线"
|
||||||
)
|
)
|
||||||
@@ -105,7 +105,7 @@ async def fastapi_is_link(
|
|||||||
return is_link(network, link)
|
return is_link(network, link)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/ispipe/",
|
"/pipes/existence",
|
||||||
summary="检查是否为管道",
|
summary="检查是否为管道",
|
||||||
description="检查指定ID是否为水网中的管道"
|
description="检查指定ID是否为水网中的管道"
|
||||||
)
|
)
|
||||||
@@ -117,7 +117,7 @@ async def fastapi_is_pipe(
|
|||||||
return is_pipe(network, link)
|
return is_pipe(network, link)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/ispump/",
|
"/pumps/existence",
|
||||||
summary="检查是否为泵",
|
summary="检查是否为泵",
|
||||||
description="检查指定ID是否为水网中的泵"
|
description="检查指定ID是否为水网中的泵"
|
||||||
)
|
)
|
||||||
@@ -129,7 +129,7 @@ async def fastapi_is_pump(
|
|||||||
return is_pump(network, link)
|
return is_pump(network, link)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/isvalve/",
|
"/valves/existence",
|
||||||
summary="检查是否为阀门",
|
summary="检查是否为阀门",
|
||||||
description="检查指定ID是否为水网中的阀门"
|
description="检查指定ID是否为水网中的阀门"
|
||||||
)
|
)
|
||||||
@@ -141,7 +141,7 @@ async def fastapi_is_valve(
|
|||||||
return is_valve(network, link)
|
return is_valve(network, link)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getnodetype/",
|
"/node-types",
|
||||||
summary="获取节点类型",
|
summary="获取节点类型",
|
||||||
description="获取指定节点的类型(接点/水源/蓄水池)"
|
description="获取指定节点的类型(接点/水源/蓄水池)"
|
||||||
)
|
)
|
||||||
@@ -153,7 +153,7 @@ async def fastapi_get_node_type(
|
|||||||
return get_node_type(network, node)
|
return get_node_type(network, node)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getlinktype/",
|
"/link-types",
|
||||||
summary="获取管线类型",
|
summary="获取管线类型",
|
||||||
description="获取指定管线的类型(管道/泵/阀门)"
|
description="获取指定管线的类型(管道/泵/阀门)"
|
||||||
)
|
)
|
||||||
@@ -165,7 +165,7 @@ async def fastapi_get_link_type(
|
|||||||
return get_link_type(network, link)
|
return get_link_type(network, link)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getelementtype/",
|
"/element-types",
|
||||||
summary="获取元素类型",
|
summary="获取元素类型",
|
||||||
description="获取指定元素的类型(节点或管线)"
|
description="获取指定元素的类型(节点或管线)"
|
||||||
)
|
)
|
||||||
@@ -177,7 +177,7 @@ async def fastapi_get_element_type(
|
|||||||
return get_element_type(network, element)
|
return get_element_type(network, element)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getelementtypevalue/",
|
"/element-type-values",
|
||||||
summary="获取元素类型值",
|
summary="获取元素类型值",
|
||||||
description="获取指定元素的类型数值标识"
|
description="获取指定元素的类型数值标识"
|
||||||
)
|
)
|
||||||
@@ -189,7 +189,7 @@ async def fastapi_get_element_type_value(
|
|||||||
return get_element_type_value(network, element)
|
return get_element_type_value(network, element)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getnodes/",
|
"/nodes",
|
||||||
summary="获取所有节点",
|
summary="获取所有节点",
|
||||||
description="获取指定水网中的所有节点ID列表"
|
description="获取指定水网中的所有节点ID列表"
|
||||||
)
|
)
|
||||||
@@ -198,7 +198,7 @@ async def fastapi_get_nodes(network: str = Query(..., description="管网名称
|
|||||||
return get_nodes(network)
|
return get_nodes(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getlinks/",
|
"/links",
|
||||||
summary="获取所有管线",
|
summary="获取所有管线",
|
||||||
description="获取指定水网中的所有管线ID列表"
|
description="获取指定水网中的所有管线ID列表"
|
||||||
)
|
)
|
||||||
@@ -207,7 +207,7 @@ async def fastapi_get_links(network: str = Query(..., description="管网名称
|
|||||||
return get_links(network)
|
return get_links(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getnodelinks/",
|
"/node-links",
|
||||||
summary="获取节点的关联管线",
|
summary="获取节点的关联管线",
|
||||||
description="获取指定节点连接的所有管线ID列表"
|
description="获取指定节点连接的所有管线ID列表"
|
||||||
)
|
)
|
||||||
@@ -223,7 +223,7 @@ def get_node_links_endpoint(
|
|||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getnodeproperties/",
|
"/node-properties",
|
||||||
summary="获取节点属性",
|
summary="获取节点属性",
|
||||||
description="获取指定节点的所有属性信息"
|
description="获取指定节点的所有属性信息"
|
||||||
)
|
)
|
||||||
@@ -235,7 +235,7 @@ async def fast_get_node_properties(
|
|||||||
return get_node_properties(network, node)
|
return get_node_properties(network, node)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getlinkproperties/",
|
"/link-properties",
|
||||||
summary="获取管线属性",
|
summary="获取管线属性",
|
||||||
description="获取指定管线的所有属性信息"
|
description="获取指定管线的所有属性信息"
|
||||||
)
|
)
|
||||||
@@ -247,7 +247,7 @@ async def fast_get_link_properties(
|
|||||||
return get_link_properties(network, link)
|
return get_link_properties(network, link)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getscadaproperties/",
|
"/scada-properties",
|
||||||
summary="获取SCADA点属性",
|
summary="获取SCADA点属性",
|
||||||
description="获取指定SCADA点的属性信息"
|
description="获取指定SCADA点的属性信息"
|
||||||
)
|
)
|
||||||
@@ -259,7 +259,7 @@ async def fast_get_scada_properties(
|
|||||||
return get_scada_info(network, scada)
|
return get_scada_info(network, scada)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getallscadaproperties/",
|
"/all-scada-properties",
|
||||||
summary="获取所有SCADA点属性",
|
summary="获取所有SCADA点属性",
|
||||||
description="获取指定水网中所有SCADA点的属性信息"
|
description="获取指定水网中所有SCADA点的属性信息"
|
||||||
)
|
)
|
||||||
@@ -270,7 +270,7 @@ async def fast_get_all_scada_properties(
|
|||||||
return get_all_scada_info(network)
|
return get_all_scada_info(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getelementpropertieswithtype/",
|
"/element-properties-with-types",
|
||||||
summary="获取指定类型元素属性",
|
summary="获取指定类型元素属性",
|
||||||
description="获取指定类型的元素属性信息"
|
description="获取指定类型的元素属性信息"
|
||||||
)
|
)
|
||||||
@@ -283,7 +283,7 @@ async def fast_get_element_properties_with_type(
|
|||||||
return get_element_properties_with_type(network, elementtype, element)
|
return get_element_properties_with_type(network, elementtype, element)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getelementproperties/",
|
"/element-properties",
|
||||||
summary="获取元素属性",
|
summary="获取元素属性",
|
||||||
description="获取指定元素的属性信息"
|
description="获取指定元素的属性信息"
|
||||||
)
|
)
|
||||||
@@ -299,7 +299,7 @@ async def fast_get_element_properties(
|
|||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/gettitleschema/",
|
"/title-schemas",
|
||||||
summary="获取标题属性架构",
|
summary="获取标题属性架构",
|
||||||
description="获取指定水网的标题(标题)属性架构定义"
|
description="获取指定水网的标题(标题)属性架构定义"
|
||||||
)
|
)
|
||||||
@@ -310,7 +310,7 @@ async def fast_get_title_schema(
|
|||||||
return get_title_schema(network)
|
return get_title_schema(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/gettitle/",
|
"/titles",
|
||||||
summary="获取水网标题属性",
|
summary="获取水网标题属性",
|
||||||
description="获取指定水网的标题(Title)信息"
|
description="获取指定水网的标题(Title)信息"
|
||||||
)
|
)
|
||||||
@@ -318,8 +318,8 @@ async def fast_get_title(network: str = Query(..., description="管网名称(
|
|||||||
"""获取水网的标题属性。"""
|
"""获取水网的标题属性。"""
|
||||||
return get_title(network)
|
return get_title(network)
|
||||||
|
|
||||||
@router.get(
|
@router.patch(
|
||||||
"/settitle/",
|
"/titles",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置水网标题属性",
|
summary="设置水网标题属性",
|
||||||
description="设置指定水网的标题(Title)信息"
|
description="设置指定水网的标题(Title)信息"
|
||||||
@@ -337,7 +337,7 @@ async def fastapi_set_title(
|
|||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getstatusschema",
|
"/status-schemas",
|
||||||
summary="获取状态属性架构",
|
summary="获取状态属性架构",
|
||||||
description="获取指定水网的状态(Status)属性架构定义"
|
description="获取指定水网的状态(Status)属性架构定义"
|
||||||
)
|
)
|
||||||
@@ -348,7 +348,7 @@ async def fastapi_get_status_schema(
|
|||||||
return get_status_schema(network)
|
return get_status_schema(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getstatus/",
|
"/status",
|
||||||
summary="获取管线状态",
|
summary="获取管线状态",
|
||||||
description="获取指定管线的状态信息"
|
description="获取指定管线的状态信息"
|
||||||
)
|
)
|
||||||
@@ -359,8 +359,8 @@ async def fastapi_get_status(
|
|||||||
"""获取管线的状态属性。"""
|
"""获取管线的状态属性。"""
|
||||||
return get_status(network, link)
|
return get_status(network, link)
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setstatus/",
|
"/status-properties",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置管线状态",
|
summary="设置管线状态",
|
||||||
description="设置指定管线的状态信息"
|
description="设置指定管线的状态信息"
|
||||||
@@ -379,8 +379,8 @@ async def fastapi_set_status_properties(
|
|||||||
# General Deletion
|
# General Deletion
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.post(
|
@router.delete(
|
||||||
"/deletenode/",
|
"/nodes",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="删除节点",
|
summary="删除节点",
|
||||||
description="删除指定的节点(接点/水源/蓄水池)"
|
description="删除指定的节点(接点/水源/蓄水池)"
|
||||||
@@ -399,8 +399,8 @@ async def fastapi_delete_node(
|
|||||||
return delete_tank(network, ChangeSet(ps))
|
return delete_tank(network, ChangeSet(ps))
|
||||||
return ChangeSet() # Should probably raise error or return empty
|
return ChangeSet() # Should probably raise error or return empty
|
||||||
|
|
||||||
@router.post(
|
@router.delete(
|
||||||
"/deletelink/",
|
"/links",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="删除管线",
|
summary="删除管线",
|
||||||
description="删除指定的管线(管道/泵/阀门)"
|
description="删除指定的管线(管道/泵/阀门)"
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ router = APIRouter()
|
|||||||
# return set_coord(network, ChangeSet(props))
|
# return set_coord(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getnodecoord/",
|
"/node-coords",
|
||||||
summary="获取节点坐标",
|
summary="获取节点坐标",
|
||||||
description="获取指定节点的地理坐标(X, Y)"
|
description="获取指定节点的地理坐标(X, Y)"
|
||||||
)
|
)
|
||||||
@@ -44,7 +44,7 @@ async def fastapi_get_node_coord(
|
|||||||
|
|
||||||
# Additional geometry queries found in main.py logic (implicit or explicit)
|
# Additional geometry queries found in main.py logic (implicit or explicit)
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getnetworkinextent/",
|
"/network-in-extents",
|
||||||
summary="获取范围内的网络元素",
|
summary="获取范围内的网络元素",
|
||||||
description="获取指定地理范围内的网络节点和管线"
|
description="获取指定地理范围内的网络节点和管线"
|
||||||
)
|
)
|
||||||
@@ -59,7 +59,7 @@ async def fastapi_get_network_in_extent(
|
|||||||
return get_network_in_extent(network, x1, y1, x2, y2)
|
return get_network_in_extent(network, x1, y1, x2, y2)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getmajornodecoords/",
|
"/majornode-coords",
|
||||||
summary="获取主要节点坐标",
|
summary="获取主要节点坐标",
|
||||||
description="获取直径大于等于指定值的节点坐标"
|
description="获取直径大于等于指定值的节点坐标"
|
||||||
)
|
)
|
||||||
@@ -71,7 +71,7 @@ async def fastapi_get_majornode_coords(
|
|||||||
return get_major_node_coords(network, diameter)
|
return get_major_node_coords(network, diameter)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getmajorpipenodes/",
|
"/major-pipe-nodes",
|
||||||
summary="获取主要管道节点",
|
summary="获取主要管道节点",
|
||||||
description="获取直径大于等于指定值的管道的节点ID"
|
description="获取直径大于等于指定值的管道的节点ID"
|
||||||
)
|
)
|
||||||
@@ -83,7 +83,7 @@ async def fastapi_get_major_pipe_nodes(
|
|||||||
return get_major_pipe_nodes(network, diameter)
|
return get_major_pipe_nodes(network, diameter)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getnetworklinknodes/",
|
"/network-link-nodes",
|
||||||
summary="获取网络管线节点",
|
summary="获取网络管线节点",
|
||||||
description="获取指定水网所有管线的起点和终点节点"
|
description="获取指定水网所有管线的起点和终点节点"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getjunctionschema", summary="获取节点架构", description="获取指定项目的节点属性架构和数据类型定义。")
|
@router.get("/network-schemas/junction", summary="获取节点架构", description="获取指定项目的节点属性架构和数据类型定义。")
|
||||||
async def fast_get_junction_schema(
|
async def fast_get_junction_schema(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> dict[str, dict[str, Any]]:
|
) -> dict[str, dict[str, Any]]:
|
||||||
@@ -27,7 +27,7 @@ async def fast_get_junction_schema(
|
|||||||
"""
|
"""
|
||||||
return get_junction_schema(network)
|
return get_junction_schema(network)
|
||||||
|
|
||||||
@router.post("/addjunction/", response_model=None, summary="添加节点", description="在供水网络中添加新的节点,指定节点ID和空间坐标。")
|
@router.post("/junctions", response_model=None, summary="添加节点", description="在供水网络中添加新的节点,指定节点ID和空间坐标。")
|
||||||
async def fastapi_add_junction(
|
async def fastapi_add_junction(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID"),
|
junction: str = Query(..., description="节点 ID"),
|
||||||
@@ -51,7 +51,7 @@ async def fastapi_add_junction(
|
|||||||
ps = {"id": junction, "x": x, "y": y, "elevation": z}
|
ps = {"id": junction, "x": x, "y": y, "elevation": z}
|
||||||
return add_junction(network, ChangeSet(ps))
|
return add_junction(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/deletejunction/", response_model=None, summary="删除节点", description="从供水网络中删除指定的节点。")
|
@router.delete("/junctions", response_model=None, summary="删除节点", description="从供水网络中删除指定的节点。")
|
||||||
async def fastapi_delete_junction(
|
async def fastapi_delete_junction(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID")
|
junction: str = Query(..., description="节点 ID")
|
||||||
@@ -69,7 +69,7 @@ async def fastapi_delete_junction(
|
|||||||
ps = {"id": junction}
|
ps = {"id": junction}
|
||||||
return delete_junction(network, ChangeSet(ps))
|
return delete_junction(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getjunctionelevation/", summary="获取节点标高", description="获取指定节点的标高(海拔高度)。")
|
@router.get("/junctions/elevation", summary="获取节点标高", description="获取指定节点的标高(海拔高度)。")
|
||||||
async def fastapi_get_junction_elevation(
|
async def fastapi_get_junction_elevation(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID")
|
junction: str = Query(..., description="节点 ID")
|
||||||
@@ -87,7 +87,7 @@ async def fastapi_get_junction_elevation(
|
|||||||
ps = get_junction(network, junction)
|
ps = get_junction(network, junction)
|
||||||
return ps["elevation"]
|
return ps["elevation"]
|
||||||
|
|
||||||
@router.get("/getjunctionx/", summary="获取节点 X 坐标", description="获取指定节点的 X 坐标值。")
|
@router.get("/junctions/x", summary="获取节点 X 坐标", description="获取指定节点的 X 坐标值。")
|
||||||
async def fastapi_get_junction_x(
|
async def fastapi_get_junction_x(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID")
|
junction: str = Query(..., description="节点 ID")
|
||||||
@@ -105,7 +105,7 @@ async def fastapi_get_junction_x(
|
|||||||
ps = get_junction(network, junction)
|
ps = get_junction(network, junction)
|
||||||
return ps["x"]
|
return ps["x"]
|
||||||
|
|
||||||
@router.get("/getjunctiony/", summary="获取节点 Y 坐标", description="获取指定节点的 Y 坐标值。")
|
@router.get("/junctions/y", summary="获取节点 Y 坐标", description="获取指定节点的 Y 坐标值。")
|
||||||
async def fastapi_get_junction_y(
|
async def fastapi_get_junction_y(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID")
|
junction: str = Query(..., description="节点 ID")
|
||||||
@@ -123,7 +123,7 @@ async def fastapi_get_junction_y(
|
|||||||
ps = get_junction(network, junction)
|
ps = get_junction(network, junction)
|
||||||
return ps["y"]
|
return ps["y"]
|
||||||
|
|
||||||
@router.get("/getjunctioncoord/", summary="获取节点坐标", description="获取指定节点的 X 和 Y 坐标。")
|
@router.get("/junctions/coord", summary="获取节点坐标", description="获取指定节点的 X 和 Y 坐标。")
|
||||||
async def fastapi_get_junction_coord(
|
async def fastapi_get_junction_coord(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID")
|
junction: str = Query(..., description="节点 ID")
|
||||||
@@ -142,7 +142,7 @@ async def fastapi_get_junction_coord(
|
|||||||
coord = {"x": ps["x"], "y": ps["y"]}
|
coord = {"x": ps["x"], "y": ps["y"]}
|
||||||
return coord
|
return coord
|
||||||
|
|
||||||
@router.get("/getjunctiondemand/", summary="获取节点需水量", description="获取指定节点的需水量。")
|
@router.get("/junctions/demand", summary="获取节点需水量", description="获取指定节点的需水量。")
|
||||||
async def fastapi_get_junction_demand(
|
async def fastapi_get_junction_demand(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID")
|
junction: str = Query(..., description="节点 ID")
|
||||||
@@ -160,7 +160,7 @@ async def fastapi_get_junction_demand(
|
|||||||
ps = get_junction(network, junction)
|
ps = get_junction(network, junction)
|
||||||
return ps["demand"]
|
return ps["demand"]
|
||||||
|
|
||||||
@router.get("/getjunctionpattern/", summary="获取节点需水模式", description="获取指定节点的需水模式标识。")
|
@router.get("/junctions/pattern", summary="获取节点需水模式", description="获取指定节点的需水模式标识。")
|
||||||
async def fastapi_get_junction_pattern(
|
async def fastapi_get_junction_pattern(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID")
|
junction: str = Query(..., description="节点 ID")
|
||||||
@@ -178,7 +178,7 @@ async def fastapi_get_junction_pattern(
|
|||||||
ps = get_junction(network, junction)
|
ps = get_junction(network, junction)
|
||||||
return ps["pattern"]
|
return ps["pattern"]
|
||||||
|
|
||||||
@router.post("/setjunctionelevation/", response_model=None, summary="设置节点标高", description="设置指定节点的标高值。")
|
@router.patch("/junctions/elevation", response_model=None, summary="设置节点标高", description="设置指定节点的标高值。")
|
||||||
async def fastapi_set_junction_elevation(
|
async def fastapi_set_junction_elevation(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID"),
|
junction: str = Query(..., description="节点 ID"),
|
||||||
@@ -198,7 +198,7 @@ async def fastapi_set_junction_elevation(
|
|||||||
ps = {"id": junction, "elevation": elevation}
|
ps = {"id": junction, "elevation": elevation}
|
||||||
return set_junction(network, ChangeSet(ps))
|
return set_junction(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setjunctionx/", response_model=None, summary="设置节点 X 坐标", description="设置指定节点的 X 坐标值。")
|
@router.patch("/junctions/x", response_model=None, summary="设置节点 X 坐标", description="设置指定节点的 X 坐标值。")
|
||||||
async def fastapi_set_junction_x(
|
async def fastapi_set_junction_x(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID"),
|
junction: str = Query(..., description="节点 ID"),
|
||||||
@@ -218,7 +218,7 @@ async def fastapi_set_junction_x(
|
|||||||
ps = {"id": junction, "x": x}
|
ps = {"id": junction, "x": x}
|
||||||
return set_junction(network, ChangeSet(ps))
|
return set_junction(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setjunctiony/", response_model=None, summary="设置节点 Y 坐标", description="设置指定节点的 Y 坐标值。")
|
@router.patch("/junctions/y", response_model=None, summary="设置节点 Y 坐标", description="设置指定节点的 Y 坐标值。")
|
||||||
async def fastapi_set_junction_y(
|
async def fastapi_set_junction_y(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID"),
|
junction: str = Query(..., description="节点 ID"),
|
||||||
@@ -238,7 +238,7 @@ async def fastapi_set_junction_y(
|
|||||||
ps = {"id": junction, "y": y}
|
ps = {"id": junction, "y": y}
|
||||||
return set_junction(network, ChangeSet(ps))
|
return set_junction(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setjunctioncoord/", response_model=None, summary="设置节点坐标", description="设置指定节点的 X 和 Y 坐标。")
|
@router.patch("/junctions/coord", response_model=None, summary="设置节点坐标", description="设置指定节点的 X 和 Y 坐标。")
|
||||||
async def fastapi_set_junction_coord(
|
async def fastapi_set_junction_coord(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID"),
|
junction: str = Query(..., description="节点 ID"),
|
||||||
@@ -260,7 +260,7 @@ async def fastapi_set_junction_coord(
|
|||||||
ps = {"id": junction, "x": x, "y": y}
|
ps = {"id": junction, "x": x, "y": y}
|
||||||
return set_junction(network, ChangeSet(ps))
|
return set_junction(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setjunctiondemand/", response_model=None, summary="设置节点需水量", description="设置指定节点的需水量。")
|
@router.patch("/junctions/demand", response_model=None, summary="设置节点需水量", description="设置指定节点的需水量。")
|
||||||
async def fastapi_set_junction_demand(
|
async def fastapi_set_junction_demand(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID"),
|
junction: str = Query(..., description="节点 ID"),
|
||||||
@@ -280,7 +280,7 @@ async def fastapi_set_junction_demand(
|
|||||||
ps = {"id": junction, "demand": demand}
|
ps = {"id": junction, "demand": demand}
|
||||||
return set_junction(network, ChangeSet(ps))
|
return set_junction(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setjunctionpattern/", response_model=None, summary="设置节点需水模式", description="设置指定节点的需水模式标识。")
|
@router.patch("/junctions/pattern", response_model=None, summary="设置节点需水模式", description="设置指定节点的需水模式标识。")
|
||||||
async def fastapi_set_junction_pattern(
|
async def fastapi_set_junction_pattern(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID"),
|
junction: str = Query(..., description="节点 ID"),
|
||||||
@@ -300,7 +300,7 @@ async def fastapi_set_junction_pattern(
|
|||||||
ps = {"id": junction, "pattern": pattern}
|
ps = {"id": junction, "pattern": pattern}
|
||||||
return set_junction(network, ChangeSet(ps))
|
return set_junction(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getjunctionproperties/", summary="获取节点属性", description="获取指定节点的所有属性信息。")
|
@router.get("/junctions/properties", summary="获取节点属性", description="获取指定节点的所有属性信息。")
|
||||||
async def fastapi_get_junction_properties(
|
async def fastapi_get_junction_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID")
|
junction: str = Query(..., description="节点 ID")
|
||||||
@@ -317,7 +317,7 @@ async def fastapi_get_junction_properties(
|
|||||||
"""
|
"""
|
||||||
return get_junction(network, junction)
|
return get_junction(network, junction)
|
||||||
|
|
||||||
@router.get("/getalljunctionproperties/", summary="获取所有节点属性", description="获取指定项目中所有节点的属性信息。")
|
@router.get("/junctions", summary="获取所有节点属性", description="获取指定项目中所有节点的属性信息。")
|
||||||
async def fastapi_get_all_junction_properties(
|
async def fastapi_get_all_junction_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
@@ -337,7 +337,7 @@ async def fastapi_get_all_junction_properties(
|
|||||||
results = get_all_junctions(network)
|
results = get_all_junctions(network)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@router.post("/setjunctionproperties/", response_model=None, summary="批量设置节点属性", description="批量设置指定节点的多个属性。")
|
@router.patch("/junctions/properties", response_model=None, summary="批量设置节点属性", description="批量设置指定节点的多个属性。")
|
||||||
async def fastapi_set_junction_properties(
|
async def fastapi_set_junction_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
junction: str = Query(..., description="节点 ID"),
|
junction: str = Query(..., description="节点 ID"),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getpipeschema", summary="获取管道模式", description="获取管道对象的模式定义,包含所有可用字段及其类型")
|
@router.get("/network-schemas/pipe", summary="获取管道模式", description="获取管道对象的模式定义,包含所有可用字段及其类型")
|
||||||
async def fastapi_get_pipe_schema(
|
async def fastapi_get_pipe_schema(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> dict[str, dict[str, Any]]:
|
) -> dict[str, dict[str, Any]]:
|
||||||
@@ -29,7 +29,7 @@ async def fastapi_get_pipe_schema(
|
|||||||
"""
|
"""
|
||||||
return get_pipe_schema(network)
|
return get_pipe_schema(network)
|
||||||
|
|
||||||
@router.post("/addpipe/", response_model=None, summary="添加管道", description="向网络中添加新的管道,需要提供管道的基本参数如长度、管径、粗糙度等")
|
@router.post("/pipes", response_model=None, summary="添加管道", description="向网络中添加新的管道,需要提供管道的基本参数如长度、管径、粗糙度等")
|
||||||
async def fastapi_add_pipe(
|
async def fastapi_add_pipe(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道标识符"),
|
pipe: str = Query(..., description="管道标识符"),
|
||||||
@@ -70,7 +70,7 @@ async def fastapi_add_pipe(
|
|||||||
}
|
}
|
||||||
return add_pipe(network, ChangeSet(ps))
|
return add_pipe(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/deletepipe/", response_model=None, summary="删除管道", description="从网络中删除指定的管道")
|
@router.delete("/pipes", response_model=None, summary="删除管道", description="从网络中删除指定的管道")
|
||||||
async def fastapi_delete_pipe(
|
async def fastapi_delete_pipe(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="要删除的管道ID")
|
pipe: str = Query(..., description="要删除的管道ID")
|
||||||
@@ -88,7 +88,7 @@ async def fastapi_delete_pipe(
|
|||||||
ps = {"id": pipe}
|
ps = {"id": pipe}
|
||||||
return delete_pipe(network, ChangeSet(ps))
|
return delete_pipe(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getpipenode1/", summary="获取管道起始节点", description="获取指定管道的起始节点ID")
|
@router.get("/pipes/node1", summary="获取管道起始节点", description="获取指定管道的起始节点ID")
|
||||||
async def fastapi_get_pipe_node1(
|
async def fastapi_get_pipe_node1(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID")
|
pipe: str = Query(..., description="管道ID")
|
||||||
@@ -106,7 +106,7 @@ async def fastapi_get_pipe_node1(
|
|||||||
ps = get_pipe(network, pipe)
|
ps = get_pipe(network, pipe)
|
||||||
return ps["node1"]
|
return ps["node1"]
|
||||||
|
|
||||||
@router.get("/getpipenode2/", summary="获取管道终止节点", description="获取指定管道的终止节点ID")
|
@router.get("/pipes/node2", summary="获取管道终止节点", description="获取指定管道的终止节点ID")
|
||||||
async def fastapi_get_pipe_node2(
|
async def fastapi_get_pipe_node2(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID")
|
pipe: str = Query(..., description="管道ID")
|
||||||
@@ -124,7 +124,7 @@ async def fastapi_get_pipe_node2(
|
|||||||
ps = get_pipe(network, pipe)
|
ps = get_pipe(network, pipe)
|
||||||
return ps["node2"]
|
return ps["node2"]
|
||||||
|
|
||||||
@router.get("/getpipelength/", summary="获取管道长度", description="获取指定管道的长度")
|
@router.get("/pipes/length", summary="获取管道长度", description="获取指定管道的长度")
|
||||||
async def fastapi_get_pipe_length(
|
async def fastapi_get_pipe_length(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID")
|
pipe: str = Query(..., description="管道ID")
|
||||||
@@ -142,7 +142,7 @@ async def fastapi_get_pipe_length(
|
|||||||
ps = get_pipe(network, pipe)
|
ps = get_pipe(network, pipe)
|
||||||
return ps["length"]
|
return ps["length"]
|
||||||
|
|
||||||
@router.get("/getpipediameter/", summary="获取管道管径", description="获取指定管道的管径")
|
@router.get("/pipes/diameter", summary="获取管道管径", description="获取指定管道的管径")
|
||||||
async def fastapi_get_pipe_diameter(
|
async def fastapi_get_pipe_diameter(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID")
|
pipe: str = Query(..., description="管道ID")
|
||||||
@@ -160,7 +160,7 @@ async def fastapi_get_pipe_diameter(
|
|||||||
ps = get_pipe(network, pipe)
|
ps = get_pipe(network, pipe)
|
||||||
return ps["diameter"]
|
return ps["diameter"]
|
||||||
|
|
||||||
@router.get("/getpiperoughness/", summary="获取管道粗糙度", description="获取指定管道的粗糙度")
|
@router.get("/pipes/roughness", summary="获取管道粗糙度", description="获取指定管道的粗糙度")
|
||||||
async def fastapi_get_pipe_roughness(
|
async def fastapi_get_pipe_roughness(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID")
|
pipe: str = Query(..., description="管道ID")
|
||||||
@@ -178,7 +178,7 @@ async def fastapi_get_pipe_roughness(
|
|||||||
ps = get_pipe(network, pipe)
|
ps = get_pipe(network, pipe)
|
||||||
return ps["roughness"]
|
return ps["roughness"]
|
||||||
|
|
||||||
@router.get("/getpipeminorloss/", summary="获取管道局部阻力系数", description="获取指定管道的局部阻力系数")
|
@router.get("/pipes/minor-loss", summary="获取管道局部阻力系数", description="获取指定管道的局部阻力系数")
|
||||||
async def fastapi_get_pipe_minor_loss(
|
async def fastapi_get_pipe_minor_loss(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID")
|
pipe: str = Query(..., description="管道ID")
|
||||||
@@ -196,7 +196,7 @@ async def fastapi_get_pipe_minor_loss(
|
|||||||
ps = get_pipe(network, pipe)
|
ps = get_pipe(network, pipe)
|
||||||
return ps["minor_loss"]
|
return ps["minor_loss"]
|
||||||
|
|
||||||
@router.get("/getpipestatus/", summary="获取管道状态", description="获取指定管道的状态(开启或关闭)")
|
@router.get("/pipes/status", summary="获取管道状态", description="获取指定管道的状态(开启或关闭)")
|
||||||
async def fastapi_get_pipe_status(
|
async def fastapi_get_pipe_status(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID")
|
pipe: str = Query(..., description="管道ID")
|
||||||
@@ -214,7 +214,7 @@ async def fastapi_get_pipe_status(
|
|||||||
ps = get_pipe(network, pipe)
|
ps = get_pipe(network, pipe)
|
||||||
return ps["status"]
|
return ps["status"]
|
||||||
|
|
||||||
@router.post("/setpipenode1/", response_model=None, summary="设置管道起始节点", description="设置指定管道的起始节点")
|
@router.patch("/pipes/node1", response_model=None, summary="设置管道起始节点", description="设置指定管道的起始节点")
|
||||||
async def fastapi_set_pipe_node1(
|
async def fastapi_set_pipe_node1(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID"),
|
pipe: str = Query(..., description="管道ID"),
|
||||||
@@ -234,7 +234,7 @@ async def fastapi_set_pipe_node1(
|
|||||||
ps = {"id": pipe, "node1": node1}
|
ps = {"id": pipe, "node1": node1}
|
||||||
return set_pipe(network, ChangeSet(ps))
|
return set_pipe(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setpipenode2/", response_model=None, summary="设置管道终止节点", description="设置指定管道的终止节点")
|
@router.patch("/pipes/node2", response_model=None, summary="设置管道终止节点", description="设置指定管道的终止节点")
|
||||||
async def fastapi_set_pipe_node2(
|
async def fastapi_set_pipe_node2(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID"),
|
pipe: str = Query(..., description="管道ID"),
|
||||||
@@ -254,7 +254,7 @@ async def fastapi_set_pipe_node2(
|
|||||||
ps = {"id": pipe, "node2": node2}
|
ps = {"id": pipe, "node2": node2}
|
||||||
return set_pipe(network, ChangeSet(ps))
|
return set_pipe(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setpipelength/", response_model=None, summary="设置管道长度", description="设置指定管道的长度")
|
@router.patch("/pipes/length", response_model=None, summary="设置管道长度", description="设置指定管道的长度")
|
||||||
async def fastapi_set_pipe_length(
|
async def fastapi_set_pipe_length(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID"),
|
pipe: str = Query(..., description="管道ID"),
|
||||||
@@ -274,7 +274,7 @@ async def fastapi_set_pipe_length(
|
|||||||
ps = {"id": pipe, "length": length}
|
ps = {"id": pipe, "length": length}
|
||||||
return set_pipe(network, ChangeSet(ps))
|
return set_pipe(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setpipediameter/", response_model=None, summary="设置管道管径", description="设置指定管道的管径")
|
@router.patch("/pipes/diameter", response_model=None, summary="设置管道管径", description="设置指定管道的管径")
|
||||||
async def fastapi_set_pipe_diameter(
|
async def fastapi_set_pipe_diameter(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID"),
|
pipe: str = Query(..., description="管道ID"),
|
||||||
@@ -294,7 +294,7 @@ async def fastapi_set_pipe_diameter(
|
|||||||
ps = {"id": pipe, "diameter": diameter}
|
ps = {"id": pipe, "diameter": diameter}
|
||||||
return set_pipe(network, ChangeSet(ps))
|
return set_pipe(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setpiperoughness/", response_model=None, summary="设置管道粗糙度", description="设置指定管道的粗糙度")
|
@router.patch("/pipes/roughness", response_model=None, summary="设置管道粗糙度", description="设置指定管道的粗糙度")
|
||||||
async def fastapi_set_pipe_roughness(
|
async def fastapi_set_pipe_roughness(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID"),
|
pipe: str = Query(..., description="管道ID"),
|
||||||
@@ -314,7 +314,7 @@ async def fastapi_set_pipe_roughness(
|
|||||||
ps = {"id": pipe, "roughness": roughness}
|
ps = {"id": pipe, "roughness": roughness}
|
||||||
return set_pipe(network, ChangeSet(ps))
|
return set_pipe(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setpipeminorloss/", response_model=None, summary="设置管道局部阻力系数", description="设置指定管道的局部阻力系数")
|
@router.patch("/pipes/minor-loss", response_model=None, summary="设置管道局部阻力系数", description="设置指定管道的局部阻力系数")
|
||||||
async def fastapi_set_pipe_minor_loss(
|
async def fastapi_set_pipe_minor_loss(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID"),
|
pipe: str = Query(..., description="管道ID"),
|
||||||
@@ -334,7 +334,7 @@ async def fastapi_set_pipe_minor_loss(
|
|||||||
ps = {"id": pipe, "minor_loss": minor_loss}
|
ps = {"id": pipe, "minor_loss": minor_loss}
|
||||||
return set_pipe(network, ChangeSet(ps))
|
return set_pipe(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setpipestatus/", response_model=None, summary="设置管道状态", description="设置指定管道的状态(开启或关闭)")
|
@router.patch("/pipes/status", response_model=None, summary="设置管道状态", description="设置指定管道的状态(开启或关闭)")
|
||||||
async def fastapi_set_pipe_status(
|
async def fastapi_set_pipe_status(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID"),
|
pipe: str = Query(..., description="管道ID"),
|
||||||
@@ -354,7 +354,7 @@ async def fastapi_set_pipe_status(
|
|||||||
ps = {"id": pipe, "status": status}
|
ps = {"id": pipe, "status": status}
|
||||||
return set_pipe(network, ChangeSet(ps))
|
return set_pipe(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getpipeproperties/", summary="获取管道属性", description="获取指定管道的所有属性信息")
|
@router.get("/pipes/properties", summary="获取管道属性", description="获取指定管道的所有属性信息")
|
||||||
async def fastapi_get_pipe_properties(
|
async def fastapi_get_pipe_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID")
|
pipe: str = Query(..., description="管道ID")
|
||||||
@@ -371,7 +371,7 @@ async def fastapi_get_pipe_properties(
|
|||||||
"""
|
"""
|
||||||
return get_pipe(network, pipe)
|
return get_pipe(network, pipe)
|
||||||
|
|
||||||
@router.get("/getallpipeproperties/", summary="获取所有管道属性", description="获取网络中所有管道的属性信息列表")
|
@router.get("/pipes", summary="获取所有管道属性", description="获取网络中所有管道的属性信息列表")
|
||||||
async def fastapi_get_all_pipe_properties(
|
async def fastapi_get_all_pipe_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
@@ -389,7 +389,7 @@ async def fastapi_get_all_pipe_properties(
|
|||||||
results = get_all_pipes(network)
|
results = get_all_pipes(network)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@router.post("/setpipeproperties/", response_model=None, summary="设置管道属性", description="批量设置指定管道的多个属性")
|
@router.patch("/pipes/properties", response_model=None, summary="设置管道属性", description="批量设置指定管道的多个属性")
|
||||||
async def fastapi_set_pipe_properties(
|
async def fastapi_set_pipe_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pipe: str = Query(..., description="管道ID"),
|
pipe: str = Query(..., description="管道ID"),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getpumpschema", summary="获取水泵模式", description="获取水泵对象的模式定义,包含所有可用字段及其类型")
|
@router.get("/network-schemas/pump", summary="获取水泵模式", description="获取水泵对象的模式定义,包含所有可用字段及其类型")
|
||||||
async def fastapi_get_pump_schema(
|
async def fastapi_get_pump_schema(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> dict[str, dict[str, Any]]:
|
) -> dict[str, dict[str, Any]]:
|
||||||
@@ -28,7 +28,7 @@ async def fastapi_get_pump_schema(
|
|||||||
"""
|
"""
|
||||||
return get_pump_schema(network)
|
return get_pump_schema(network)
|
||||||
|
|
||||||
@router.post("/addpump/", response_model=None, summary="添加水泵", description="向网络中添加新的水泵,需要提供水泵的基本参数如功率等")
|
@router.post("/pumps", response_model=None, summary="添加水泵", description="向网络中添加新的水泵,需要提供水泵的基本参数如功率等")
|
||||||
async def fastapi_add_pump(
|
async def fastapi_add_pump(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="水泵标识符"),
|
pump: str = Query(..., description="水泵标识符"),
|
||||||
@@ -52,7 +52,7 @@ async def fastapi_add_pump(
|
|||||||
ps = {"id": pump, "node1": node1, "node2": node2, "power": power}
|
ps = {"id": pump, "node1": node1, "node2": node2, "power": power}
|
||||||
return add_pump(network, ChangeSet(ps))
|
return add_pump(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/deletepump/", response_model=None, summary="删除水泵", description="从网络中删除指定的水泵")
|
@router.delete("/pumps", response_model=None, summary="删除水泵", description="从网络中删除指定的水泵")
|
||||||
async def fastapi_delete_pump(
|
async def fastapi_delete_pump(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="要删除的水泵ID")
|
pump: str = Query(..., description="要删除的水泵ID")
|
||||||
@@ -70,7 +70,7 @@ async def fastapi_delete_pump(
|
|||||||
ps = {"id": pump}
|
ps = {"id": pump}
|
||||||
return delete_pump(network, ChangeSet(ps))
|
return delete_pump(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getpumpnode1/", summary="获取水泵起始节点", description="获取指定水泵的起始节点ID")
|
@router.get("/pumps/node1", summary="获取水泵起始节点", description="获取指定水泵的起始节点ID")
|
||||||
async def fastapi_get_pump_node1(
|
async def fastapi_get_pump_node1(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="水泵ID")
|
pump: str = Query(..., description="水泵ID")
|
||||||
@@ -88,7 +88,7 @@ async def fastapi_get_pump_node1(
|
|||||||
ps = get_pump(network, pump)
|
ps = get_pump(network, pump)
|
||||||
return ps["node1"]
|
return ps["node1"]
|
||||||
|
|
||||||
@router.get("/getpumpnode2/", summary="获取水泵终止节点", description="获取指定水泵的终止节点ID")
|
@router.get("/pumps/node2", summary="获取水泵终止节点", description="获取指定水泵的终止节点ID")
|
||||||
async def fastapi_get_pump_node2(
|
async def fastapi_get_pump_node2(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="水泵ID")
|
pump: str = Query(..., description="水泵ID")
|
||||||
@@ -106,7 +106,7 @@ async def fastapi_get_pump_node2(
|
|||||||
ps = get_pump(network, pump)
|
ps = get_pump(network, pump)
|
||||||
return ps["node2"]
|
return ps["node2"]
|
||||||
|
|
||||||
@router.post("/setpumpnode1/", response_model=None, summary="设置水泵起始节点", description="设置指定水泵的起始节点")
|
@router.patch("/pumps/node1", response_model=None, summary="设置水泵起始节点", description="设置指定水泵的起始节点")
|
||||||
async def fastapi_set_pump_node1(
|
async def fastapi_set_pump_node1(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="水泵ID"),
|
pump: str = Query(..., description="水泵ID"),
|
||||||
@@ -126,7 +126,7 @@ async def fastapi_set_pump_node1(
|
|||||||
ps = {"id": pump, "node1": node1}
|
ps = {"id": pump, "node1": node1}
|
||||||
return set_pump(network, ChangeSet(ps))
|
return set_pump(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/setpumpnode2/", response_model=None, summary="设置水泵终止节点", description="设置指定水泵的终止节点")
|
@router.patch("/pumps/node2", response_model=None, summary="设置水泵终止节点", description="设置指定水泵的终止节点")
|
||||||
async def fastapi_set_pump_node2(
|
async def fastapi_set_pump_node2(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="水泵ID"),
|
pump: str = Query(..., description="水泵ID"),
|
||||||
@@ -146,7 +146,7 @@ async def fastapi_set_pump_node2(
|
|||||||
ps = {"id": pump, "node2": node2}
|
ps = {"id": pump, "node2": node2}
|
||||||
return set_pump(network, ChangeSet(ps))
|
return set_pump(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/getpumpproperties/", summary="获取水泵属性", description="获取指定水泵的所有属性信息")
|
@router.get("/pumps/properties", summary="获取水泵属性", description="获取指定水泵的所有属性信息")
|
||||||
async def fastapi_get_pump_properties(
|
async def fastapi_get_pump_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="水泵ID")
|
pump: str = Query(..., description="水泵ID")
|
||||||
@@ -163,7 +163,7 @@ async def fastapi_get_pump_properties(
|
|||||||
"""
|
"""
|
||||||
return get_pump(network, pump)
|
return get_pump(network, pump)
|
||||||
|
|
||||||
@router.get("/getallpumpproperties/", summary="获取所有水泵属性", description="获取网络中所有水泵的属性信息列表")
|
@router.get("/pumps", summary="获取所有水泵属性", description="获取网络中所有水泵的属性信息列表")
|
||||||
async def fastapi_get_all_pump_properties(
|
async def fastapi_get_all_pump_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
@@ -181,7 +181,7 @@ async def fastapi_get_all_pump_properties(
|
|||||||
results = get_all_pumps(network)
|
results = get_all_pumps(network)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@router.post("/setpumpproperties/", response_model=None, summary="设置水泵属性", description="批量设置指定水泵的多个属性")
|
@router.patch("/pumps/properties", response_model=None, summary="设置水泵属性", description="批量设置指定水泵的多个属性")
|
||||||
async def fastapi_set_pump_properties(
|
async def fastapi_set_pump_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
pump: str = Query(..., description="水泵ID"),
|
pump: str = Query(..., description="水泵ID"),
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ router = APIRouter()
|
|||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getregionschema/",
|
"/network-schemas/region",
|
||||||
summary="获取区域属性架构",
|
summary="获取区域属性架构",
|
||||||
description="获取指定水网的区域属性架构定义"
|
description="获取指定水网的区域属性架构定义"
|
||||||
)
|
)
|
||||||
@@ -56,7 +56,7 @@ async def fastapi_get_region_schema(
|
|||||||
return get_region_schema(network)
|
return get_region_schema(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getregion/",
|
"/regions/detail",
|
||||||
summary="获取区域信息",
|
summary="获取区域信息",
|
||||||
description="获取指定ID的区域详细信息"
|
description="获取指定ID的区域详细信息"
|
||||||
)
|
)
|
||||||
@@ -67,8 +67,8 @@ async def fastapi_get_region(
|
|||||||
"""获取区域的详细信息。"""
|
"""获取区域的详细信息。"""
|
||||||
return get_region(network, id)
|
return get_region(network, id)
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setregion/",
|
"/regions",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置区域属性",
|
summary="设置区域属性",
|
||||||
description="修改指定区域的属性信息"
|
description="修改指定区域的属性信息"
|
||||||
@@ -82,7 +82,7 @@ async def fastapi_set_region(
|
|||||||
return set_region(network, ChangeSet(props))
|
return set_region(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/addregion/",
|
"/regions",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="添加新区域",
|
summary="添加新区域",
|
||||||
description="向水网添加一个新的区域"
|
description="向水网添加一个新的区域"
|
||||||
@@ -95,8 +95,8 @@ async def fastapi_add_region(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_region(network, ChangeSet(props))
|
return add_region(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post(
|
@router.delete(
|
||||||
"/deleteregion/",
|
"/regions",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="删除区域",
|
summary="删除区域",
|
||||||
description="删除指定的区域"
|
description="删除指定的区域"
|
||||||
@@ -114,8 +114,8 @@ async def fastapi_delete_region(
|
|||||||
# district_metering_area 33
|
# district_metering_area 33
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.post(
|
||||||
"/calculatedistrictmeteringareaforregion/",
|
"/district-metering-areas/for-region",
|
||||||
summary="计算区域内DMA分区",
|
summary="计算区域内DMA分区",
|
||||||
description="为指定区域计算区域计量(DMA)分区方案"
|
description="为指定区域计算区域计量(DMA)分区方案"
|
||||||
)
|
)
|
||||||
@@ -141,8 +141,8 @@ async def fastapi_calculate_district_metering_area_for_region(
|
|||||||
network, region, part_count, part_type
|
network, region, part_count, part_type
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.get(
|
@router.post(
|
||||||
"/calculatedistrictmeteringareafornetwork/",
|
"/district-metering-areas/for-network",
|
||||||
summary="计算整网DMA分区",
|
summary="计算整网DMA分区",
|
||||||
description="为整个水网计算区域计量(DMA)分区方案"
|
description="为整个水网计算区域计量(DMA)分区方案"
|
||||||
)
|
)
|
||||||
@@ -165,7 +165,7 @@ async def fastapi_calculate_district_metering_area_for_network(
|
|||||||
return calculate_district_metering_area_for_network(network, part_count, part_type)
|
return calculate_district_metering_area_for_network(network, part_count, part_type)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getdistrictmeteringareaschema/",
|
"/network-schemas/district-metering-area",
|
||||||
summary="获取DMA属性架构",
|
summary="获取DMA属性架构",
|
||||||
description="获取指定水网的区域计量(DMA)属性架构定义"
|
description="获取指定水网的区域计量(DMA)属性架构定义"
|
||||||
)
|
)
|
||||||
@@ -176,7 +176,7 @@ async def fastapi_get_district_metering_area_schema(
|
|||||||
return get_district_metering_area_schema(network)
|
return get_district_metering_area_schema(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getdistrictmeteringarea/",
|
"/district-metering-areas/detail",
|
||||||
summary="获取DMA信息",
|
summary="获取DMA信息",
|
||||||
description="获取指定ID的区域计量(DMA)详细信息"
|
description="获取指定ID的区域计量(DMA)详细信息"
|
||||||
)
|
)
|
||||||
@@ -187,8 +187,8 @@ async def fastapi_get_district_metering_area(
|
|||||||
"""获取DMA的详细信息。"""
|
"""获取DMA的详细信息。"""
|
||||||
return get_district_metering_area(network, id)
|
return get_district_metering_area(network, id)
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setdistrictmeteringarea/",
|
"/district-metering-areas",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置DMA属性",
|
summary="设置DMA属性",
|
||||||
description="修改指定DMA的属性信息"
|
description="修改指定DMA的属性信息"
|
||||||
@@ -202,7 +202,7 @@ async def fastapi_set_district_metering_area(
|
|||||||
return set_district_metering_area(network, ChangeSet(props))
|
return set_district_metering_area(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/adddistrictmeteringarea/",
|
"/district-metering-areas",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="添加新DMA",
|
summary="添加新DMA",
|
||||||
description="向水网添加一个新的区域计量(DMA)"
|
description="向水网添加一个新的区域计量(DMA)"
|
||||||
@@ -222,8 +222,8 @@ async def fastapi_add_district_metering_area(
|
|||||||
props["boundary"] = newBoundary
|
props["boundary"] = newBoundary
|
||||||
return add_district_metering_area(network, ChangeSet(props))
|
return add_district_metering_area(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post(
|
@router.delete(
|
||||||
"/deletedistrictmeteringarea/",
|
"/district-metering-areas",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="删除DMA",
|
summary="删除DMA",
|
||||||
description="删除指定的区域计量(DMA)"
|
description="删除指定的区域计量(DMA)"
|
||||||
@@ -237,7 +237,7 @@ async def fastapi_delete_district_metering_area(
|
|||||||
return delete_district_metering_area(network, ChangeSet(props))
|
return delete_district_metering_area(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getalldistrictmeteringareaids/",
|
"/district-metering-areas/ids",
|
||||||
summary="获取所有DMA ID",
|
summary="获取所有DMA ID",
|
||||||
description="获取指定水网中所有DMA的ID列表"
|
description="获取指定水网中所有DMA的ID列表"
|
||||||
)
|
)
|
||||||
@@ -248,7 +248,7 @@ async def fastapi_get_all_district_metering_area_ids(
|
|||||||
return get_all_district_metering_area_ids(network)
|
return get_all_district_metering_area_ids(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getalldistrictmeteringareas/",
|
"/district-metering-areas",
|
||||||
summary="获取所有DMA",
|
summary="获取所有DMA",
|
||||||
description="获取指定水网中所有DMA的详细信息"
|
description="获取指定水网中所有DMA的详细信息"
|
||||||
)
|
)
|
||||||
@@ -259,7 +259,7 @@ async def getalldistrictmeteringareas(
|
|||||||
return get_all_district_metering_areas(network)
|
return get_all_district_metering_areas(network)
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/generatedistrictmeteringarea/",
|
"/district-metering-area-generation-runs",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="生成DMA分区",
|
summary="生成DMA分区",
|
||||||
description="根据参数自动生成水网的DMA分区方案"
|
description="根据参数自动生成水网的DMA分区方案"
|
||||||
@@ -276,7 +276,7 @@ async def fastapi_generate_district_metering_area(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/generatesubdistrictmeteringarea/",
|
"/sub-district-metering-areas",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="生成DMA子分区",
|
summary="生成DMA子分区",
|
||||||
description="为指定DMA生成子DMA分区"
|
description="为指定DMA生成子DMA分区"
|
||||||
@@ -298,8 +298,8 @@ async def fastapi_generate_sub_district_metering_area(
|
|||||||
# service_area 34
|
# service_area 34
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.post(
|
||||||
"/calculateservicearea/",
|
"/service-area-calculations",
|
||||||
summary="计算服务区",
|
summary="计算服务区",
|
||||||
description="计算指定水网的服务区分区,返回全部时间步结果"
|
description="计算指定水网的服务区分区,返回全部时间步结果"
|
||||||
)
|
)
|
||||||
@@ -310,7 +310,7 @@ async def fastapi_calculate_service_area(
|
|||||||
return calculate_service_area(network)
|
return calculate_service_area(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getserviceareaschema/",
|
"/network-schemas/service-area",
|
||||||
summary="获取服务区属性架构",
|
summary="获取服务区属性架构",
|
||||||
description="获取指定水网的服务区属性架构定义"
|
description="获取指定水网的服务区属性架构定义"
|
||||||
)
|
)
|
||||||
@@ -321,7 +321,7 @@ async def fastapi_get_service_area_schema(
|
|||||||
return get_service_area_schema(network)
|
return get_service_area_schema(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getservicearea/",
|
"/service-areas/detail",
|
||||||
summary="获取服务区信息",
|
summary="获取服务区信息",
|
||||||
description="获取指定ID的服务区详细信息"
|
description="获取指定ID的服务区详细信息"
|
||||||
)
|
)
|
||||||
@@ -332,8 +332,8 @@ async def fastapi_get_service_area(
|
|||||||
"""获取服务区的详细信息。"""
|
"""获取服务区的详细信息。"""
|
||||||
return get_service_area(network, id)
|
return get_service_area(network, id)
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setservicearea/",
|
"/service-areas",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置服务区属性",
|
summary="设置服务区属性",
|
||||||
description="修改指定服务区的属性信息"
|
description="修改指定服务区的属性信息"
|
||||||
@@ -347,7 +347,7 @@ async def fastapi_set_service_area(
|
|||||||
return set_service_area(network, ChangeSet(props))
|
return set_service_area(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/addservicearea/",
|
"/service-areas",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="添加新服务区",
|
summary="添加新服务区",
|
||||||
description="向水网添加一个新的服务区"
|
description="向水网添加一个新的服务区"
|
||||||
@@ -360,8 +360,8 @@ async def fastapi_add_service_area(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_service_area(network, ChangeSet(props))
|
return add_service_area(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post(
|
@router.delete(
|
||||||
"/deleteservicearea/",
|
"/service-areas",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="删除服务区",
|
summary="删除服务区",
|
||||||
description="删除指定的服务区"
|
description="删除指定的服务区"
|
||||||
@@ -375,7 +375,7 @@ async def fastapi_delete_service_area(
|
|||||||
return delete_service_area(network, ChangeSet(props))
|
return delete_service_area(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getallserviceareas/",
|
"/service-areas",
|
||||||
summary="获取所有服务区",
|
summary="获取所有服务区",
|
||||||
description="获取指定水网中的所有服务区信息"
|
description="获取指定水网中的所有服务区信息"
|
||||||
)
|
)
|
||||||
@@ -386,7 +386,7 @@ async def fastapi_get_all_service_areas(
|
|||||||
return get_all_service_areas(network)
|
return get_all_service_areas(network)
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/generateservicearea/",
|
"/service-area-generation-runs",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="生成服务区分区",
|
summary="生成服务区分区",
|
||||||
description="根据参数自动生成水网的服务区分区"
|
description="根据参数自动生成水网的服务区分区"
|
||||||
@@ -403,8 +403,8 @@ async def fastapi_generate_service_area(
|
|||||||
# virtual_district 35
|
# virtual_district 35
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.post(
|
||||||
"/calculatevirtualdistrict/",
|
"/virtual-district-calculations",
|
||||||
summary="计算虚拟分区",
|
summary="计算虚拟分区",
|
||||||
description="根据指定的压力监测节点作为中心节点计算虚拟分区方案"
|
description="根据指定的压力监测节点作为中心节点计算虚拟分区方案"
|
||||||
)
|
)
|
||||||
@@ -416,7 +416,7 @@ async def fastapi_calculate_virtual_district(
|
|||||||
return calculate_virtual_district(network, centers)
|
return calculate_virtual_district(network, centers)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvirtualdistrictschema/",
|
"/network-schemas/virtual-district",
|
||||||
summary="获取虚拟分区属性架构",
|
summary="获取虚拟分区属性架构",
|
||||||
description="获取指定水网的虚拟分区属性架构定义"
|
description="获取指定水网的虚拟分区属性架构定义"
|
||||||
)
|
)
|
||||||
@@ -427,7 +427,7 @@ async def fastapi_get_virtual_district_schema(
|
|||||||
return get_virtual_district_schema(network)
|
return get_virtual_district_schema(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvirtualdistrict/",
|
"/virtual-districts/detail",
|
||||||
summary="获取虚拟分区信息",
|
summary="获取虚拟分区信息",
|
||||||
description="获取指定ID的虚拟分区详细信息"
|
description="获取指定ID的虚拟分区详细信息"
|
||||||
)
|
)
|
||||||
@@ -438,8 +438,8 @@ async def fastapi_get_virtual_district(
|
|||||||
"""获取虚拟分区的详细信息。"""
|
"""获取虚拟分区的详细信息。"""
|
||||||
return get_virtual_district(network, id)
|
return get_virtual_district(network, id)
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setvirtualdistrict/",
|
"/virtual-districts",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置虚拟分区属性",
|
summary="设置虚拟分区属性",
|
||||||
description="修改指定虚拟分区的属性信息"
|
description="修改指定虚拟分区的属性信息"
|
||||||
@@ -453,7 +453,7 @@ async def fastapi_set_virtual_district(
|
|||||||
return set_virtual_district(network, ChangeSet(props))
|
return set_virtual_district(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/addvirtualdistrict/",
|
"/virtual-districts",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="添加新虚拟分区",
|
summary="添加新虚拟分区",
|
||||||
description="向水网添加一个新的虚拟分区"
|
description="向水网添加一个新的虚拟分区"
|
||||||
@@ -466,8 +466,8 @@ async def fastapi_add_virtual_district(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_virtual_district(network, ChangeSet(props))
|
return add_virtual_district(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post(
|
@router.delete(
|
||||||
"/deletevirtualdistrict/",
|
"/virtual-districts",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="删除虚拟分区",
|
summary="删除虚拟分区",
|
||||||
description="删除指定的虚拟分区"
|
description="删除指定的虚拟分区"
|
||||||
@@ -481,7 +481,7 @@ async def fastapi_delete_virtual_district(
|
|||||||
return delete_virtual_district(network, ChangeSet(props))
|
return delete_virtual_district(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getallvirtualdistrict/",
|
"/virtual-districts",
|
||||||
summary="获取所有虚拟分区",
|
summary="获取所有虚拟分区",
|
||||||
description="获取指定水网中的所有虚拟分区信息"
|
description="获取指定水网中的所有虚拟分区信息"
|
||||||
)
|
)
|
||||||
@@ -492,7 +492,7 @@ async def fastapi_get_all_virtual_district(
|
|||||||
return get_all_virtual_districts(network)
|
return get_all_virtual_districts(network)
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/generatevirtualdistrict/",
|
"/virtual-district-generation-runs",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="生成虚拟分区",
|
summary="生成虚拟分区",
|
||||||
description="根据参数自动生成虚拟分区方案"
|
description="根据参数自动生成虚拟分区方案"
|
||||||
@@ -506,8 +506,8 @@ async def fastapi_generate_virtual_district(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return generate_virtual_district(network, props["centers"], inflate_delta)
|
return generate_virtual_district(network, props["centers"], inflate_delta)
|
||||||
|
|
||||||
@router.get(
|
@router.post(
|
||||||
"/calculatedistrictmeteringareafornodes/",
|
"/district-metering-areas/for-nodes",
|
||||||
summary="计算节点DMA分区",
|
summary="计算节点DMA分区",
|
||||||
description="为指定节点集计算区域计量(DMA)分区方案"
|
description="为指定节点集计算区域计量(DMA)分区方案"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from app.services.tjnetwork import (
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getreservoirschema",
|
"/network-schemas/reservoir",
|
||||||
summary="获取水库模式",
|
summary="获取水库模式",
|
||||||
description="获取指定供水网络中所有水库的模式/属性字段定义"
|
description="获取指定供水网络中所有水库的模式/属性字段定义"
|
||||||
)
|
)
|
||||||
@@ -35,7 +35,7 @@ async def fast_get_reservoir_schema(
|
|||||||
return get_reservoir_schema(network)
|
return get_reservoir_schema(network)
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/addreservoir/",
|
"/reservoirs",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="添加水库",
|
summary="添加水库",
|
||||||
description="在指定供水网络中添加新的水库/水源节点"
|
description="在指定供水网络中添加新的水库/水源节点"
|
||||||
@@ -65,8 +65,8 @@ async def fastapi_add_reservoir(
|
|||||||
ps = {"id": reservoir, "x": x, "y": y, "head": head}
|
ps = {"id": reservoir, "x": x, "y": y, "head": head}
|
||||||
return add_reservoir(network, ChangeSet(ps))
|
return add_reservoir(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.delete(
|
||||||
"/deletereservoir/",
|
"/reservoirs",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="删除水库",
|
summary="删除水库",
|
||||||
description="从指定供水网络中删除指定的水库/水源节点"
|
description="从指定供水网络中删除指定的水库/水源节点"
|
||||||
@@ -91,7 +91,7 @@ async def fastapi_delete_reservoir(
|
|||||||
return delete_reservoir(network, ChangeSet(ps))
|
return delete_reservoir(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getreservoirhead/",
|
"/reservoirs/head",
|
||||||
summary="获取水库水头",
|
summary="获取水库水头",
|
||||||
description="获取指定水库的供水水头/总水头值"
|
description="获取指定水库的供水水头/总水头值"
|
||||||
)
|
)
|
||||||
@@ -115,7 +115,7 @@ async def fastapi_get_reservoir_head(
|
|||||||
return ps["head"]
|
return ps["head"]
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getreservoirpattern/",
|
"/reservoirs/pattern",
|
||||||
summary="获取水库模式",
|
summary="获取水库模式",
|
||||||
description="获取指定水库的运行模式/供水模式"
|
description="获取指定水库的运行模式/供水模式"
|
||||||
)
|
)
|
||||||
@@ -139,7 +139,7 @@ async def fastapi_get_reservoir_pattern(
|
|||||||
return ps["pattern"]
|
return ps["pattern"]
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getreservoirx/",
|
"/reservoirs/x",
|
||||||
summary="获取水库X坐标",
|
summary="获取水库X坐标",
|
||||||
description="获取指定水库的X坐标位置"
|
description="获取指定水库的X坐标位置"
|
||||||
)
|
)
|
||||||
@@ -163,7 +163,7 @@ async def fastapi_get_reservoir_x(
|
|||||||
return ps["x"]
|
return ps["x"]
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getreservoiry/",
|
"/reservoirs/y",
|
||||||
summary="获取水库Y坐标",
|
summary="获取水库Y坐标",
|
||||||
description="获取指定水库的Y坐标位置"
|
description="获取指定水库的Y坐标位置"
|
||||||
)
|
)
|
||||||
@@ -187,7 +187,7 @@ async def fastapi_get_reservoir_y(
|
|||||||
return ps["y"]
|
return ps["y"]
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getreservoircoord/",
|
"/reservoirs/coord",
|
||||||
summary="获取水库坐标",
|
summary="获取水库坐标",
|
||||||
description="获取指定水库的平面坐标(X和Y坐标)"
|
description="获取指定水库的平面坐标(X和Y坐标)"
|
||||||
)
|
)
|
||||||
@@ -211,8 +211,8 @@ async def fastapi_get_reservoir_coord(
|
|||||||
coord = {"id": reservoir, "x": ps["x"], "y": ps["y"]}
|
coord = {"id": reservoir, "x": ps["x"], "y": ps["y"]}
|
||||||
return coord
|
return coord
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setreservoirhead/",
|
"/reservoirs/head",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置水库水头",
|
summary="设置水库水头",
|
||||||
description="更新指定水库的供水水头/总水头值"
|
description="更新指定水库的供水水头/总水头值"
|
||||||
@@ -238,8 +238,8 @@ async def fastapi_set_reservoir_head(
|
|||||||
ps = {"id": reservoir, "head": head}
|
ps = {"id": reservoir, "head": head}
|
||||||
return set_reservoir(network, ChangeSet(ps))
|
return set_reservoir(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setreservoirpattern/",
|
"/reservoirs/pattern",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置水库模式",
|
summary="设置水库模式",
|
||||||
description="更新指定水库的运行模式/供水模式"
|
description="更新指定水库的运行模式/供水模式"
|
||||||
@@ -265,8 +265,8 @@ async def fastapi_set_reservoir_pattern(
|
|||||||
ps = {"id": reservoir, "pattern": pattern}
|
ps = {"id": reservoir, "pattern": pattern}
|
||||||
return set_reservoir(network, ChangeSet(ps))
|
return set_reservoir(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setreservoirx/",
|
"/reservoirs/x",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置水库X坐标",
|
summary="设置水库X坐标",
|
||||||
description="更新指定水库的X坐标位置"
|
description="更新指定水库的X坐标位置"
|
||||||
@@ -292,8 +292,8 @@ async def fastapi_set_reservoir_x(
|
|||||||
ps = {"id": reservoir, "x": x}
|
ps = {"id": reservoir, "x": x}
|
||||||
return set_reservoir(network, ChangeSet(ps))
|
return set_reservoir(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setreservoiry/",
|
"/reservoirs/y",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置水库Y坐标",
|
summary="设置水库Y坐标",
|
||||||
description="更新指定水库的Y坐标位置"
|
description="更新指定水库的Y坐标位置"
|
||||||
@@ -319,8 +319,8 @@ async def fastapi_set_reservoir_y(
|
|||||||
ps = {"id": reservoir, "y": y}
|
ps = {"id": reservoir, "y": y}
|
||||||
return set_reservoir(network, ChangeSet(ps))
|
return set_reservoir(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setreservoircoord/",
|
"/reservoirs/coord",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置水库坐标",
|
summary="设置水库坐标",
|
||||||
description="更新指定水库的平面坐标(X和Y坐标)"
|
description="更新指定水库的平面坐标(X和Y坐标)"
|
||||||
@@ -349,7 +349,7 @@ async def fastapi_set_reservoir_coord(
|
|||||||
return set_reservoir(network, ChangeSet(ps))
|
return set_reservoir(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getreservoirproperties/",
|
"/reservoirs/properties",
|
||||||
summary="获取水库属性",
|
summary="获取水库属性",
|
||||||
description="获取指定水库的所有属性"
|
description="获取指定水库的所有属性"
|
||||||
)
|
)
|
||||||
@@ -372,7 +372,7 @@ async def fastapi_get_reservoir_properties(
|
|||||||
return get_reservoir(network, reservoir)
|
return get_reservoir(network, reservoir)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getallreservoirproperties/",
|
"/reservoirs",
|
||||||
summary="获取所有水库属性",
|
summary="获取所有水库属性",
|
||||||
description="获取指定供水网络中所有水库的属性"
|
description="获取指定供水网络中所有水库的属性"
|
||||||
)
|
)
|
||||||
@@ -393,8 +393,8 @@ async def fastapi_get_all_reservoir_properties(
|
|||||||
results = get_all_reservoirs(network)
|
results = get_all_reservoirs(network)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setreservoirproperties/",
|
"/reservoirs/properties",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置水库属性",
|
summary="设置水库属性",
|
||||||
description="批量更新指定水库的多个属性"
|
description="批量更新指定水库的多个属性"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ router = APIRouter()
|
|||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/gettagschema/",
|
"/network-schemas/tag",
|
||||||
summary="获取标签属性架构",
|
summary="获取标签属性架构",
|
||||||
description="获取指定水网的标签(Tag)属性架构定义"
|
description="获取指定水网的标签(Tag)属性架构定义"
|
||||||
)
|
)
|
||||||
@@ -27,7 +27,7 @@ async def fastapi_get_tag_schema(
|
|||||||
return get_tag_schema(network)
|
return get_tag_schema(network)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/gettag/",
|
"/tags/detail",
|
||||||
summary="获取标签信息",
|
summary="获取标签信息",
|
||||||
description="获取指定类型和ID的标签信息"
|
description="获取指定类型和ID的标签信息"
|
||||||
)
|
)
|
||||||
@@ -40,7 +40,7 @@ async def fastapi_get_tag(
|
|||||||
return get_tag(network, t_type, id)
|
return get_tag(network, t_type, id)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/gettags/",
|
"/tags",
|
||||||
summary="获取所有标签",
|
summary="获取所有标签",
|
||||||
description="获取指定水网中的所有标签信息"
|
description="获取指定水网中的所有标签信息"
|
||||||
)
|
)
|
||||||
@@ -51,8 +51,8 @@ async def fastapi_get_tags(
|
|||||||
tags = get_tags(network)
|
tags = get_tags(network)
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/settag/",
|
"/tags",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置标签",
|
summary="设置标签",
|
||||||
description="为指定元素设置或修改标签信息"
|
description="为指定元素设置或修改标签信息"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/gettankschema", summary="获取水箱模式", description="获取指定网络的水箱数据结构模式定义")
|
@router.get("/network-schemas/tank", summary="获取水箱模式", description="获取指定网络的水箱数据结构模式定义")
|
||||||
async def fast_get_tank_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
async def fast_get_tank_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
获取水箱的数据结构模式。
|
获取水箱的数据结构模式。
|
||||||
@@ -26,7 +26,7 @@ async def fast_get_tank_schema(network: str = Query(..., description="管网名
|
|||||||
"""
|
"""
|
||||||
return get_tank_schema(network)
|
return get_tank_schema(network)
|
||||||
|
|
||||||
@router.post("/addtank/", summary="新增水箱", description="向指定网络中新增一个水箱", response_model=None)
|
@router.post("/tanks", summary="新增水箱", description="向指定网络中新增一个水箱", response_model=None)
|
||||||
async def fastapi_add_tank(
|
async def fastapi_add_tank(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -70,7 +70,7 @@ async def fastapi_add_tank(
|
|||||||
}
|
}
|
||||||
return add_tank(network, ChangeSet(ps))
|
return add_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/deletetank/", summary="删除水箱", description="删除指定网络中的水箱", response_model=None)
|
@router.delete("/tanks", summary="删除水箱", description="删除指定网络中的水箱", response_model=None)
|
||||||
async def fastapi_delete_tank(
|
async def fastapi_delete_tank(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -88,7 +88,7 @@ async def fastapi_delete_tank(
|
|||||||
ps = {"id": tank}
|
ps = {"id": tank}
|
||||||
return delete_tank(network, ChangeSet(ps))
|
return delete_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/gettankelevation/", summary="获取水箱标高", description="获取指定水箱的标高值")
|
@router.get("/tanks/elevation", summary="获取水箱标高", description="获取指定水箱的标高值")
|
||||||
async def fastapi_get_tank_elevation(
|
async def fastapi_get_tank_elevation(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -106,7 +106,7 @@ async def fastapi_get_tank_elevation(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["elevation"]
|
return ps["elevation"]
|
||||||
|
|
||||||
@router.get("/gettankinitlevel/", summary="获取水箱初始水位", description="获取指定水箱的初始水位值")
|
@router.get("/tanks/init-level", summary="获取水箱初始水位", description="获取指定水箱的初始水位值")
|
||||||
async def fastapi_get_tank_init_level(
|
async def fastapi_get_tank_init_level(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -124,7 +124,7 @@ async def fastapi_get_tank_init_level(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["init_level"]
|
return ps["init_level"]
|
||||||
|
|
||||||
@router.get("/gettankminlevel/", summary="获取水箱最小水位", description="获取指定水箱的最小水位值")
|
@router.get("/tanks/min-level", summary="获取水箱最小水位", description="获取指定水箱的最小水位值")
|
||||||
async def fastapi_get_tank_min_level(
|
async def fastapi_get_tank_min_level(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -142,7 +142,7 @@ async def fastapi_get_tank_min_level(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["min_level"]
|
return ps["min_level"]
|
||||||
|
|
||||||
@router.get("/gettankmaxlevel/", summary="获取水箱最大水位", description="获取指定水箱的最大水位值")
|
@router.get("/tanks/max-level", summary="获取水箱最大水位", description="获取指定水箱的最大水位值")
|
||||||
async def fastapi_get_tank_max_level(
|
async def fastapi_get_tank_max_level(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -160,7 +160,7 @@ async def fastapi_get_tank_max_level(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["max_level"]
|
return ps["max_level"]
|
||||||
|
|
||||||
@router.get("/gettankdiameter/", summary="获取水箱直径", description="获取指定水箱的直径值")
|
@router.get("/tanks/diameter", summary="获取水箱直径", description="获取指定水箱的直径值")
|
||||||
async def fastapi_get_tank_diameter(
|
async def fastapi_get_tank_diameter(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -178,7 +178,7 @@ async def fastapi_get_tank_diameter(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["diameter"]
|
return ps["diameter"]
|
||||||
|
|
||||||
@router.get("/gettankminvol/", summary="获取水箱最小体积", description="获取指定水箱的最小体积值")
|
@router.get("/tanks/min-vol", summary="获取水箱最小体积", description="获取指定水箱的最小体积值")
|
||||||
async def fastapi_get_tank_min_vol(
|
async def fastapi_get_tank_min_vol(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -196,7 +196,7 @@ async def fastapi_get_tank_min_vol(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["min_vol"]
|
return ps["min_vol"]
|
||||||
|
|
||||||
@router.get("/gettankvolcurve/", summary="获取水箱容积曲线", description="获取指定水箱的容积曲线标识")
|
@router.get("/tanks/vol-curve", summary="获取水箱容积曲线", description="获取指定水箱的容积曲线标识")
|
||||||
async def fastapi_get_tank_vol_curve(
|
async def fastapi_get_tank_vol_curve(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -214,7 +214,7 @@ async def fastapi_get_tank_vol_curve(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["vol_curve"]
|
return ps["vol_curve"]
|
||||||
|
|
||||||
@router.get("/gettankoverflow/", summary="获取水箱溢流口", description="获取指定水箱的溢流口配置")
|
@router.get("/tanks/overflow", summary="获取水箱溢流口", description="获取指定水箱的溢流口配置")
|
||||||
async def fastapi_get_tank_overflow(
|
async def fastapi_get_tank_overflow(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -232,7 +232,7 @@ async def fastapi_get_tank_overflow(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["overflow"]
|
return ps["overflow"]
|
||||||
|
|
||||||
@router.get("/gettankx/", summary="获取水箱X坐标", description="获取指定水箱的X坐标值")
|
@router.get("/tanks/x", summary="获取水箱X坐标", description="获取指定水箱的X坐标值")
|
||||||
async def fastapi_get_tank_x(
|
async def fastapi_get_tank_x(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -250,7 +250,7 @@ async def fastapi_get_tank_x(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["x"]
|
return ps["x"]
|
||||||
|
|
||||||
@router.get("/gettanky/", summary="获取水箱Y坐标", description="获取指定水箱的Y坐标值")
|
@router.get("/tanks/y", summary="获取水箱Y坐标", description="获取指定水箱的Y坐标值")
|
||||||
async def fastapi_get_tank_y(
|
async def fastapi_get_tank_y(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -268,7 +268,7 @@ async def fastapi_get_tank_y(
|
|||||||
ps = get_tank(network, tank)
|
ps = get_tank(network, tank)
|
||||||
return ps["y"]
|
return ps["y"]
|
||||||
|
|
||||||
@router.get("/gettankcoord/", summary="获取水箱坐标", description="获取指定水箱的X和Y坐标")
|
@router.get("/tanks/coord", summary="获取水箱坐标", description="获取指定水箱的X和Y坐标")
|
||||||
async def fastapi_get_tank_coord(
|
async def fastapi_get_tank_coord(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -287,7 +287,7 @@ async def fastapi_get_tank_coord(
|
|||||||
coord = {"x": ps["x"], "y": ps["y"]}
|
coord = {"x": ps["x"], "y": ps["y"]}
|
||||||
return coord
|
return coord
|
||||||
|
|
||||||
@router.post("/settankelevation/", summary="设置水箱标高", description="设置指定水箱的标高值", response_model=None)
|
@router.patch("/tanks/elevation", summary="设置水箱标高", description="设置指定水箱的标高值", response_model=None)
|
||||||
async def fastapi_set_tank_elevation(
|
async def fastapi_set_tank_elevation(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -307,7 +307,7 @@ async def fastapi_set_tank_elevation(
|
|||||||
ps = {"id": tank, "elevation": elevation}
|
ps = {"id": tank, "elevation": elevation}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settankinitlevel/", summary="设置水箱初始水位", description="设置指定水箱的初始水位值", response_model=None)
|
@router.patch("/tanks/init-level", summary="设置水箱初始水位", description="设置指定水箱的初始水位值", response_model=None)
|
||||||
async def fastapi_set_tank_init_level(
|
async def fastapi_set_tank_init_level(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -327,7 +327,7 @@ async def fastapi_set_tank_init_level(
|
|||||||
ps = {"id": tank, "init_level": init_level}
|
ps = {"id": tank, "init_level": init_level}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settankminlevel/", summary="设置水箱最小水位", description="设置指定水箱的最小水位值", response_model=None)
|
@router.patch("/tanks/min-level", summary="设置水箱最小水位", description="设置指定水箱的最小水位值", response_model=None)
|
||||||
async def fastapi_set_tank_min_level(
|
async def fastapi_set_tank_min_level(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -347,7 +347,7 @@ async def fastapi_set_tank_min_level(
|
|||||||
ps = {"id": tank, "min_level": min_level}
|
ps = {"id": tank, "min_level": min_level}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settankmaxlevel/", summary="设置水箱最大水位", description="设置指定水箱的最大水位值", response_model=None)
|
@router.patch("/tanks/max-level", summary="设置水箱最大水位", description="设置指定水箱的最大水位值", response_model=None)
|
||||||
async def fastapi_set_tank_max_level(
|
async def fastapi_set_tank_max_level(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -367,7 +367,7 @@ async def fastapi_set_tank_max_level(
|
|||||||
ps = {"id": tank, "max_level": max_level}
|
ps = {"id": tank, "max_level": max_level}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settankdiameter/", summary="设置水箱直径", description="设置指定水箱的直径值", response_model=None)
|
@router.patch("/tanks/diameter", summary="设置水箱直径", description="设置指定水箱的直径值", response_model=None)
|
||||||
async def fastapi_set_tank_diameter(
|
async def fastapi_set_tank_diameter(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -387,7 +387,7 @@ async def fastapi_set_tank_diameter(
|
|||||||
ps = {"id": tank, "diameter": diameter}
|
ps = {"id": tank, "diameter": diameter}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settankminvol/", summary="设置水箱最小体积", description="设置指定水箱的最小体积值", response_model=None)
|
@router.patch("/tanks/min-vol", summary="设置水箱最小体积", description="设置指定水箱的最小体积值", response_model=None)
|
||||||
async def fastapi_set_tank_min_vol(
|
async def fastapi_set_tank_min_vol(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -407,7 +407,7 @@ async def fastapi_set_tank_min_vol(
|
|||||||
ps = {"id": tank, "min_vol": min_vol}
|
ps = {"id": tank, "min_vol": min_vol}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settankvolcurve/", summary="设置水箱容积曲线", description="设置指定水箱的容积曲线标识", response_model=None)
|
@router.patch("/tanks/vol-curve", summary="设置水箱容积曲线", description="设置指定水箱的容积曲线标识", response_model=None)
|
||||||
async def fastapi_set_tank_vol_curve(
|
async def fastapi_set_tank_vol_curve(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -427,7 +427,7 @@ async def fastapi_set_tank_vol_curve(
|
|||||||
ps = {"id": tank, "vol_curve": vol_curve}
|
ps = {"id": tank, "vol_curve": vol_curve}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settankoverflow/", summary="设置水箱溢流口", description="设置指定水箱的溢流口配置", response_model=None)
|
@router.patch("/tanks/overflow", summary="设置水箱溢流口", description="设置指定水箱的溢流口配置", response_model=None)
|
||||||
async def fastapi_set_tank_overflow(
|
async def fastapi_set_tank_overflow(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -447,7 +447,7 @@ async def fastapi_set_tank_overflow(
|
|||||||
ps = {"id": tank, "overflow": overflow}
|
ps = {"id": tank, "overflow": overflow}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settankx/", summary="设置水箱X坐标", description="设置指定水箱的X坐标值", response_model=None)
|
@router.patch("/tanks/x", summary="设置水箱X坐标", description="设置指定水箱的X坐标值", response_model=None)
|
||||||
async def fastapi_set_tank_x(
|
async def fastapi_set_tank_x(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -467,7 +467,7 @@ async def fastapi_set_tank_x(
|
|||||||
ps = {"id": tank, "x": x}
|
ps = {"id": tank, "x": x}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settanky/", summary="设置水箱Y坐标", description="设置指定水箱的Y坐标值", response_model=None)
|
@router.patch("/tanks/y", summary="设置水箱Y坐标", description="设置指定水箱的Y坐标值", response_model=None)
|
||||||
async def fastapi_set_tank_y(
|
async def fastapi_set_tank_y(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -487,7 +487,7 @@ async def fastapi_set_tank_y(
|
|||||||
ps = {"id": tank, "y": y}
|
ps = {"id": tank, "y": y}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post("/settankcoord/", summary="设置水箱坐标", description="设置指定水箱的X和Y坐标", response_model=None)
|
@router.patch("/tanks/coord", summary="设置水箱坐标", description="设置指定水箱的X和Y坐标", response_model=None)
|
||||||
async def fastapi_set_tank_coord(
|
async def fastapi_set_tank_coord(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
@@ -509,7 +509,7 @@ async def fastapi_set_tank_coord(
|
|||||||
ps = {"id": tank, "x": x, "y": y}
|
ps = {"id": tank, "x": x, "y": y}
|
||||||
return set_tank(network, ChangeSet(ps))
|
return set_tank(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get("/gettankproperties/", summary="获取水箱属性", description="获取指定水箱的所有属性")
|
@router.get("/tanks/properties", summary="获取水箱属性", description="获取指定水箱的所有属性")
|
||||||
async def fastapi_get_tank_properties(
|
async def fastapi_get_tank_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID")
|
tank: str = Query(..., description="水箱ID")
|
||||||
@@ -526,7 +526,7 @@ async def fastapi_get_tank_properties(
|
|||||||
"""
|
"""
|
||||||
return get_tank(network, tank)
|
return get_tank(network, tank)
|
||||||
|
|
||||||
@router.get("/getalltankproperties/", summary="获取所有水箱属性", description="获取指定网络中所有水箱的属性")
|
@router.get("/tanks", summary="获取所有水箱属性", description="获取指定网络中所有水箱的属性")
|
||||||
async def fastapi_get_all_tank_properties(
|
async def fastapi_get_all_tank_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
@@ -544,7 +544,7 @@ async def fastapi_get_all_tank_properties(
|
|||||||
results = get_all_tanks(network)
|
results = get_all_tanks(network)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@router.post("/settankproperties/", summary="设置水箱属性", description="批量设置指定水箱的多个属性", response_model=None)
|
@router.patch("/tanks/properties", summary="设置水箱属性", description="批量设置指定水箱的多个属性", response_model=None)
|
||||||
async def fastapi_set_tank_properties(
|
async def fastapi_set_tank_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
tank: str = Query(..., description="水箱ID"),
|
tank: str = Query(..., description="水箱ID"),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from app.services.tjnetwork import (
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvalveschema",
|
"/network-schemas/valve",
|
||||||
summary="获取阀门架构",
|
summary="获取阀门架构",
|
||||||
description="获取指定水网中所有阀门的架构和字段定义",
|
description="获取指定水网中所有阀门的架构和字段定义",
|
||||||
)
|
)
|
||||||
@@ -30,7 +30,7 @@ async def fastapi_get_valve_schema(
|
|||||||
return get_valve_schema(network)
|
return get_valve_schema(network)
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/addvalve/",
|
"/valves",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="添加阀门",
|
summary="添加阀门",
|
||||||
description="在指定的水网中添加新的阀门",
|
description="在指定的水网中添加新的阀门",
|
||||||
@@ -62,8 +62,8 @@ async def fastapi_add_valve(
|
|||||||
|
|
||||||
return add_valve(network, ChangeSet(ps))
|
return add_valve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.delete(
|
||||||
"/deletevalve/",
|
"/valves",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="删除阀门",
|
summary="删除阀门",
|
||||||
description="从指定的水网中删除指定的阀门",
|
description="从指定的水网中删除指定的阀门",
|
||||||
@@ -81,7 +81,7 @@ async def fastapi_delete_valve(
|
|||||||
return delete_valve(network, ChangeSet(ps))
|
return delete_valve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvalvenode1/",
|
"/valves/node1",
|
||||||
summary="获取阀门起点节点",
|
summary="获取阀门起点节点",
|
||||||
description="获取指定阀门连接的起点节点ID",
|
description="获取指定阀门连接的起点节点ID",
|
||||||
)
|
)
|
||||||
@@ -98,7 +98,7 @@ async def fastapi_get_valve_node1(
|
|||||||
return ps["node1"]
|
return ps["node1"]
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvalvenode2/",
|
"/valves/node2",
|
||||||
summary="获取阀门终点节点",
|
summary="获取阀门终点节点",
|
||||||
description="获取指定阀门连接的终点节点ID",
|
description="获取指定阀门连接的终点节点ID",
|
||||||
)
|
)
|
||||||
@@ -115,7 +115,7 @@ async def fastapi_get_valve_node2(
|
|||||||
return ps["node2"]
|
return ps["node2"]
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvalvediameter/",
|
"/valves/diameter",
|
||||||
summary="获取阀门直径",
|
summary="获取阀门直径",
|
||||||
description="获取指定阀门的直径",
|
description="获取指定阀门的直径",
|
||||||
)
|
)
|
||||||
@@ -132,7 +132,7 @@ async def fastapi_get_valve_diameter(
|
|||||||
return ps["diameter"]
|
return ps["diameter"]
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvalvetype/",
|
"/valves/type",
|
||||||
summary="获取阀门类型",
|
summary="获取阀门类型",
|
||||||
description="获取指定阀门的类型",
|
description="获取指定阀门的类型",
|
||||||
)
|
)
|
||||||
@@ -149,7 +149,7 @@ async def fastapi_get_valve_type(
|
|||||||
return ps["type"]
|
return ps["type"]
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvalvesetting/",
|
"/valves/setting",
|
||||||
summary="获取阀门开度",
|
summary="获取阀门开度",
|
||||||
description="获取指定阀门的开度/设置值",
|
description="获取指定阀门的开度/设置值",
|
||||||
)
|
)
|
||||||
@@ -166,7 +166,7 @@ async def fastapi_get_valve_setting(
|
|||||||
return ps["setting"]
|
return ps["setting"]
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvalveminorloss/",
|
"/valves/minor-loss",
|
||||||
summary="获取阀门损失系数",
|
summary="获取阀门损失系数",
|
||||||
description="获取指定阀门的损失系数",
|
description="获取指定阀门的损失系数",
|
||||||
)
|
)
|
||||||
@@ -182,8 +182,8 @@ async def fastapi_get_valve_minor_loss(
|
|||||||
ps = get_valve(network, valve)
|
ps = get_valve(network, valve)
|
||||||
return ps["minor_loss"]
|
return ps["minor_loss"]
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setvalvenode1/",
|
"/valves/node1",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置阀门起点节点",
|
summary="设置阀门起点节点",
|
||||||
description="设置指定阀门的起点节点",
|
description="设置指定阀门的起点节点",
|
||||||
@@ -201,8 +201,8 @@ async def fastapi_set_valve_node1(
|
|||||||
ps = {"id": valve, "node1": node1}
|
ps = {"id": valve, "node1": node1}
|
||||||
return set_valve(network, ChangeSet(ps))
|
return set_valve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setvalvenode2/",
|
"/valves/node2",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置阀门终点节点",
|
summary="设置阀门终点节点",
|
||||||
description="设置指定阀门的终点节点",
|
description="设置指定阀门的终点节点",
|
||||||
@@ -220,8 +220,8 @@ async def fastapi_set_valve_node2(
|
|||||||
ps = {"id": valve, "node2": node2}
|
ps = {"id": valve, "node2": node2}
|
||||||
return set_valve(network, ChangeSet(ps))
|
return set_valve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setvalvenodediameter/",
|
"/valves/diameter",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置阀门直径",
|
summary="设置阀门直径",
|
||||||
description="设置指定阀门的直径",
|
description="设置指定阀门的直径",
|
||||||
@@ -239,8 +239,8 @@ async def fastapi_set_valve_diameter(
|
|||||||
ps = {"id": valve, "diameter": diameter}
|
ps = {"id": valve, "diameter": diameter}
|
||||||
return set_valve(network, ChangeSet(ps))
|
return set_valve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setvalvetype/",
|
"/valves/type",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置阀门类型",
|
summary="设置阀门类型",
|
||||||
description="设置指定阀门的类型",
|
description="设置指定阀门的类型",
|
||||||
@@ -258,8 +258,8 @@ async def fastapi_set_valve_type(
|
|||||||
ps = {"id": valve, "type": type}
|
ps = {"id": valve, "type": type}
|
||||||
return set_valve(network, ChangeSet(ps))
|
return set_valve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setvalvesetting/",
|
"/valves/setting",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="设置阀门开度",
|
summary="设置阀门开度",
|
||||||
description="设置指定阀门的开度/设置值",
|
description="设置指定阀门的开度/设置值",
|
||||||
@@ -278,7 +278,7 @@ async def fastapi_set_valve_setting(
|
|||||||
return set_valve(network, ChangeSet(ps))
|
return set_valve(network, ChangeSet(ps))
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getvalveproperties/",
|
"/valves/properties",
|
||||||
summary="获取阀门所有属性",
|
summary="获取阀门所有属性",
|
||||||
description="获取指定阀门的所有属性",
|
description="获取指定阀门的所有属性",
|
||||||
)
|
)
|
||||||
@@ -294,7 +294,7 @@ async def fastapi_get_valve_properties(
|
|||||||
return get_valve(network, valve)
|
return get_valve(network, valve)
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getallvalveproperties/",
|
"/valves",
|
||||||
summary="获取所有阀门属性",
|
summary="获取所有阀门属性",
|
||||||
description="获取指定水网中所有阀门的属性",
|
description="获取指定水网中所有阀门的属性",
|
||||||
)
|
)
|
||||||
@@ -311,8 +311,8 @@ async def fastapi_get_all_valve_properties(
|
|||||||
results = get_all_valves(network)
|
results = get_all_valves(network)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@router.post(
|
@router.patch(
|
||||||
"/setvalveproperties/",
|
"/valves/properties",
|
||||||
response_model=None,
|
response_model=None,
|
||||||
summary="批量设置阀门属性",
|
summary="批量设置阀门属性",
|
||||||
description="批量设置指定阀门的多个属性",
|
description="批量设置指定阀门的多个属性",
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ inpDir = "data/" # Assuming data directory exists or is defined somewhere.
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
lockedPrjs: Dict[str, str] = {}
|
lockedPrjs: Dict[str, str] = {}
|
||||||
|
|
||||||
@router.get("/project-info", summary="获取项目信息", description="从数据库获取项目的详细信息,包括地图范围等。", response_model=ProjectMetaResponse)
|
@router.get("/projects/current", summary="获取项目信息", description="从数据库获取项目的详细信息,包括地图范围等。", response_model=ProjectMetaResponse)
|
||||||
@router.get("/project_info/", summary="获取项目信息(旧路径)", description="从数据库获取项目的详细信息,包括地图范围等。", response_model=ProjectMetaResponse, deprecated=True)
|
|
||||||
async def get_project_info_endpoint(
|
async def get_project_info_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或项目代码)"),
|
network: str = Query(..., description="管网名称(或项目代码)"),
|
||||||
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
|
||||||
@@ -70,7 +69,7 @@ async def get_project_info_endpoint(
|
|||||||
project_role="viewer", # Default role for public access
|
project_role="viewer", # Default role for public access
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.get("/listprojects/", summary="获取项目列表", description="获取服务器上所有可用的供水管网项目名称列表。")
|
@router.get("/project-codes", summary="获取项目列表", description="获取服务器上所有可用的供水管网项目名称列表。")
|
||||||
async def list_projects_endpoint() -> list[str]:
|
async def list_projects_endpoint() -> list[str]:
|
||||||
"""
|
"""
|
||||||
获取项目列表
|
获取项目列表
|
||||||
@@ -79,7 +78,7 @@ async def list_projects_endpoint() -> list[str]:
|
|||||||
"""
|
"""
|
||||||
return list_project()
|
return list_project()
|
||||||
|
|
||||||
@router.get("/haveproject/", summary="检查项目是否存在", description="检查指定名称的项目是否存在。")
|
@router.get("/projects/existence", summary="检查项目是否存在", description="检查指定名称的项目是否存在。")
|
||||||
async def have_project_endpoint(
|
async def have_project_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
):
|
):
|
||||||
@@ -90,7 +89,7 @@ async def have_project_endpoint(
|
|||||||
"""
|
"""
|
||||||
return have_project(network)
|
return have_project(network)
|
||||||
|
|
||||||
@router.post("/createproject/", summary="创建新项目", description="创建一个新的供水管网项目。如果项目已存在,可能会覆盖或报错(取决于底层实现)。")
|
@router.post("/projects", summary="创建新项目", description="创建一个新的供水管网项目。如果项目已存在,可能会覆盖或报错(取决于底层实现)。")
|
||||||
async def create_project_endpoint(
|
async def create_project_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
_=Depends(require_permission(ENVIRONMENT_MANAGE)),
|
_=Depends(require_permission(ENVIRONMENT_MANAGE)),
|
||||||
@@ -103,7 +102,7 @@ async def create_project_endpoint(
|
|||||||
create_project(network)
|
create_project(network)
|
||||||
return network
|
return network
|
||||||
|
|
||||||
@router.post("/deleteproject/", summary="删除项目", description="永久删除指定的供水管网项目。此操作不可恢复。")
|
@router.delete("/projects", summary="删除项目", description="永久删除指定的供水管网项目。此操作不可恢复。")
|
||||||
async def delete_project_endpoint(
|
async def delete_project_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
_=Depends(require_permission(ENVIRONMENT_MANAGE)),
|
_=Depends(require_permission(ENVIRONMENT_MANAGE)),
|
||||||
@@ -116,7 +115,7 @@ async def delete_project_endpoint(
|
|||||||
delete_project(network)
|
delete_project(network)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@router.get("/isprojectopen/", summary="检查项目是否已打开", description="检查指定项目是否已被加载到内存中。")
|
@router.get("/projects/current/status", summary="检查项目是否已打开", description="检查指定项目是否已被加载到内存中。")
|
||||||
async def is_project_open_endpoint(
|
async def is_project_open_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
):
|
):
|
||||||
@@ -127,8 +126,7 @@ async def is_project_open_endpoint(
|
|||||||
"""
|
"""
|
||||||
return is_project_open(network)
|
return is_project_open(network)
|
||||||
|
|
||||||
@router.post("/projects/open", summary="打开项目", description="将指定项目加载到内存中,并初始化数据库连接池。")
|
@router.post("/projects/current", summary="打开项目", description="将指定项目加载到内存中,并初始化数据库连接池。")
|
||||||
@router.post("/openproject/", summary="打开项目(旧路径)", description="将指定项目加载到内存中,并初始化数据库连接池。", deprecated=True)
|
|
||||||
async def open_project_endpoint(
|
async def open_project_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
):
|
):
|
||||||
@@ -162,7 +160,7 @@ async def open_project_endpoint(
|
|||||||
|
|
||||||
return network
|
return network
|
||||||
|
|
||||||
@router.post("/closeproject/", summary="关闭项目", description="将指定项目从内存中卸载,释放资源。")
|
@router.delete("/projects/current", summary="关闭项目", description="将指定项目从内存中卸载,释放资源。")
|
||||||
async def close_project_endpoint(
|
async def close_project_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
):
|
):
|
||||||
@@ -174,7 +172,7 @@ async def close_project_endpoint(
|
|||||||
close_project(network)
|
close_project(network)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@router.post("/copyproject/", summary="复制项目", description="将现有项目复制为新项目。")
|
@router.post("/project-copies", summary="复制项目", description="将现有项目复制为新项目。")
|
||||||
async def copy_project_endpoint(
|
async def copy_project_endpoint(
|
||||||
source: str = Query(..., description="管网名称(或数据库名称)"),
|
source: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
target: str = Query(..., description="管网名称(或数据库名称)"),
|
target: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
@@ -189,7 +187,7 @@ async def copy_project_endpoint(
|
|||||||
copy_project(source, target)
|
copy_project(source, target)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@router.get("/exportinp/", response_model=None, summary="导出项目为 ChangeSet", description="导出项目的变更集 (ChangeSet),包含顶点、SCADA 元素、DMA、SA、VD 等信息。")
|
@router.get("/projects/current/exports/change-set", response_model=None, summary="导出项目为 ChangeSet", description="导出项目的变更集 (ChangeSet),包含顶点、SCADA 元素、DMA、SA、VD 等信息。")
|
||||||
async def export_inp_endpoint(
|
async def export_inp_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
version: str = Query(..., description="版本号 (通常用于增量更新)")
|
version: str = Query(..., description="版本号 (通常用于增量更新)")
|
||||||
@@ -222,7 +220,7 @@ async def export_inp_endpoint(
|
|||||||
|
|
||||||
return cs
|
return cs
|
||||||
|
|
||||||
@router.post("/readinp/", summary="读取 INP 文件到项目", description="从服务器文件系统中读取指定的 INP 文件并加载到项目中。")
|
@router.post("/projects/current/imports", summary="读取 INP 文件到项目", description="从服务器文件系统中读取指定的 INP 文件并加载到项目中。")
|
||||||
async def read_inp_endpoint(
|
async def read_inp_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
inp: str = Query(..., description="INP 文件名 (不包含路径)")
|
inp: str = Query(..., description="INP 文件名 (不包含路径)")
|
||||||
@@ -236,7 +234,7 @@ async def read_inp_endpoint(
|
|||||||
read_inp(network, inp)
|
read_inp(network, inp)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@router.get("/dumpinp/", summary="导出项目到 INP 文件", description="将项目当前状态保存为 INP 文件到服务器文件系统。")
|
@router.post("/projects/current/exports/inp", summary="导出项目到 INP 文件", description="将项目当前状态保存为 INP 文件到服务器文件系统。")
|
||||||
async def dump_inp_endpoint(
|
async def dump_inp_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
inp: str = Query(..., description="目标文件名")
|
inp: str = Query(..., description="目标文件名")
|
||||||
@@ -250,7 +248,7 @@ async def dump_inp_endpoint(
|
|||||||
dump_inp(network, inp)
|
dump_inp(network, inp)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@router.get("/isprojectlocked/", summary="检查项目是否被锁定", description="检查指定项目是否处于锁定状态。")
|
@router.get("/projects/current/lock", summary="检查项目是否被锁定", description="检查指定项目是否处于锁定状态。")
|
||||||
async def is_project_locked_endpoint(
|
async def is_project_locked_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -262,7 +260,7 @@ async def is_project_locked_endpoint(
|
|||||||
"""
|
"""
|
||||||
return network in lockedPrjs.keys()
|
return network in lockedPrjs.keys()
|
||||||
|
|
||||||
@router.get("/isprojectlockedbyme/", summary="检查项目是否被当前用户锁定", description="检查指定项目是否被当前客户端 (IP) 锁定。")
|
@router.get("/projects/current/lock/ownership", summary="检查项目是否被当前用户锁定", description="检查指定项目是否被当前客户端 (IP) 锁定。")
|
||||||
async def is_project_locked_by_me_endpoint(
|
async def is_project_locked_by_me_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -278,7 +276,7 @@ async def is_project_locked_by_me_endpoint(
|
|||||||
# 0 successfully locked
|
# 0 successfully locked
|
||||||
# 1 already locked by you
|
# 1 already locked by you
|
||||||
# 2 locked by others
|
# 2 locked by others
|
||||||
@router.post("/lockproject/", summary="锁定项目", description="锁定指定项目以防止并发修改。")
|
@router.post("/projects/current/lock", summary="锁定项目", description="锁定指定项目以防止并发修改。")
|
||||||
async def lock_project_endpoint(
|
async def lock_project_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -301,7 +299,7 @@ async def lock_project_endpoint(
|
|||||||
else:
|
else:
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
@router.post("/unlockproject/", summary="解锁项目", description="释放对项目的锁定。")
|
@router.delete("/projects/current/lock", summary="解锁项目", description="释放对项目的锁定。")
|
||||||
def unlock_project_endpoint(
|
def unlock_project_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -319,7 +317,7 @@ def unlock_project_endpoint(
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@router.get("/downloadinp/", status_code=status.HTTP_200_OK, summary="下载 INP 文件", description="从服务器数据目录下载指定的 INP 文件。")
|
@router.get("/projects/current/files/inp", status_code=status.HTTP_200_OK, summary="下载 INP 文件", description="从服务器数据目录下载指定的 INP 文件。")
|
||||||
async def fastapi_download_inp(
|
async def fastapi_download_inp(
|
||||||
name: str = Query(..., description="文件名"),
|
name: str = Query(..., description="文件名"),
|
||||||
response: Response = None
|
response: Response = None
|
||||||
@@ -339,7 +337,7 @@ async def fastapi_download_inp(
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# DingZQ, 2024-12-28, convert v3 to v2
|
# DingZQ, 2024-12-28, convert v3 to v2
|
||||||
@router.get("/convertv3tov2/", response_model=None, summary="转换 INP V3 为 V2", description="将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。")
|
@router.post("/project-conversions", response_model=None, summary="转换 INP V3 为 V2", description="将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。")
|
||||||
async def fastapi_convert_v3_to_v2(
|
async def fastapi_convert_v3_to_v2(
|
||||||
req: Request
|
req: Request
|
||||||
) -> ChangeSet:
|
) -> ChangeSet:
|
||||||
@@ -373,7 +371,6 @@ async def fastapi_convert_v3_to_v2(
|
|||||||
|
|
||||||
return cs
|
return cs
|
||||||
|
|
||||||
@router.post("/readinp/", summary="读取 INP 文件到项目", description="从服务器文件系统中读取指定的 INP 文件并加载到项目中。")
|
|
||||||
async def read_inp_endpoint(
|
async def read_inp_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
inp: str = Query(..., description="INP 文件名 (不包含路径)")
|
inp: str = Query(..., description="INP 文件名 (不包含路径)")
|
||||||
@@ -387,7 +384,6 @@ async def read_inp_endpoint(
|
|||||||
read_inp(network, inp)
|
read_inp(network, inp)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@router.get("/dumpinp/", summary="导出项目到 INP 文件", description="将项目当前状态保存为 INP 文件到服务器文件系统。")
|
|
||||||
async def dump_inp_endpoint(
|
async def dump_inp_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
inp: str = Query(..., description="目标文件名")
|
inp: str = Query(..., description="目标文件名")
|
||||||
@@ -401,7 +397,6 @@ async def dump_inp_endpoint(
|
|||||||
dump_inp(network, inp)
|
dump_inp(network, inp)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@router.get("/isprojectlocked/", summary="检查项目是否被锁定", description="检查指定项目是否处于锁定状态。")
|
|
||||||
async def is_project_locked_endpoint(
|
async def is_project_locked_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -413,7 +408,6 @@ async def is_project_locked_endpoint(
|
|||||||
"""
|
"""
|
||||||
return network in lockedPrjs.keys()
|
return network in lockedPrjs.keys()
|
||||||
|
|
||||||
@router.get("/isprojectlockedbyme/", summary="检查项目是否被当前用户锁定", description="检查指定项目是否被当前客户端 (IP) 锁定。")
|
|
||||||
async def is_project_locked_by_me_endpoint(
|
async def is_project_locked_by_me_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -429,7 +423,6 @@ async def is_project_locked_by_me_endpoint(
|
|||||||
# 0 successfully locked
|
# 0 successfully locked
|
||||||
# 1 already locked by you
|
# 1 already locked by you
|
||||||
# 2 locked by others
|
# 2 locked by others
|
||||||
@router.post("/lockproject/", summary="锁定项目", description="锁定指定项目以防止并发修改。")
|
|
||||||
async def lock_project_endpoint(
|
async def lock_project_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -452,7 +445,6 @@ async def lock_project_endpoint(
|
|||||||
else:
|
else:
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
@router.post("/unlockproject/", summary="解锁项目", description="释放对项目的锁定。")
|
|
||||||
def unlock_project_endpoint(
|
def unlock_project_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -470,7 +462,6 @@ def unlock_project_endpoint(
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@router.get("/downloadinp/", status_code=status.HTTP_200_OK, summary="下载 INP 文件", description="从服务器数据目录下载指定的 INP 文件。")
|
|
||||||
async def fastapi_download_inp(
|
async def fastapi_download_inp(
|
||||||
name: str = Query(..., description="文件名"),
|
name: str = Query(..., description="文件名"),
|
||||||
response: Response = None
|
response: Response = None
|
||||||
@@ -490,7 +481,6 @@ async def fastapi_download_inp(
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# DingZQ, 2024-12-28, convert v3 to v2
|
# DingZQ, 2024-12-28, convert v3 to v2
|
||||||
@router.get("/convertv3tov2/", response_model=None, summary="转换 INP V3 为 V2", description="将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。")
|
|
||||||
async def fastapi_convert_v3_to_v2(
|
async def fastapi_convert_v3_to_v2(
|
||||||
req: Request
|
req: Request
|
||||||
) -> ChangeSet:
|
) -> ChangeSet:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ async def get_database_connection(
|
|||||||
yield conn
|
yield conn
|
||||||
|
|
||||||
|
|
||||||
@router.get("/scada-info", summary="获取SCADA信息", description="使用连接池查询所有SCADA信息")
|
@router.get("/scada-info/database-view", summary="获取SCADA信息", description="使用连接池查询所有SCADA信息")
|
||||||
async def get_scada_info_with_connection(
|
async def get_scada_info_with_connection(
|
||||||
conn: AsyncConnection = Depends(get_database_connection),
|
conn: AsyncConnection = Depends(get_database_connection),
|
||||||
):
|
):
|
||||||
@@ -33,7 +33,7 @@ async def get_scada_info_with_connection(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/scheme-list", summary="获取方案列表", description="使用连接池查询所有方案信息")
|
@router.get("/schemes/list-with-connection", summary="获取方案列表", description="使用连接池查询所有方案信息")
|
||||||
async def get_scheme_list_with_connection(
|
async def get_scheme_list_with_connection(
|
||||||
conn: AsyncConnection = Depends(get_database_connection),
|
conn: AsyncConnection = Depends(get_database_connection),
|
||||||
):
|
):
|
||||||
@@ -49,7 +49,7 @@ async def get_scheme_list_with_connection(
|
|||||||
raise HTTPException(status_code=500, detail=f"查询方案信息时发生错误: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"查询方案信息时发生错误: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/burst-locate-result", summary="获取爆管定位结果", description="使用连接池查询所有爆管定位结果")
|
@router.get("/burst-locations/database-view", summary="获取爆管定位结果", description="使用连接池查询所有爆管定位结果")
|
||||||
async def get_burst_locate_result_with_connection(
|
async def get_burst_locate_result_with_connection(
|
||||||
conn: AsyncConnection = Depends(get_database_connection),
|
conn: AsyncConnection = Depends(get_database_connection),
|
||||||
):
|
):
|
||||||
@@ -67,7 +67,7 @@ async def get_burst_locate_result_with_connection(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/burst-locate-result/{burst_incident}", summary="按事件查询爆管定位结果", description="根据爆管事件ID查询对应的爆管定位结果")
|
@router.get("/burst-locations/{burst_incident}", summary="按事件查询爆管定位结果", description="根据爆管事件ID查询对应的爆管定位结果")
|
||||||
async def get_burst_locate_result_by_incident(
|
async def get_burst_locate_result_by_incident(
|
||||||
burst_incident: str = Path(..., description="爆管事件ID"),
|
burst_incident: str = Path(..., description="爆管事件ID"),
|
||||||
conn: AsyncConnection = Depends(get_database_connection),
|
conn: AsyncConnection = Depends(get_database_connection),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from app.services.tjnetwork import (
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getpiperiskprobabilitynow/",
|
"/pipes/risk-probability-now",
|
||||||
summary="获取管道当前风险概率",
|
summary="获取管道当前风险概率",
|
||||||
description="获取指定管道当前时刻的风险概率值"
|
description="获取指定管道当前时刻的风险概率值"
|
||||||
)
|
)
|
||||||
@@ -35,7 +35,7 @@ async def fastapi_get_pipe_risk_probability_now(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getpiperiskprobability/",
|
"/pipes/risk-probability",
|
||||||
summary="获取管道风险概率历史",
|
summary="获取管道风险概率历史",
|
||||||
description="获取指定管道的风险概率历史数据"
|
description="获取指定管道的风险概率历史数据"
|
||||||
)
|
)
|
||||||
@@ -59,7 +59,7 @@ async def fastapi_get_pipe_risk_probability(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getpipesriskprobability/",
|
"/pipes-risk-probabilities",
|
||||||
summary="批量获取多条管道风险概率",
|
summary="批量获取多条管道风险概率",
|
||||||
description="批量获取多条管道的风险概率值"
|
description="批量获取多条管道的风险概率值"
|
||||||
)
|
)
|
||||||
@@ -84,7 +84,7 @@ async def fastapi_get_pipes_risk_probability(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getnetworkpiperiskprobabilitynow/",
|
"/network-pipe-risk-probability-nows",
|
||||||
summary="获取整个网络的管道风险概率",
|
summary="获取整个网络的管道风险概率",
|
||||||
description="获取指定网络中所有管道的当前风险概率值"
|
description="获取指定网络中所有管道的当前风险概率值"
|
||||||
)
|
)
|
||||||
@@ -106,7 +106,7 @@ async def fastapi_get_network_pipe_risk_probability_now(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/getpiperiskprobabilitygeometries/",
|
"/pipes/risk-probability-geometries",
|
||||||
summary="获取管道风险几何信息",
|
summary="获取管道风险几何信息",
|
||||||
description="获取指定网络中管道的风险相关几何数据"
|
description="获取指定网络中管道的风险相关几何数据"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getscadaproperties/", summary="获取SCADA属性", tags=["SCADA基础"])
|
|
||||||
async def fast_get_scada_properties(
|
async def fast_get_scada_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
scada: str = Query(..., description="SCADA设备ID")
|
scada: str = Query(..., description="SCADA设备ID")
|
||||||
@@ -50,7 +49,6 @@ async def fast_get_scada_properties(
|
|||||||
"""
|
"""
|
||||||
return get_scada_info(network, scada)
|
return get_scada_info(network, scada)
|
||||||
|
|
||||||
@router.get("/getallscadaproperties/", summary="获取所有SCADA属性", tags=["SCADA基础"])
|
|
||||||
async def fast_get_all_scada_properties(
|
async def fast_get_all_scada_properties(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
@@ -72,7 +70,7 @@ async def fast_get_all_scada_properties(
|
|||||||
# scada_device 设备管理
|
# scada_device 设备管理
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get("/getscadadeviceschema/", summary="获取SCADA设备架构", tags=["SCADA设备"])
|
@router.get("/network-schemas/scada-device", summary="获取SCADA设备架构", tags=["SCADA设备"])
|
||||||
async def fastapi_get_scada_device_schema(
|
async def fastapi_get_scada_device_schema(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> dict[str, dict[str, Any]]:
|
) -> dict[str, dict[str, Any]]:
|
||||||
@@ -89,7 +87,7 @@ async def fastapi_get_scada_device_schema(
|
|||||||
"""
|
"""
|
||||||
return get_scada_device_schema(network)
|
return get_scada_device_schema(network)
|
||||||
|
|
||||||
@router.get("/getscadadevice/", summary="获取SCADA设备", tags=["SCADA设备"])
|
@router.get("/scada-devices/detail", summary="获取SCADA设备", tags=["SCADA设备"])
|
||||||
async def fastapi_get_scada_device(
|
async def fastapi_get_scada_device(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
id: str = Query(..., description="SCADA设备ID")
|
id: str = Query(..., description="SCADA设备ID")
|
||||||
@@ -108,7 +106,7 @@ async def fastapi_get_scada_device(
|
|||||||
"""
|
"""
|
||||||
return get_scada_device(network, id)
|
return get_scada_device(network, id)
|
||||||
|
|
||||||
@router.post("/setscadadevice/", response_model=None, summary="更新SCADA设备", tags=["SCADA设备"])
|
@router.patch("/scada-devices", response_model=None, summary="更新SCADA设备", tags=["SCADA设备"])
|
||||||
async def fastapi_set_scada_device(
|
async def fastapi_set_scada_device(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -128,7 +126,7 @@ async def fastapi_set_scada_device(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_scada_device(network, ChangeSet(props))
|
return set_scada_device(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/addscadadevice/", response_model=None, summary="添加SCADA设备", tags=["SCADA设备"])
|
@router.post("/scada-devices", response_model=None, summary="添加SCADA设备", tags=["SCADA设备"])
|
||||||
async def fastapi_add_scada_device(
|
async def fastapi_add_scada_device(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -148,7 +146,7 @@ async def fastapi_add_scada_device(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_scada_device(network, ChangeSet(props))
|
return add_scada_device(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/deletescadadevice/", response_model=None, summary="删除SCADA设备", tags=["SCADA设备"])
|
@router.delete("/scada-devices", response_model=None, summary="删除SCADA设备", tags=["SCADA设备"])
|
||||||
async def fastapi_delete_scada_device(
|
async def fastapi_delete_scada_device(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -168,7 +166,7 @@ async def fastapi_delete_scada_device(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return delete_scada_device(network, ChangeSet(props))
|
return delete_scada_device(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/cleanscadadevice/", response_model=None, summary="清空SCADA设备表", tags=["SCADA设备"])
|
@router.post("/scada-device-cleaning-runs", response_model=None, summary="清空SCADA设备表", tags=["SCADA设备"])
|
||||||
async def fastapi_clean_scada_device(
|
async def fastapi_clean_scada_device(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> ChangeSet:
|
) -> ChangeSet:
|
||||||
@@ -185,7 +183,7 @@ async def fastapi_clean_scada_device(
|
|||||||
"""
|
"""
|
||||||
return clean_scada_device(network)
|
return clean_scada_device(network)
|
||||||
|
|
||||||
@router.get("/getallscadadeviceids/", summary="获取所有SCADA设备ID", tags=["SCADA设备"])
|
@router.get("/scada-devices/ids", summary="获取所有SCADA设备ID", tags=["SCADA设备"])
|
||||||
async def fastapi_get_all_scada_device_ids(
|
async def fastapi_get_all_scada_device_ids(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
@@ -200,7 +198,7 @@ async def fastapi_get_all_scada_device_ids(
|
|||||||
"""
|
"""
|
||||||
return get_all_scada_device_ids(network)
|
return get_all_scada_device_ids(network)
|
||||||
|
|
||||||
@router.get("/getallscadadevices/", summary="获取所有SCADA设备", tags=["SCADA设备"])
|
@router.get("/scada-devices", summary="获取所有SCADA设备", tags=["SCADA设备"])
|
||||||
async def fastapi_get_all_scada_devices(
|
async def fastapi_get_all_scada_devices(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
@@ -220,7 +218,7 @@ async def fastapi_get_all_scada_devices(
|
|||||||
# scada_device_data 设备数据管理
|
# scada_device_data 设备数据管理
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get("/getscadadevicedataschema/", summary="获取SCADA设备数据架构", tags=["SCADA设备数据"])
|
@router.get("/network-schemas/scada-device-data", summary="获取SCADA设备数据架构", tags=["SCADA设备数据"])
|
||||||
async def fastapi_get_scada_device_data_schema(
|
async def fastapi_get_scada_device_data_schema(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
) -> dict[str, dict[str, Any]]:
|
) -> dict[str, dict[str, Any]]:
|
||||||
@@ -237,7 +235,7 @@ async def fastapi_get_scada_device_data_schema(
|
|||||||
"""
|
"""
|
||||||
return get_scada_device_data_schema(network)
|
return get_scada_device_data_schema(network)
|
||||||
|
|
||||||
@router.get("/getscadadevicedata/", summary="获取SCADA设备数据", tags=["SCADA设备数据"])
|
@router.get("/scada-device-datas/detail", summary="获取SCADA设备数据", tags=["SCADA设备数据"])
|
||||||
async def fastapi_get_scada_device_data(
|
async def fastapi_get_scada_device_data(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
device_id: str = Query(..., description="SCADA设备ID")
|
device_id: str = Query(..., description="SCADA设备ID")
|
||||||
@@ -256,7 +254,7 @@ async def fastapi_get_scada_device_data(
|
|||||||
"""
|
"""
|
||||||
return get_scada_device_data(network, device_id)
|
return get_scada_device_data(network, device_id)
|
||||||
|
|
||||||
@router.post("/setscadadevicedata/", response_model=None, summary="更新SCADA设备数据", tags=["SCADA设备数据"])
|
@router.patch("/scada-device-datas", response_model=None, summary="更新SCADA设备数据", tags=["SCADA设备数据"])
|
||||||
async def fastapi_set_scada_device_data(
|
async def fastapi_set_scada_device_data(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -276,7 +274,7 @@ async def fastapi_set_scada_device_data(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_scada_device_data(network, ChangeSet(props))
|
return set_scada_device_data(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/addscadadevicedata/", response_model=None, summary="添加SCADA设备数据", tags=["SCADA设备数据"])
|
@router.post("/scada-device-datas", response_model=None, summary="添加SCADA设备数据", tags=["SCADA设备数据"])
|
||||||
async def fastapi_add_scada_device_data(
|
async def fastapi_add_scada_device_data(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -296,7 +294,7 @@ async def fastapi_add_scada_device_data(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_scada_device_data(network, ChangeSet(props))
|
return add_scada_device_data(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/deletescadadevicedata/", response_model=None, summary="删除SCADA设备数据", tags=["SCADA设备数据"])
|
@router.delete("/scada-device-datas", response_model=None, summary="删除SCADA设备数据", tags=["SCADA设备数据"])
|
||||||
async def fastapi_delete_scada_device_data(
|
async def fastapi_delete_scada_device_data(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -316,7 +314,7 @@ async def fastapi_delete_scada_device_data(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return delete_scada_device_data(network, ChangeSet(props))
|
return delete_scada_device_data(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/cleanscadadevicedata/", response_model=None, summary="清空SCADA设备数据表", tags=["SCADA设备数据"])
|
@router.post("/scada-device-data-cleaning-runs", response_model=None, summary="清空SCADA设备数据表", tags=["SCADA设备数据"])
|
||||||
async def fastapi_clean_scada_device_data(
|
async def fastapi_clean_scada_device_data(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> ChangeSet:
|
) -> ChangeSet:
|
||||||
@@ -338,7 +336,7 @@ async def fastapi_clean_scada_device_data(
|
|||||||
# scada_element SCADA元素映射
|
# scada_element SCADA元素映射
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get("/getscadaelementschema/", summary="获取SCADA元素架构", tags=["SCADA元素映射"])
|
@router.get("/network-schemas/scada-element", summary="获取SCADA元素架构", tags=["SCADA元素映射"])
|
||||||
async def fastapi_get_scada_element_schema(
|
async def fastapi_get_scada_element_schema(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
) -> dict[str, dict[str, Any]]:
|
) -> dict[str, dict[str, Any]]:
|
||||||
@@ -355,7 +353,7 @@ async def fastapi_get_scada_element_schema(
|
|||||||
"""
|
"""
|
||||||
return get_scada_element_schema(network)
|
return get_scada_element_schema(network)
|
||||||
|
|
||||||
@router.get("/getscadaelements/", summary="获取所有SCADA元素映射", tags=["SCADA元素映射"])
|
@router.get("/scada-elements", summary="获取所有SCADA元素映射", tags=["SCADA元素映射"])
|
||||||
async def fastapi_get_scada_elements(
|
async def fastapi_get_scada_elements(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
@@ -372,7 +370,7 @@ async def fastapi_get_scada_elements(
|
|||||||
"""
|
"""
|
||||||
return get_all_scada_elements(network)
|
return get_all_scada_elements(network)
|
||||||
|
|
||||||
@router.get("/getscadaelement/", summary="获取单个SCADA元素映射", tags=["SCADA元素映射"])
|
@router.get("/scada-elements/detail", summary="获取单个SCADA元素映射", tags=["SCADA元素映射"])
|
||||||
async def fastapi_get_scada_element(
|
async def fastapi_get_scada_element(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
id: str = Query(..., description="SCADA元素映射ID")
|
id: str = Query(..., description="SCADA元素映射ID")
|
||||||
@@ -391,7 +389,7 @@ async def fastapi_get_scada_element(
|
|||||||
"""
|
"""
|
||||||
return get_scada_element(network, id)
|
return get_scada_element(network, id)
|
||||||
|
|
||||||
@router.post("/setscadaelement/", response_model=None, summary="更新SCADA元素映射", tags=["SCADA元素映射"])
|
@router.patch("/scada-elements", response_model=None, summary="更新SCADA元素映射", tags=["SCADA元素映射"])
|
||||||
async def fastapi_set_scada_element(
|
async def fastapi_set_scada_element(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -411,7 +409,7 @@ async def fastapi_set_scada_element(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return set_scada_element(network, ChangeSet(props))
|
return set_scada_element(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/addscadaelement/", response_model=None, summary="添加SCADA元素映射", tags=["SCADA元素映射"])
|
@router.post("/scada-elements", response_model=None, summary="添加SCADA元素映射", tags=["SCADA元素映射"])
|
||||||
async def fastapi_add_scada_element(
|
async def fastapi_add_scada_element(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -431,7 +429,7 @@ async def fastapi_add_scada_element(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return add_scada_element(network, ChangeSet(props))
|
return add_scada_element(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/deletescadaelement/", response_model=None, summary="删除SCADA元素映射", tags=["SCADA元素映射"])
|
@router.delete("/scada-elements", response_model=None, summary="删除SCADA元素映射", tags=["SCADA元素映射"])
|
||||||
async def fastapi_delete_scada_element(
|
async def fastapi_delete_scada_element(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -451,7 +449,7 @@ async def fastapi_delete_scada_element(
|
|||||||
props = await req.json()
|
props = await req.json()
|
||||||
return delete_scada_element(network, ChangeSet(props))
|
return delete_scada_element(network, ChangeSet(props))
|
||||||
|
|
||||||
@router.post("/cleanscadaelement/", response_model=None, summary="清空SCADA元素映射表", tags=["SCADA元素映射"])
|
@router.post("/scada-element-cleaning-runs", response_model=None, summary="清空SCADA元素映射表", tags=["SCADA元素映射"])
|
||||||
async def fastapi_clean_scada_element(
|
async def fastapi_clean_scada_element(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> ChangeSet:
|
) -> ChangeSet:
|
||||||
@@ -473,7 +471,7 @@ async def fastapi_clean_scada_element(
|
|||||||
# scada_info SCADA信息
|
# scada_info SCADA信息
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
@router.get("/getscadainfoschema/", summary="获取SCADA信息架构", tags=["SCADA信息"])
|
@router.get("/scada-info-schemas", summary="获取SCADA信息架构", tags=["SCADA信息"])
|
||||||
async def fastapi_get_scada_info_schema(
|
async def fastapi_get_scada_info_schema(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> dict[str, dict[str, Any]]:
|
) -> dict[str, dict[str, Any]]:
|
||||||
@@ -490,7 +488,7 @@ async def fastapi_get_scada_info_schema(
|
|||||||
"""
|
"""
|
||||||
return get_scada_info_schema(network)
|
return get_scada_info_schema(network)
|
||||||
|
|
||||||
@router.get("/getscadainfo/", summary="获取SCADA信息", tags=["SCADA信息"])
|
@router.get("/scada-info/detail", summary="获取SCADA信息", tags=["SCADA信息"])
|
||||||
async def fastapi_get_scada_info(
|
async def fastapi_get_scada_info(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
id: str = Query(..., description="SCADA信息ID")
|
id: str = Query(..., description="SCADA信息ID")
|
||||||
@@ -509,7 +507,7 @@ async def fastapi_get_scada_info(
|
|||||||
"""
|
"""
|
||||||
return get_scada_info(network, id)
|
return get_scada_info(network, id)
|
||||||
|
|
||||||
@router.get("/getallscadainfo/", summary="获取所有SCADA信息", tags=["SCADA信息"])
|
@router.get("/scada-info", summary="获取所有SCADA信息", tags=["SCADA信息"])
|
||||||
async def fastapi_get_all_scada_info(
|
async def fastapi_get_all_scada_info(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)")
|
network: str = Query(..., description="管网名称(或数据库名称)")
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from app.services.time_api import extract_date
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getschemeschema/", summary="获取方案模式", description="获取指定网络的方案模式定义")
|
@router.get("/network-schemas/scheme", summary="获取方案模式", description="获取指定网络的方案模式定义")
|
||||||
async def fastapi_get_scheme_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[Any, Any]]:
|
async def fastapi_get_scheme_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[Any, Any]]:
|
||||||
"""
|
"""
|
||||||
获取方案模式定义
|
获取方案模式定义
|
||||||
@@ -16,7 +16,7 @@ async def fastapi_get_scheme_schema(network: str = Query(..., description="管
|
|||||||
"""
|
"""
|
||||||
return get_scheme_schema(network)
|
return get_scheme_schema(network)
|
||||||
|
|
||||||
@router.get("/getscheme/", summary="获取单个方案", description="根据名称获取指定的方案信息")
|
@router.get("/schemes/detail", summary="获取单个方案", description="根据名称获取指定的方案信息")
|
||||||
async def fastapi_get_scheme(network: str = Query(..., description="管网名称(或数据库名称)"), schema_name: str = Query(..., description="方案名称")) -> dict[Any, Any]:
|
async def fastapi_get_scheme(network: str = Query(..., description="管网名称(或数据库名称)"), schema_name: str = Query(..., description="方案名称")) -> dict[Any, Any]:
|
||||||
"""
|
"""
|
||||||
获取单个方案详情
|
获取单个方案详情
|
||||||
@@ -26,7 +26,6 @@ async def fastapi_get_scheme(network: str = Query(..., description="管网名称
|
|||||||
return get_scheme(network, schema_name)
|
return get_scheme(network, schema_name)
|
||||||
|
|
||||||
@router.get("/schemes", summary="获取所有方案", description="获取指定网络的所有方案信息")
|
@router.get("/schemes", summary="获取所有方案", description="获取指定网络的所有方案信息")
|
||||||
@router.get("/getallschemes/", summary="获取所有方案(旧路径)", description="获取指定网络的所有方案信息", deprecated=True)
|
|
||||||
async def fastapi_get_all_schemes(
|
async def fastapi_get_all_schemes(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
scheme_type: str | None = Query(None, description="方案类型;为空时返回全部类型"),
|
scheme_type: str | None = Query(None, description="方案类型;为空时返回全部类型"),
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ def _get_scheme_response(
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/sensor-placement-schemes/optimize",
|
"/sensor-placement-optimization-runs",
|
||||||
response_model=SensorPlacementSchemeResponse,
|
response_model=SensorPlacementSchemeResponse,
|
||||||
summary="创建并返回监测点优化方案",
|
summary="创建并返回监测点优化方案",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ def run_simulation_manually_by_date(
|
|||||||
|
|
||||||
|
|
||||||
# 必须用这个PlainTextResponse,不然每个key都有引号
|
# 必须用这个PlainTextResponse,不然每个key都有引号
|
||||||
@router.get("/runproject/", response_class=PlainTextResponse, summary="运行项目模拟", description="基于指定的管网项目运行标准水力模拟,返回纯文本格式的模拟报告。")
|
@router.post("/project-runs", response_class=PlainTextResponse, summary="运行项目模拟", description="基于指定的管网项目运行标准水力模拟,返回纯文本格式的模拟报告。")
|
||||||
async def run_project_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> str:
|
async def run_project_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> str:
|
||||||
"""
|
"""
|
||||||
运行项目模拟
|
运行项目模拟
|
||||||
@@ -155,7 +155,7 @@ async def run_project_endpoint(network: str = Query(..., description="管网名
|
|||||||
# output 和 report
|
# output 和 report
|
||||||
# output 是 json
|
# output 是 json
|
||||||
# report 是 text
|
# report 是 text
|
||||||
@router.get("/runprojectreturndict/", summary="运行项目模拟(返回字典)", description="基于指定的管网项目运行标准水力模拟,返回JSON格式的字典,包含输出数据和报告文本。")
|
@router.post("/project-return-dict-runs", summary="运行项目模拟(返回字典)", description="基于指定的管网项目运行标准水力模拟,返回JSON格式的字典,包含输出数据和报告文本。")
|
||||||
async def run_project_return_dict_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
async def run_project_return_dict_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
运行项目模拟(返回字典)
|
运行项目模拟(返回字典)
|
||||||
@@ -172,7 +172,7 @@ async def run_project_return_dict_endpoint(network: str = Query(..., description
|
|||||||
|
|
||||||
|
|
||||||
# put in inp folder, name without extension
|
# put in inp folder, name without extension
|
||||||
@router.get("/runinp/", summary="运行INP文件", description="运行指定INP文件格式的管网模型进行水力模拟。INP文件应该放在inp文件夹中,参数为文件名不含扩展名。")
|
@router.post("/inp-runs", summary="运行INP文件", description="运行指定INP文件格式的管网模型进行水力模拟。INP文件应该放在inp文件夹中,参数为文件名不含扩展名。")
|
||||||
async def run_inp_endpoint(network: str = Query(..., description="inp文件名(不含扩展名)")) -> str:
|
async def run_inp_endpoint(network: str = Query(..., description="inp文件名(不含扩展名)")) -> str:
|
||||||
"""
|
"""
|
||||||
运行INP文件
|
运行INP文件
|
||||||
@@ -185,7 +185,7 @@ async def run_inp_endpoint(network: str = Query(..., description="inp文件名
|
|||||||
|
|
||||||
|
|
||||||
# path is absolute path
|
# path is absolute path
|
||||||
@router.get("/dumpoutput/", summary="导出模拟输出", description="导出指定路径的模拟输出文件内容。参数应为绝对路径。")
|
@router.get("/outputs", summary="导出模拟输出", description="导出指定路径的模拟输出文件内容。参数应为绝对路径。")
|
||||||
async def dump_output_endpoint(output: str = Query(..., description="模拟输出文件的绝对路径")) -> str:
|
async def dump_output_endpoint(output: str = Query(..., description="模拟输出文件的绝对路径")) -> str:
|
||||||
"""
|
"""
|
||||||
导出模拟输出
|
导出模拟输出
|
||||||
@@ -198,8 +198,7 @@ async def dump_output_endpoint(output: str = Query(..., description="模拟输
|
|||||||
|
|
||||||
|
|
||||||
# Analysis Endpoints
|
# Analysis Endpoints
|
||||||
@router.get("/burst-analysis", summary="爆管分析(高级)", description="高级版本的爆管分析,支持在指定时间点修改泵控制模式和阀门开度,以分析这些改变对爆管影响的作用。支持固定泵和变速泵的独立控制。")
|
@router.post("/burst-analyses", summary="爆管分析(高级)", description="高级版本的爆管分析,支持在指定时间点修改泵控制模式和阀门开度,以分析这些改变对爆管影响的作用。支持固定泵和变速泵的独立控制。")
|
||||||
@router.get("/burst_analysis/", summary="爆管分析(高级,旧路径)", description="高级版本的爆管分析,支持在指定时间点修改泵控制模式和阀门开度,以分析这些改变对爆管影响的作用。支持固定泵和变速泵的独立控制。", deprecated=True)
|
|
||||||
async def fastapi_burst_analysis(
|
async def fastapi_burst_analysis(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
modify_pattern_start_time: str = Query(..., description="模式修改开始时间(ISO 8601格式)"),
|
modify_pattern_start_time: str = Query(..., description="模式修改开始时间(ISO 8601格式)"),
|
||||||
@@ -233,7 +232,7 @@ async def fastapi_burst_analysis(
|
|||||||
return "success"
|
return "success"
|
||||||
|
|
||||||
|
|
||||||
@router.get("/valve_close_analysis/", response_class=PlainTextResponse, summary="阀门关闭分析(高级)", description="高级版本的阀门关闭分析,支持同时关闭多个阀门,并在指定持续时间内进行模拟。返回纯文本格式的分析结果。")
|
@router.post("/valve-closure-analyses", response_class=PlainTextResponse, summary="阀门关闭分析(高级)", description="高级版本的阀门关闭分析,支持同时关闭多个阀门,并在指定持续时间内进行模拟。返回纯文本格式的分析结果。")
|
||||||
async def fastapi_valve_close_analysis(
|
async def fastapi_valve_close_analysis(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
start_time: str = Query(..., description="阀门关闭开始时间(ISO 8601格式)"),
|
start_time: str = Query(..., description="阀门关闭开始时间(ISO 8601格式)"),
|
||||||
@@ -262,8 +261,7 @@ async def fastapi_valve_close_analysis(
|
|||||||
return result or "success"
|
return result or "success"
|
||||||
|
|
||||||
|
|
||||||
@router.get("/valve-isolation-analysis", summary="阀门隔离分析", description="分析当发生突发事件时,通过关闭指定阀门进行隔离,确定哪些阀门必须关闭、哪些可选关闭,以及隔离的可行性。")
|
@router.post("/valve-isolation-analyses", summary="阀门隔离分析", description="分析当发生突发事件时,通过关闭指定阀门进行隔离,确定哪些阀门必须关闭、哪些可选关闭,以及隔离的可行性。")
|
||||||
@router.get("/valve_isolation_analysis/", summary="阀门隔离分析(旧路径)", description="分析当发生突发事件时,通过关闭指定阀门进行隔离,确定哪些阀门必须关闭、哪些可选关闭,以及隔离的可行性。", deprecated=True)
|
|
||||||
async def valve_isolation_endpoint(
|
async def valve_isolation_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
accident_element: List[str] = Query(..., description="发生事故的管段/节点ID列表"),
|
accident_element: List[str] = Query(..., description="发生事故的管段/节点ID列表"),
|
||||||
@@ -304,8 +302,7 @@ async def valve_isolation_endpoint(
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@router.get("/flushing-analysis", response_class=PlainTextResponse, summary="冲洗分析(高级)", description="高级版本的冲洗分析,支持同时开启多个阀门进行冲洗,指定排污节点,并设置固定的冲洗流量。返回纯文本格式的分析结果。")
|
@router.post("/flushing-analyses", response_class=PlainTextResponse, summary="冲洗分析(高级)", description="高级版本的冲洗分析,支持同时开启多个阀门进行冲洗,指定排污节点,并设置固定的冲洗流量。返回纯文本格式的分析结果。")
|
||||||
@router.get("/flushing_analysis/", response_class=PlainTextResponse, summary="冲洗分析(高级,旧路径)", description="高级版本的冲洗分析,支持同时开启多个阀门进行冲洗,指定排污节点,并设置固定的冲洗流量。返回纯文本格式的分析结果。", deprecated=True)
|
|
||||||
async def fastapi_flushing_analysis(
|
async def fastapi_flushing_analysis(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
start_time: str = Query(..., description="冲洗开始时间(ISO 8601格式)"),
|
start_time: str = Query(..., description="冲洗开始时间(ISO 8601格式)"),
|
||||||
@@ -347,8 +344,7 @@ async def fastapi_flushing_analysis(
|
|||||||
return result or "success"
|
return result or "success"
|
||||||
|
|
||||||
|
|
||||||
@router.get("/contaminant-simulation", response_class=PlainTextResponse, summary="污染物模拟", description="对管网中的污染物扩散进行模拟,评估污染源对管网的影响范围和浓度分布。支持指定污染源位置、污染浓度和扩散模式。")
|
@router.post("/contaminant-simulations", response_class=PlainTextResponse, summary="污染物模拟", description="对管网中的污染物扩散进行模拟,评估污染源对管网的影响范围和浓度分布。支持指定污染源位置、污染浓度和扩散模式。")
|
||||||
@router.get("/contaminant_simulation/", response_class=PlainTextResponse, summary="污染物模拟(旧路径)", description="对管网中的污染物扩散进行模拟,评估污染源对管网的影响范围和浓度分布。支持指定污染源位置、污染浓度和扩散模式。", deprecated=True)
|
|
||||||
async def fastapi_contaminant_simulation(
|
async def fastapi_contaminant_simulation(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
start_time: str = Query(..., description="污染开始时间(ISO 8601格式)"),
|
start_time: str = Query(..., description="污染开始时间(ISO 8601格式)"),
|
||||||
@@ -385,7 +381,7 @@ async def fastapi_contaminant_simulation(
|
|||||||
return result or "success"
|
return result or "success"
|
||||||
|
|
||||||
|
|
||||||
@router.get("/age_analysis/", response_class=PlainTextResponse, summary="水龄分析(高级)", description="高级版本的水龄分析,在指定时间点进行分析,支持自定义模拟持续时间。返回纯文本格式的分析结果。")
|
@router.post("/water-age-analyses", response_class=PlainTextResponse, summary="水龄分析(高级)", description="高级版本的水龄分析,在指定时间点进行分析,支持自定义模拟持续时间。返回纯文本格式的分析结果。")
|
||||||
async def fastapi_age_analysis(
|
async def fastapi_age_analysis(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
start_time: str = Query(..., description="分析开始时间(ISO 8601格式)"),
|
start_time: str = Query(..., description="分析开始时间(ISO 8601格式)"),
|
||||||
@@ -409,7 +405,7 @@ async def fastapi_age_analysis(
|
|||||||
# return scheduling_analysis(network)
|
# return scheduling_analysis(network)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/pressureregulation/", summary="压力调节(基础)", description="对管网的压力进行调节分析,通过控制泵的运行来维持目标节点的目标压力。此为基础版本。")
|
@router.post("/pressure-regulation-calculations", summary="压力调节(基础)", description="对管网的压力进行调节分析,通过控制泵的运行来维持目标节点的目标压力。此为基础版本。")
|
||||||
async def pressure_regulation_endpoint(
|
async def pressure_regulation_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
target_node: str = Query(..., description="目标节点ID"),
|
target_node: str = Query(..., description="目标节点ID"),
|
||||||
@@ -427,7 +423,7 @@ async def pressure_regulation_endpoint(
|
|||||||
return pressure_regulation(network, target_node, target_pressure)
|
return pressure_regulation(network, target_node, target_pressure)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/pressure_regulation/", summary="压力调节(高级)", description="高级版本的压力调节分析,通过JSON请求体提供详细的控制参数,包括固定泵和变速泵的独立控制、水箱初始水位等。")
|
@router.post("/pressure-regulation-analyses", summary="压力调节(高级)", description="高级版本的压力调节分析,通过JSON请求体提供详细的控制参数,包括固定泵和变速泵的独立控制、水箱初始水位等。")
|
||||||
async def fastapi_pressure_regulation(data: PressureRegulation = Body(..., description="压力调节控制参数")) -> str:
|
async def fastapi_pressure_regulation(data: PressureRegulation = Body(..., description="压力调节控制参数")) -> str:
|
||||||
"""
|
"""
|
||||||
压力调节(高级版本)
|
压力调节(高级版本)
|
||||||
@@ -465,7 +461,7 @@ async def fastapi_pressure_regulation(data: PressureRegulation = Body(..., descr
|
|||||||
return "success"
|
return "success"
|
||||||
|
|
||||||
|
|
||||||
@router.post("/project_management/", summary="项目管理(高级)", description="高级版本的项目管理,通过JSON请求体提供详细的控制参数,包括泵控制策略、水箱初始水位和区域需水量控制。")
|
@router.post("/project-managements", summary="项目管理(高级)", description="高级版本的项目管理,通过JSON请求体提供详细的控制参数,包括泵控制策略、水箱初始水位和区域需水量控制。")
|
||||||
async def fastapi_project_management(data: ProjectManagement = Body(..., description="项目管理控制参数")) -> str:
|
async def fastapi_project_management(data: ProjectManagement = Body(..., description="项目管理控制参数")) -> str:
|
||||||
"""
|
"""
|
||||||
项目管理(高级版本)
|
项目管理(高级版本)
|
||||||
@@ -494,7 +490,7 @@ async def fastapi_project_management(data: ProjectManagement = Body(..., descrip
|
|||||||
# return daily_scheduling_analysis(network)
|
# return daily_scheduling_analysis(network)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/scheduling_analysis/", summary="排程分析", description="对管网的供水排程进行分析,优化泵的运行时间和出水流量,平衡水厂出水、水箱进出水,满足用户需求。")
|
@router.post("/scheduling-analyses", summary="排程分析", description="对管网的供水排程进行分析,优化泵的运行时间和出水流量,平衡水厂出水、水箱进出水,满足用户需求。")
|
||||||
async def fastapi_scheduling_analysis(data: SchedulingAnalysis = Body(..., description="排程分析参数")) -> str:
|
async def fastapi_scheduling_analysis(data: SchedulingAnalysis = Body(..., description="排程分析参数")) -> str:
|
||||||
"""
|
"""
|
||||||
排程分析
|
排程分析
|
||||||
@@ -520,7 +516,7 @@ async def fastapi_scheduling_analysis(data: SchedulingAnalysis = Body(..., descr
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/daily_scheduling_analysis/", summary="日排程分析", description="对管网的每日供水排程进行分析,优化水库、水厂、水箱和用户需求的协调,制定合理的每日排程方案。")
|
@router.post("/daily-scheduling-analyses", summary="日排程分析", description="对管网的每日供水排程进行分析,优化水库、水厂、水箱和用户需求的协调,制定合理的每日排程方案。")
|
||||||
async def fastapi_daily_scheduling_analysis(data: DailySchedulingAnalysis = Body(..., description="日排程分析参数")) -> str:
|
async def fastapi_daily_scheduling_analysis(data: DailySchedulingAnalysis = Body(..., description="日排程分析参数")) -> str:
|
||||||
"""
|
"""
|
||||||
日排程分析
|
日排程分析
|
||||||
@@ -552,7 +548,7 @@ async def fastapi_daily_scheduling_analysis(data: DailySchedulingAnalysis = Body
|
|||||||
# return pump_failure(network, pump_id, time)
|
# return pump_failure(network, pump_id, time)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/pump_failure/", summary="泵故障管理", description="记录和管理泵的故障状态,包括故障发生时间和受影响的泵列表。系统将记录故障日志并更新泵状态。")
|
@router.post("/pump-failure-events", summary="泵故障管理", description="记录和管理泵的故障状态,包括故障发生时间和受影响的泵列表。系统将记录故障日志并更新泵状态。")
|
||||||
async def fastapi_pump_failure(data: PumpFailureState = Body(..., description="泵故障状态信息")) -> str:
|
async def fastapi_pump_failure(data: PumpFailureState = Body(..., description="泵故障状态信息")) -> str:
|
||||||
"""
|
"""
|
||||||
泵故障管理
|
泵故障管理
|
||||||
@@ -596,7 +592,7 @@ async def fastapi_pump_failure(data: PumpFailureState = Body(..., description="
|
|||||||
return json.dumps("SUCCESS")
|
return json.dumps("SUCCESS")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/pressuresensorplacementsensitivity/", summary="压力传感器放置-灵敏度分析(基础)", description="基于灵敏度分析方法,为指定管网项目确定最优的压力传感器放置位置。此为基础版本。")
|
@router.post("/pressure-sensor-placement-sensitivity-calculations", summary="压力传感器放置-灵敏度分析(基础)", description="基于灵敏度分析方法,为指定管网项目确定最优的压力传感器放置位置。此为基础版本。")
|
||||||
async def pressure_sensor_placement_sensitivity_endpoint(
|
async def pressure_sensor_placement_sensitivity_endpoint(
|
||||||
name: str = Query(..., description="管网名称(或数据库名称)"),
|
name: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
scheme_name: str = Query(..., description="放置方案名称"),
|
scheme_name: str = Query(..., description="放置方案名称"),
|
||||||
@@ -620,7 +616,7 @@ async def pressure_sensor_placement_sensitivity_endpoint(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/pressure_sensor_placement_sensitivity/", summary="压力传感器放置-灵敏度分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于灵敏度分析方法确定最优放置位置。")
|
@router.post("/pressure-sensor-placement-sensitivities", summary="压力传感器放置-灵敏度分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于灵敏度分析方法确定最优放置位置。")
|
||||||
async def fastapi_pressure_sensor_placement_sensitivity(
|
async def fastapi_pressure_sensor_placement_sensitivity(
|
||||||
data: PressureSensorPlacement = Body(..., description="传感器放置分析参数"),
|
data: PressureSensorPlacement = Body(..., description="传感器放置分析参数"),
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -646,7 +642,7 @@ async def fastapi_pressure_sensor_placement_sensitivity(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/pressuresensorplacementkmeans/", summary="压力传感器放置-KMeans聚类分析(基础)", description="基于KMeans聚类算法,为指定管网项目确定压力传感器的最优放置位置。此为基础版本。")
|
@router.post("/pressure-sensor-placement-kmeans-calculations", summary="压力传感器放置-KMeans聚类分析(基础)", description="基于KMeans聚类算法,为指定管网项目确定压力传感器的最优放置位置。此为基础版本。")
|
||||||
async def pressure_sensor_placement_kmeans_endpoint(
|
async def pressure_sensor_placement_kmeans_endpoint(
|
||||||
name: str = Query(..., description="管网名称(或数据库名称)"),
|
name: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
scheme_name: str = Query(..., description="放置方案名称"),
|
scheme_name: str = Query(..., description="放置方案名称"),
|
||||||
@@ -670,7 +666,7 @@ async def pressure_sensor_placement_kmeans_endpoint(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/pressure_sensor_placement_kmeans/", summary="压力传感器放置-KMeans聚类分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于KMeans聚类算法确定最优放置位置。")
|
@router.post("/pressure-sensor-placement-kmeans", summary="压力传感器放置-KMeans聚类分析(高级)", description="高级版本的压力传感器放置分析,通过JSON请求体提供详细参数。基于KMeans聚类算法确定最优放置位置。")
|
||||||
async def fastapi_pressure_sensor_placement_kmeans(
|
async def fastapi_pressure_sensor_placement_kmeans(
|
||||||
data: PressureSensorPlacement = Body(..., description="传感器放置分析参数"),
|
data: PressureSensorPlacement = Body(..., description="传感器放置分析参数"),
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -697,7 +693,6 @@ async def fastapi_pressure_sensor_placement_kmeans(
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/sensor-placement-schemes", summary="传感器放置方案创建", description="创建新的传感器放置方案,支持灵敏度分析和KMeans聚类两种方法。根据指定的方法自动计算最优的传感器放置位置。")
|
@router.post("/sensor-placement-schemes", summary="传感器放置方案创建", description="创建新的传感器放置方案,支持灵敏度分析和KMeans聚类两种方法。根据指定的方法自动计算最优的传感器放置位置。")
|
||||||
@router.post("/sensorplacementscheme/create", summary="传感器放置方案创建(旧路径)", description="创建新的传感器放置方案,支持灵敏度分析和KMeans聚类两种方法。根据指定的方法自动计算最优的传感器放置位置。", deprecated=True)
|
|
||||||
async def fastapi_pressure_sensor_placement(
|
async def fastapi_pressure_sensor_placement(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
scheme_name: str = Query(..., description="放置方案名称"),
|
scheme_name: str = Query(..., description="放置方案名称"),
|
||||||
@@ -745,8 +740,7 @@ async def fastapi_pressure_sensor_placement(
|
|||||||
return "success"
|
return "success"
|
||||||
|
|
||||||
|
|
||||||
@router.post("/simulations/run-by-date", summary="手动运行日期指定模拟", description="根据指定的开始时间和持续时间,手动运行水力模拟。开始时间必须是显式带时区的 ISO 8601 / RFC3339 时间。")
|
@router.post("/simulation-runs", summary="手动运行日期指定模拟", description="根据指定的开始时间和持续时间,手动运行水力模拟。开始时间必须是显式带时区的 ISO 8601 / RFC3339 时间。")
|
||||||
@router.post("/runsimulationmanuallybydate/", summary="手动运行日期指定模拟(旧路径)", description="根据指定的开始时间和持续时间,手动运行水力模拟。开始时间必须是显式带时区的 ISO 8601 / RFC3339 时间。", deprecated=True)
|
|
||||||
async def fastapi_run_simulation_manually_by_date(
|
async def fastapi_run_simulation_manually_by_date(
|
||||||
data: RunSimulationManuallyByDate = Body(..., description="模拟运行参数"),
|
data: RunSimulationManuallyByDate = Body(..., description="模拟运行参数"),
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from app.services.tjnetwork import (
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@router.get("/getcurrentoperationid/", summary="获取当前操作ID", description="获取网络当前的操作ID")
|
@router.get("/current-operation-ids", summary="获取当前操作ID", description="获取网络当前的操作ID")
|
||||||
async def get_current_operation_id_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> int:
|
async def get_current_operation_id_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> int:
|
||||||
"""
|
"""
|
||||||
获取当前操作ID
|
获取当前操作ID
|
||||||
@@ -32,7 +32,7 @@ async def get_current_operation_id_endpoint(network: str = Query(..., descriptio
|
|||||||
"""
|
"""
|
||||||
return get_current_operation(network)
|
return get_current_operation(network)
|
||||||
|
|
||||||
@router.post("/undo/", summary="撤销操作", description="撤销网络上最后的一个操作")
|
@router.post("/undos", summary="撤销操作", description="撤销网络上最后的一个操作")
|
||||||
async def undo_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")):
|
async def undo_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")):
|
||||||
"""
|
"""
|
||||||
撤销操作
|
撤销操作
|
||||||
@@ -41,7 +41,7 @@ async def undo_endpoint(network: str = Query(..., description="管网名称(
|
|||||||
"""
|
"""
|
||||||
return execute_undo(network)
|
return execute_undo(network)
|
||||||
|
|
||||||
@router.post("/redo/", summary="重做操作", description="重做网络上被撤销的操作")
|
@router.post("/redos", summary="重做操作", description="重做网络上被撤销的操作")
|
||||||
async def redo_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")):
|
async def redo_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")):
|
||||||
"""
|
"""
|
||||||
重做操作
|
重做操作
|
||||||
@@ -50,7 +50,7 @@ async def redo_endpoint(network: str = Query(..., description="管网名称(
|
|||||||
"""
|
"""
|
||||||
return execute_redo(network)
|
return execute_redo(network)
|
||||||
|
|
||||||
@router.get("/getsnapshots/", summary="获取快照列表", description="获取网络中的所有快照")
|
@router.get("/snapshots", summary="获取快照列表", description="获取网络中的所有快照")
|
||||||
async def list_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[tuple[int, str]]:
|
async def list_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[tuple[int, str]]:
|
||||||
"""
|
"""
|
||||||
获取快照列表
|
获取快照列表
|
||||||
@@ -59,7 +59,7 @@ async def list_snapshot_endpoint(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return list_snapshot(network)
|
return list_snapshot(network)
|
||||||
|
|
||||||
@router.get("/havesnapshot/", summary="检查快照是否存在", description="检查指定标签的快照是否存在")
|
@router.get("/snapshots/existence", summary="检查快照是否存在", description="检查指定标签的快照是否存在")
|
||||||
async def have_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> bool:
|
async def have_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> bool:
|
||||||
"""
|
"""
|
||||||
检查快照是否存在
|
检查快照是否存在
|
||||||
@@ -68,7 +68,7 @@ async def have_snapshot_endpoint(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return have_snapshot(network, tag)
|
return have_snapshot(network, tag)
|
||||||
|
|
||||||
@router.get("/havesnapshotforoperation/", summary="检查操作快照是否存在", description="检查指定操作ID的快照是否存在")
|
@router.get("/snapshot-for-operations", summary="检查操作快照是否存在", description="检查指定操作ID的快照是否存在")
|
||||||
async def have_snapshot_for_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), operation: int = Query(..., description="操作ID")) -> bool:
|
async def have_snapshot_for_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), operation: int = Query(..., description="操作ID")) -> bool:
|
||||||
"""
|
"""
|
||||||
检查操作快照是否存在
|
检查操作快照是否存在
|
||||||
@@ -77,7 +77,7 @@ async def have_snapshot_for_operation_endpoint(network: str = Query(..., descrip
|
|||||||
"""
|
"""
|
||||||
return have_snapshot_for_operation(network, operation)
|
return have_snapshot_for_operation(network, operation)
|
||||||
|
|
||||||
@router.get("/havesnapshotforcurrentoperation/", summary="检查当前操作快照是否存在", description="检查当前操作的快照是否存在")
|
@router.get("/snapshot-for-current-operations", summary="检查当前操作快照是否存在", description="检查当前操作的快照是否存在")
|
||||||
async def have_snapshot_for_current_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> bool:
|
async def have_snapshot_for_current_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> bool:
|
||||||
"""
|
"""
|
||||||
检查当前操作快照是否存在
|
检查当前操作快照是否存在
|
||||||
@@ -86,7 +86,7 @@ async def have_snapshot_for_current_operation_endpoint(network: str = Query(...,
|
|||||||
"""
|
"""
|
||||||
return have_snapshot_for_current_operation(network)
|
return have_snapshot_for_current_operation(network)
|
||||||
|
|
||||||
@router.post("/takesnapshotforoperation/", summary="为操作创建快照", description="为指定的操作创建快照")
|
@router.post("/snapshot-for-operations", summary="为操作创建快照", description="为指定的操作创建快照")
|
||||||
async def take_snapshot_for_operation_endpoint(
|
async def take_snapshot_for_operation_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
operation: int = Query(..., description="操作ID"),
|
operation: int = Query(..., description="操作ID"),
|
||||||
@@ -99,7 +99,7 @@ async def take_snapshot_for_operation_endpoint(
|
|||||||
"""
|
"""
|
||||||
return take_snapshot_for_operation(network, operation, tag)
|
return take_snapshot_for_operation(network, operation, tag)
|
||||||
|
|
||||||
@router.post("/takesnapshotforcurrentoperation", summary="为当前操作创建快照", description="为当前操作创建快照")
|
@router.post("/snapshot-for-current-operations", summary="为当前操作创建快照", description="为当前操作创建快照")
|
||||||
async def take_snapshot_for_current_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None:
|
async def take_snapshot_for_current_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None:
|
||||||
"""
|
"""
|
||||||
为当前操作创建快照
|
为当前操作创建快照
|
||||||
@@ -108,17 +108,7 @@ async def take_snapshot_for_current_operation_endpoint(network: str = Query(...,
|
|||||||
"""
|
"""
|
||||||
return take_snapshot_for_current_operation(network, tag)
|
return take_snapshot_for_current_operation(network, tag)
|
||||||
|
|
||||||
# 兼容旧拼写: takenapshotforcurrentoperation
|
@router.post("/snapshots", summary="创建快照", description="为网络创建一个快照")
|
||||||
@router.post("/takenapshotforcurrentoperation", summary="为当前操作创建快照(兼容模式)", description="为当前操作创建快照(兼容旧的API路径)")
|
|
||||||
async def take_snapshot_for_current_operation_legacy_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None:
|
|
||||||
"""
|
|
||||||
为当前操作创建快照(兼容模式)
|
|
||||||
|
|
||||||
兼容旧的API路径,为网络当前操作创建一个快照
|
|
||||||
"""
|
|
||||||
return take_snapshot_for_current_operation(network, tag)
|
|
||||||
|
|
||||||
@router.post("/takesnapshot/", summary="创建快照", description="为网络创建一个快照")
|
|
||||||
async def take_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None:
|
async def take_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签")) -> None:
|
||||||
"""
|
"""
|
||||||
创建快照
|
创建快照
|
||||||
@@ -127,7 +117,7 @@ async def take_snapshot_endpoint(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return take_snapshot(network, tag)
|
return take_snapshot(network, tag)
|
||||||
|
|
||||||
@router.post("/picksnapshot/", summary="选择快照", description="选择并恢复到指定的快照", response_model=None)
|
@router.patch("/snapshots", summary="选择快照", description="选择并恢复到指定的快照", response_model=None)
|
||||||
async def pick_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签"), discard: bool = Query(False, description="是否丢弃当前更改")) -> ChangeSet:
|
async def pick_snapshot_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), tag: str = Query(..., description="快照标签"), discard: bool = Query(False, description="是否丢弃当前更改")) -> ChangeSet:
|
||||||
"""
|
"""
|
||||||
选择快照
|
选择快照
|
||||||
@@ -136,7 +126,7 @@ async def pick_snapshot_endpoint(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return pick_snapshot(network, tag, discard)
|
return pick_snapshot(network, tag, discard)
|
||||||
|
|
||||||
@router.post("/pickoperation/", summary="选择操作", description="选择并恢复到指定的操作", response_model=None)
|
@router.patch("/operations", summary="选择操作", description="选择并恢复到指定的操作", response_model=None)
|
||||||
async def pick_operation_endpoint(
|
async def pick_operation_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
operation: int = Query(..., description="操作ID"),
|
operation: int = Query(..., description="操作ID"),
|
||||||
@@ -149,7 +139,7 @@ async def pick_operation_endpoint(
|
|||||||
"""
|
"""
|
||||||
return pick_operation(network, operation, discard)
|
return pick_operation(network, operation, discard)
|
||||||
|
|
||||||
@router.get("/syncwithserver/", summary="与服务器同步", description="将网络与服务器同步到指定操作", response_model=None)
|
@router.post("/with-servers", summary="与服务器同步", description="将网络与服务器同步到指定操作", response_model=None)
|
||||||
async def sync_with_server_endpoint(
|
async def sync_with_server_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
operation: int = Query(..., description="目标操作ID"),
|
operation: int = Query(..., description="目标操作ID"),
|
||||||
@@ -162,7 +152,7 @@ async def sync_with_server_endpoint(
|
|||||||
"""
|
"""
|
||||||
return sync_with_server(network, operation)
|
return sync_with_server(network, operation)
|
||||||
|
|
||||||
@router.post("/batch/", summary="执行批量命令", description="执行多个网络操作命令", response_model=None)
|
@router.post("/network-command-batches", summary="执行批量命令", description="执行多个网络操作命令", response_model=None)
|
||||||
async def execute_batch_commands_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), req: Request = None) -> ChangeSet:
|
async def execute_batch_commands_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), req: Request = None) -> ChangeSet:
|
||||||
"""
|
"""
|
||||||
执行批量命令
|
执行批量命令
|
||||||
@@ -175,7 +165,7 @@ async def execute_batch_commands_endpoint(network: str = Query(..., description=
|
|||||||
rcs = execute_batch_commands(network, cs)
|
rcs = execute_batch_commands(network, cs)
|
||||||
return rcs
|
return rcs
|
||||||
|
|
||||||
@router.post("/compressedbatch/", summary="执行压缩批量命令", description="执行压缩的批量命令", response_model=None)
|
@router.post("/network-command-batches/compressed", summary="执行压缩批量命令", description="执行压缩的批量命令", response_model=None)
|
||||||
async def execute_compressed_batch_commands_endpoint(
|
async def execute_compressed_batch_commands_endpoint(
|
||||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
req: Request = None
|
req: Request = None
|
||||||
@@ -190,7 +180,7 @@ async def execute_compressed_batch_commands_endpoint(
|
|||||||
cs.operations = jo_root["operations"]
|
cs.operations = jo_root["operations"]
|
||||||
return execute_batch_command(network, cs)
|
return execute_batch_command(network, cs)
|
||||||
|
|
||||||
@router.get("/getrestoreoperation/", summary="获取恢复操作ID", description="获取网络的恢复操作ID")
|
@router.get("/restore-operations", summary="获取恢复操作ID", description="获取网络的恢复操作ID")
|
||||||
async def get_restore_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> int:
|
async def get_restore_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)")) -> int:
|
||||||
"""
|
"""
|
||||||
获取恢复操作ID
|
获取恢复操作ID
|
||||||
@@ -199,7 +189,7 @@ async def get_restore_operation_endpoint(network: str = Query(..., description="
|
|||||||
"""
|
"""
|
||||||
return get_restore_operation(network)
|
return get_restore_operation(network)
|
||||||
|
|
||||||
@router.post("/setrestoreoperation/", summary="设置恢复操作ID", description="设置网络的恢复操作ID")
|
@router.patch("/restore-operations", summary="设置恢复操作ID", description="设置网络的恢复操作ID")
|
||||||
async def set_restore_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), operation: int = Query(..., description="操作ID")) -> None:
|
async def set_restore_operation_endpoint(network: str = Query(..., description="管网名称(或数据库名称)"), operation: int = Query(..., description="操作ID")) -> None:
|
||||||
"""
|
"""
|
||||||
设置恢复操作ID
|
设置恢复操作ID
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from .dependencies import get_timescale_connection, get_postgres_connection
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/composite/scada-simulation", summary="获取SCADA关联的模拟数据")
|
@router.get("/timeseries/views/scada-simulations", summary="获取SCADA关联的模拟数据")
|
||||||
async def get_scada_associated_simulation_data(
|
async def get_scada_associated_simulation_data(
|
||||||
start_time: datetime = Query(..., description="查询开始时间"),
|
start_time: datetime = Query(..., description="查询开始时间"),
|
||||||
end_time: datetime = Query(..., description="查询结束时间"),
|
end_time: datetime = Query(..., description="查询结束时间"),
|
||||||
@@ -73,7 +73,7 @@ async def get_scada_associated_simulation_data(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/composite/element-simulation", summary="获取管网元素的模拟数据")
|
@router.get("/timeseries/views/element-simulations", summary="获取管网元素的模拟数据")
|
||||||
async def get_feature_simulation_data(
|
async def get_feature_simulation_data(
|
||||||
start_time: datetime = Query(..., description="查询开始时间"),
|
start_time: datetime = Query(..., description="查询开始时间"),
|
||||||
end_time: datetime = Query(..., description="查询结束时间"),
|
end_time: datetime = Query(..., description="查询结束时间"),
|
||||||
@@ -143,7 +143,7 @@ async def get_feature_simulation_data(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/composite/element-scada", summary="获取管网元素关联的SCADA监测数据")
|
@router.get("/timeseries/views/element-scada-readings", summary="获取管网元素关联的SCADA监测数据")
|
||||||
async def get_element_associated_scada_data(
|
async def get_element_associated_scada_data(
|
||||||
element_id: str = Query(..., description="管网元素ID(管道或节点)"),
|
element_id: str = Query(..., description="管网元素ID(管道或节点)"),
|
||||||
start_time: datetime = Query(..., description="查询开始时间"),
|
start_time: datetime = Query(..., description="查询开始时间"),
|
||||||
@@ -185,7 +185,7 @@ async def get_element_associated_scada_data(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.post("/composite/clean-scada", summary="清洗SCADA监测数据")
|
@router.post("/timeseries/scada-cleaning-runs", summary="清洗SCADA监测数据")
|
||||||
async def clean_scada_data(
|
async def clean_scada_data(
|
||||||
device_ids: str = Query(..., description="设备ID列表或 'all' 表示清洗所有设备"),
|
device_ids: str = Query(..., description="设备ID列表或 'all' 表示清洗所有设备"),
|
||||||
start_time: datetime = Query(..., description="清洗数据的开始时间"),
|
start_time: datetime = Query(..., description="清洗数据的开始时间"),
|
||||||
@@ -228,7 +228,7 @@ async def clean_scada_data(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/composite/pipeline-health-prediction", summary="预测管道健康状况")
|
@router.get("/pipeline-health-predictions", summary="预测管道健康状况")
|
||||||
async def predict_pipeline_health(
|
async def predict_pipeline_health(
|
||||||
query_time: datetime = Query(..., description="查询时间"),
|
query_time: datetime = Query(..., description="查询时间"),
|
||||||
network_name: str = Query(..., description="管网名称(或数据库名称)"),
|
network_name: str = Query(..., description="管网名称(或数据库名称)"),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ TIME_RANGE_START_DESC = f"时间范围开始时间。{TIME_WITH_TZ_DESC}"
|
|||||||
TIME_RANGE_END_DESC = f"时间范围结束时间。{TIME_WITH_TZ_DESC}"
|
TIME_RANGE_END_DESC = f"时间范围结束时间。{TIME_WITH_TZ_DESC}"
|
||||||
|
|
||||||
|
|
||||||
@router.post("/realtime/links/batch", status_code=201, summary="批量插入实时管道数据")
|
@router.post("/timeseries/realtime/links/batches", status_code=201, summary="批量插入实时管道数据")
|
||||||
async def insert_realtime_links(
|
async def insert_realtime_links(
|
||||||
data: List[dict] = Body(..., description="管道数据列表,每项包含管道ID、时间戳等信息"),
|
data: List[dict] = Body(..., description="管道数据列表,每项包含管道ID、时间戳等信息"),
|
||||||
conn: AsyncConnection = Depends(get_timescale_connection)
|
conn: AsyncConnection = Depends(get_timescale_connection)
|
||||||
@@ -34,7 +34,7 @@ async def insert_realtime_links(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/realtime/links",
|
"/timeseries/realtime/links",
|
||||||
summary="查询实时管道数据",
|
summary="查询实时管道数据",
|
||||||
description="按时间范围查询实时管道数据。start_time 和 end_time 必须显式带时区;允许传 UTC+8,服务端会先归一化为 UTC 再执行查询。",
|
description="按时间范围查询实时管道数据。start_time 和 end_time 必须显式带时区;允许传 UTC+8,服务端会先归一化为 UTC 再执行查询。",
|
||||||
)
|
)
|
||||||
@@ -60,7 +60,7 @@ async def get_realtime_links(
|
|||||||
|
|
||||||
|
|
||||||
@router.delete(
|
@router.delete(
|
||||||
"/realtime/links",
|
"/timeseries/realtime/links",
|
||||||
summary="删除实时管道数据",
|
summary="删除实时管道数据",
|
||||||
description="按时间范围删除实时管道数据。start_time 和 end_time 必须显式带时区;允许传 UTC+8,服务端按请求中的绝对时间删除对应 UTC 数据。",
|
description="按时间范围删除实时管道数据。start_time 和 end_time 必须显式带时区;允许传 UTC+8,服务端按请求中的绝对时间删除对应 UTC 数据。",
|
||||||
)
|
)
|
||||||
@@ -85,7 +85,7 @@ async def delete_realtime_links(
|
|||||||
return {"message": "Deleted successfully"}
|
return {"message": "Deleted successfully"}
|
||||||
|
|
||||||
|
|
||||||
@router.patch("/realtime/links/{link_id}/field", summary="更新实时管道字段")
|
@router.patch("/timeseries/realtime/links/{link_id}/field", summary="更新实时管道字段")
|
||||||
async def update_realtime_link_field(
|
async def update_realtime_link_field(
|
||||||
link_id: str = Path(..., description="管道ID"),
|
link_id: str = Path(..., description="管道ID"),
|
||||||
time: datetime = Query(..., description=f"要更新记录的时间戳。{TIME_WITH_TZ_DESC}"),
|
time: datetime = Query(..., description=f"要更新记录的时间戳。{TIME_WITH_TZ_DESC}"),
|
||||||
@@ -117,7 +117,7 @@ async def update_realtime_link_field(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.post("/realtime/nodes/batch", status_code=201, summary="批量插入实时节点数据")
|
@router.post("/timeseries/realtime/nodes/batches", status_code=201, summary="批量插入实时节点数据")
|
||||||
async def insert_realtime_nodes(
|
async def insert_realtime_nodes(
|
||||||
data: List[dict] = Body(..., description="节点数据列表,每项包含节点ID、时间戳等信息"),
|
data: List[dict] = Body(..., description="节点数据列表,每项包含节点ID、时间戳等信息"),
|
||||||
conn: AsyncConnection = Depends(get_timescale_connection)
|
conn: AsyncConnection = Depends(get_timescale_connection)
|
||||||
@@ -138,7 +138,7 @@ async def insert_realtime_nodes(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/realtime/nodes",
|
"/timeseries/realtime/nodes",
|
||||||
summary="查询实时节点数据",
|
summary="查询实时节点数据",
|
||||||
description="按时间范围查询实时节点数据。start_time 和 end_time 必须显式带时区;允许传 UTC+8,服务端会先归一化为 UTC 再执行查询。",
|
description="按时间范围查询实时节点数据。start_time 和 end_time 必须显式带时区;允许传 UTC+8,服务端会先归一化为 UTC 再执行查询。",
|
||||||
)
|
)
|
||||||
@@ -164,7 +164,7 @@ async def get_realtime_nodes(
|
|||||||
|
|
||||||
|
|
||||||
@router.delete(
|
@router.delete(
|
||||||
"/realtime/nodes",
|
"/timeseries/realtime/nodes",
|
||||||
summary="删除实时节点数据",
|
summary="删除实时节点数据",
|
||||||
description="按时间范围删除实时节点数据。start_time 和 end_time 必须显式带时区;允许传 UTC+8,服务端按请求中的绝对时间删除对应 UTC 数据。",
|
description="按时间范围删除实时节点数据。start_time 和 end_time 必须显式带时区;允许传 UTC+8,服务端按请求中的绝对时间删除对应 UTC 数据。",
|
||||||
)
|
)
|
||||||
@@ -191,7 +191,7 @@ async def delete_realtime_nodes(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/realtime/simulation/store", status_code=201, summary="存储实时模拟结果")
|
@router.post("/timeseries/realtime/simulation-results", status_code=201, summary="存储实时模拟结果")
|
||||||
async def store_realtime_simulation_result(
|
async def store_realtime_simulation_result(
|
||||||
node_result_list: List[dict] = Body(..., description="节点模拟结果列表"),
|
node_result_list: List[dict] = Body(..., description="节点模拟结果列表"),
|
||||||
link_result_list: List[dict] = Body(..., description="管道模拟结果列表"),
|
link_result_list: List[dict] = Body(..., description="管道模拟结果列表"),
|
||||||
@@ -218,7 +218,7 @@ async def store_realtime_simulation_result(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/realtime/query/by-time-property",
|
"/timeseries/realtime/records",
|
||||||
summary="按时间和属性查询实时数据",
|
summary="按时间和属性查询实时数据",
|
||||||
description="查询指定时间点的实时属性值。query_time 必须显式带时区;允许传 UTC+8,服务端会先归一化为 UTC 再执行查询。",
|
description="查询指定时间点的实时属性值。query_time 必须显式带时区;允许传 UTC+8,服务端会先归一化为 UTC 再执行查询。",
|
||||||
)
|
)
|
||||||
@@ -254,7 +254,7 @@ async def query_realtime_records_by_time_property(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/realtime/query/by-id-time",
|
"/timeseries/realtime/simulation-results",
|
||||||
summary="按ID和时间查询实时模拟数据",
|
summary="按ID和时间查询实时模拟数据",
|
||||||
description="查询指定元素在某一时间点的实时模拟结果。query_time 必须显式带时区;允许传 UTC+8,服务端会先归一化为 UTC 再执行查询。",
|
description="查询指定元素在某一时间点的实时模拟结果。query_time 必须显式带时区;允许传 UTC+8,服务端会先归一化为 UTC 再执行查询。",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from .dependencies import get_timescale_connection
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/scada/batch", status_code=201, summary="批量插入SCADA监测数据")
|
@router.post("/timeseries/scada-readings/batches", status_code=201, summary="批量插入SCADA监测数据")
|
||||||
async def insert_scada_data(
|
async def insert_scada_data(
|
||||||
data: List[dict] = Body(..., description="SCADA设备监测数据列表"),
|
data: List[dict] = Body(..., description="SCADA设备监测数据列表"),
|
||||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||||
@@ -29,7 +29,7 @@ async def insert_scada_data(
|
|||||||
return {"message": f"Inserted {len(data)} records"}
|
return {"message": f"Inserted {len(data)} records"}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/scada/by-ids-time-range", summary="按设备ID和时间范围查询SCADA数据")
|
@router.get("/timeseries/scada-readings", summary="按设备ID和时间范围查询SCADA数据")
|
||||||
async def get_scada_by_ids_time_range(
|
async def get_scada_by_ids_time_range(
|
||||||
start_time: datetime = Query(..., description="查询开始时间"),
|
start_time: datetime = Query(..., description="查询开始时间"),
|
||||||
end_time: datetime = Query(..., description="查询结束时间"),
|
end_time: datetime = Query(..., description="查询结束时间"),
|
||||||
@@ -60,7 +60,7 @@ async def get_scada_by_ids_time_range(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/scada/by-ids-field-time-range", summary="按设备ID、字段和时间范围查询SCADA数据"
|
"/timeseries/scada-readings/fields", summary="按设备ID、字段和时间范围查询SCADA数据"
|
||||||
)
|
)
|
||||||
async def get_scada_field_by_ids_time_range(
|
async def get_scada_field_by_ids_time_range(
|
||||||
start_time: datetime = Query(..., description="查询开始时间"),
|
start_time: datetime = Query(..., description="查询开始时间"),
|
||||||
@@ -101,7 +101,7 @@ async def get_scada_field_by_ids_time_range(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.patch("/scada/{device_id}/field", summary="更新SCADA设备字段")
|
@router.patch("/timeseries/scada-readings/{device_id}/field", summary="更新SCADA设备字段")
|
||||||
async def update_scada_field(
|
async def update_scada_field(
|
||||||
device_id: str = Path(..., description="设备ID"),
|
device_id: str = Path(..., description="设备ID"),
|
||||||
time: datetime = Query(..., description="更新数据的时间戳"),
|
time: datetime = Query(..., description="更新数据的时间戳"),
|
||||||
@@ -133,7 +133,7 @@ async def update_scada_field(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/scada/by-id-time-range", summary="按设备ID和时间范围删除SCADA数据")
|
@router.delete("/timeseries/scada-readings", summary="按设备ID和时间范围删除SCADA数据")
|
||||||
async def delete_scada_data(
|
async def delete_scada_data(
|
||||||
device_id: str = Query(..., description="设备ID"),
|
device_id: str = Query(..., description="设备ID"),
|
||||||
start_time: datetime = Query(..., description="删除开始时间"),
|
start_time: datetime = Query(..., description="删除开始时间"),
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from .dependencies import get_timescale_connection
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/scheme/links/batch", status_code=201, summary="批量插入方案管道数据")
|
@router.post("/timeseries/schemes/links/batches", status_code=201, summary="批量插入方案管道数据")
|
||||||
async def insert_scheme_links(
|
async def insert_scheme_links(
|
||||||
data: List[dict] = Body(..., description="方案管道数据列表"),
|
data: List[dict] = Body(..., description="方案管道数据列表"),
|
||||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||||
@@ -29,7 +29,7 @@ async def insert_scheme_links(
|
|||||||
return {"message": f"Inserted {len(data)} records"}
|
return {"message": f"Inserted {len(data)} records"}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/scheme/links", summary="查询方案管道数据")
|
@router.get("/timeseries/schemes/links", summary="查询方案管道数据")
|
||||||
async def get_scheme_links(
|
async def get_scheme_links(
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
scheme_name: str = Query(..., description="方案名称"),
|
scheme_name: str = Query(..., description="方案名称"),
|
||||||
@@ -56,7 +56,7 @@ async def get_scheme_links(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/scheme/links/{link_id}/field", summary="查询方案管道字段数据")
|
@router.get("/timeseries/schemes/links/{link_id}/field", summary="查询方案管道字段数据")
|
||||||
async def get_scheme_link_field(
|
async def get_scheme_link_field(
|
||||||
link_id: str = Path(..., description="管道ID"),
|
link_id: str = Path(..., description="管道ID"),
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
@@ -93,7 +93,7 @@ async def get_scheme_link_field(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.patch("/scheme/links/{link_id}/field", summary="更新方案管道字段")
|
@router.patch("/timeseries/schemes/links/{link_id}/field", summary="更新方案管道字段")
|
||||||
async def update_scheme_link_field(
|
async def update_scheme_link_field(
|
||||||
link_id: str = Path(..., description="管道ID"),
|
link_id: str = Path(..., description="管道ID"),
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
@@ -131,7 +131,7 @@ async def update_scheme_link_field(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/scheme/links", summary="删除方案管道数据")
|
@router.delete("/timeseries/schemes/links", summary="删除方案管道数据")
|
||||||
async def delete_scheme_links(
|
async def delete_scheme_links(
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
scheme_name: str = Query(..., description="方案名称"),
|
scheme_name: str = Query(..., description="方案名称"),
|
||||||
@@ -159,7 +159,7 @@ async def delete_scheme_links(
|
|||||||
return {"message": "Deleted successfully"}
|
return {"message": "Deleted successfully"}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/scheme/nodes/batch", status_code=201, summary="批量插入方案节点数据")
|
@router.post("/timeseries/schemes/nodes/batches", status_code=201, summary="批量插入方案节点数据")
|
||||||
async def insert_scheme_nodes(
|
async def insert_scheme_nodes(
|
||||||
data: List[dict] = Body(..., description="方案节点数据列表"),
|
data: List[dict] = Body(..., description="方案节点数据列表"),
|
||||||
conn: AsyncConnection = Depends(get_timescale_connection),
|
conn: AsyncConnection = Depends(get_timescale_connection),
|
||||||
@@ -179,7 +179,7 @@ async def insert_scheme_nodes(
|
|||||||
return {"message": f"Inserted {len(data)} records"}
|
return {"message": f"Inserted {len(data)} records"}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/scheme/nodes/{node_id}/field", summary="查询方案节点字段数据")
|
@router.get("/timeseries/schemes/nodes/{node_id}/field", summary="查询方案节点字段数据")
|
||||||
async def get_scheme_node_field(
|
async def get_scheme_node_field(
|
||||||
node_id: str = Path(..., description="节点ID"),
|
node_id: str = Path(..., description="节点ID"),
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
@@ -216,7 +216,7 @@ async def get_scheme_node_field(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.patch("/scheme/nodes/{node_id}/field", summary="更新方案节点字段")
|
@router.patch("/timeseries/schemes/nodes/{node_id}/field", summary="更新方案节点字段")
|
||||||
async def update_scheme_node_field(
|
async def update_scheme_node_field(
|
||||||
node_id: str = Path(..., description="节点ID"),
|
node_id: str = Path(..., description="节点ID"),
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
@@ -254,7 +254,7 @@ async def update_scheme_node_field(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/scheme/nodes", summary="删除方案节点数据")
|
@router.delete("/timeseries/schemes/nodes", summary="删除方案节点数据")
|
||||||
async def delete_scheme_nodes(
|
async def delete_scheme_nodes(
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
scheme_name: str = Query(..., description="方案名称"),
|
scheme_name: str = Query(..., description="方案名称"),
|
||||||
@@ -282,7 +282,7 @@ async def delete_scheme_nodes(
|
|||||||
return {"message": "Deleted successfully"}
|
return {"message": "Deleted successfully"}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/scheme/simulation/store", status_code=201, summary="存储方案模拟结果")
|
@router.post("/timeseries/schemes/simulation-results", status_code=201, summary="存储方案模拟结果")
|
||||||
async def store_scheme_simulation_result(
|
async def store_scheme_simulation_result(
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
scheme_name: str = Query(..., description="方案名称"),
|
scheme_name: str = Query(..., description="方案名称"),
|
||||||
@@ -318,7 +318,7 @@ async def store_scheme_simulation_result(
|
|||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/scheme/query/by-scheme-time-property", summary="按方案、时间和属性查询数据"
|
"/timeseries/schemes/records", summary="按方案、时间和属性查询数据"
|
||||||
)
|
)
|
||||||
async def query_scheme_records_by_scheme_time_property(
|
async def query_scheme_records_by_scheme_time_property(
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
@@ -355,7 +355,7 @@ async def query_scheme_records_by_scheme_time_property(
|
|||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/scheme/query/by-id-time", summary="按ID和时间查询方案模拟数据")
|
@router.get("/timeseries/schemes/simulation-results", summary="按ID和时间查询方案模拟数据")
|
||||||
async def query_scheme_simulation_by_id_time(
|
async def query_scheme_simulation_by_id_time(
|
||||||
scheme_type: str = Query(..., description="方案类型"),
|
scheme_type: str = Query(..., description="方案类型"),
|
||||||
scheme_name: str = Query(..., description="方案名称"),
|
scheme_name: str = Query(..., description="方案名称"),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ router = APIRouter()
|
|||||||
# user 39
|
# user 39
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
||||||
@router.get("/getuserschema/", summary="获取用户模式", description="获取指定网络的用户模式定义")
|
@router.get("/network-schemas/user", summary="获取用户模式", description="获取指定网络的用户模式定义")
|
||||||
async def fastapi_get_user_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[Any, Any]]:
|
async def fastapi_get_user_schema(network: str = Query(..., description="管网名称(或数据库名称)")) -> dict[str, dict[Any, Any]]:
|
||||||
"""
|
"""
|
||||||
获取用户模式定义
|
获取用户模式定义
|
||||||
@@ -17,7 +17,7 @@ async def fastapi_get_user_schema(network: str = Query(..., description="管网
|
|||||||
"""
|
"""
|
||||||
return get_user_schema(network)
|
return get_user_schema(network)
|
||||||
|
|
||||||
@router.get("/getuser/", summary="获取单个用户", description="获取指定网络中的单个用户信息")
|
@router.get("/users/detail", summary="获取单个用户", description="获取指定网络中的单个用户信息")
|
||||||
async def fastapi_get_user(network: str = Query(..., description="管网名称(或数据库名称)"), user_name: str = Query(..., description="用户名")) -> dict[Any, Any]:
|
async def fastapi_get_user(network: str = Query(..., description="管网名称(或数据库名称)"), user_name: str = Query(..., description="用户名")) -> dict[Any, Any]:
|
||||||
"""
|
"""
|
||||||
获取用户信息
|
获取用户信息
|
||||||
@@ -26,7 +26,7 @@ async def fastapi_get_user(network: str = Query(..., description="管网名称
|
|||||||
"""
|
"""
|
||||||
return get_user(network, user_name)
|
return get_user(network, user_name)
|
||||||
|
|
||||||
@router.get("/getallusers/", summary="获取所有用户", description="获取指定网络的所有用户列表")
|
@router.get("/users", summary="获取所有用户", description="获取指定网络的所有用户列表")
|
||||||
async def fastapi_get_all_users(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]:
|
async def fastapi_get_all_users(network: str = Query(..., description="管网名称(或数据库名称)")) -> list[dict[Any, Any]]:
|
||||||
"""
|
"""
|
||||||
获取所有用户列表
|
获取所有用户列表
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ router = APIRouter()
|
|||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/web-search",
|
"/web-searches",
|
||||||
summary="Web Search",
|
summary="Web Search",
|
||||||
description="调用 Bocha Web Search API 获取实时网页搜索结果",
|
description="调用 Bocha Web Search API 获取实时网页搜索结果",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,349 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import re
|
||||||
|
from collections.abc import Iterable
|
||||||
|
from copy import copy
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Any, Generic, TypeVar, get_args, get_origin
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, Query
|
||||||
|
from fastapi.routing import APIRoute
|
||||||
|
from pydantic import BaseModel, JsonValue, create_model
|
||||||
|
|
||||||
|
from app.api.problem_details import ProblemDetails
|
||||||
|
from app.api.v1.router import api_router as handler_api_router
|
||||||
|
from app.auth.metadata_dependencies import get_current_metadata_user
|
||||||
|
from app.auth.project_dependencies import ProjectContext, get_project_context
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
class Page(BaseModel, Generic[T]):
|
||||||
|
items: list[T]
|
||||||
|
total: int
|
||||||
|
limit: int
|
||||||
|
offset: int
|
||||||
|
|
||||||
|
|
||||||
|
_NAME_IS_NETWORK = {
|
||||||
|
"pressure_sensor_placement_sensitivity_endpoint",
|
||||||
|
"pressure_sensor_placement_kmeans_endpoint",
|
||||||
|
}
|
||||||
|
_DERIVE_USERNAME = {
|
||||||
|
"pressure_sensor_placement_sensitivity_endpoint": "username",
|
||||||
|
"pressure_sensor_placement_kmeans_endpoint": "username",
|
||||||
|
"fastapi_pressure_sensor_placement": "user_name",
|
||||||
|
}
|
||||||
|
_PUBLIC_PARAMETER_RENAMES = {
|
||||||
|
"burst_ID": "burst_id",
|
||||||
|
"drainage_node_ID": "drainage_node_id",
|
||||||
|
}
|
||||||
|
_MODEL_NAME_IS_NETWORK = {"RunSimulationManuallyByDate"}
|
||||||
|
_MODEL_USERNAME_FROM_AUTH: set[str] = set()
|
||||||
|
|
||||||
|
|
||||||
|
def _clean_name(name: str) -> str:
|
||||||
|
for prefix in ("fastapi_", "fast_"):
|
||||||
|
if name.startswith(prefix):
|
||||||
|
name = name[len(prefix) :]
|
||||||
|
break
|
||||||
|
if name.endswith("_endpoint"):
|
||||||
|
name = name[: -len("_endpoint")]
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def _rest_body_model(annotation):
|
||||||
|
if not inspect.isclass(annotation) or not issubclass(annotation, BaseModel):
|
||||||
|
return None
|
||||||
|
project_fields = {
|
||||||
|
name
|
||||||
|
for name in ("network", "network_name")
|
||||||
|
if name in annotation.model_fields
|
||||||
|
}
|
||||||
|
if annotation.__name__ in _MODEL_NAME_IS_NETWORK and "name" in annotation.model_fields:
|
||||||
|
project_fields.add("name")
|
||||||
|
username_fields = (
|
||||||
|
{
|
||||||
|
name
|
||||||
|
for name in ("username", "user_name")
|
||||||
|
if name in annotation.model_fields
|
||||||
|
}
|
||||||
|
if annotation.__name__ in _MODEL_USERNAME_FROM_AUTH
|
||||||
|
else set()
|
||||||
|
)
|
||||||
|
excluded_fields = project_fields | username_fields
|
||||||
|
if not excluded_fields:
|
||||||
|
return None
|
||||||
|
|
||||||
|
public_fields = {
|
||||||
|
name: (field.annotation, copy(field))
|
||||||
|
for name, field in annotation.model_fields.items()
|
||||||
|
if name not in excluded_fields
|
||||||
|
}
|
||||||
|
public_model = create_model(
|
||||||
|
f"{annotation.__name__}Rest",
|
||||||
|
__module__=annotation.__module__,
|
||||||
|
**public_fields,
|
||||||
|
)
|
||||||
|
return annotation, public_model, project_fields, username_fields
|
||||||
|
|
||||||
|
|
||||||
|
def _with_header_project_context(endpoint, route_name: str):
|
||||||
|
signature = inspect.signature(endpoint)
|
||||||
|
network_parameters = [
|
||||||
|
name for name in ("network", "network_name") if name in signature.parameters
|
||||||
|
]
|
||||||
|
if route_name in _NAME_IS_NETWORK and "name" in signature.parameters:
|
||||||
|
network_parameters.append("name")
|
||||||
|
username_parameter = _DERIVE_USERNAME.get(route_name)
|
||||||
|
parameter_renames = {
|
||||||
|
internal: public
|
||||||
|
for internal, public in _PUBLIC_PARAMETER_RENAMES.items()
|
||||||
|
if internal in signature.parameters
|
||||||
|
}
|
||||||
|
body_models = {
|
||||||
|
name: body_model
|
||||||
|
for name, parameter in signature.parameters.items()
|
||||||
|
if (body_model := _rest_body_model(parameter.annotation)) is not None
|
||||||
|
}
|
||||||
|
model_has_username = any(model[3] for model in body_models.values())
|
||||||
|
if (
|
||||||
|
not network_parameters
|
||||||
|
and not username_parameter
|
||||||
|
and not parameter_renames
|
||||||
|
and not body_models
|
||||||
|
):
|
||||||
|
return endpoint
|
||||||
|
|
||||||
|
existing_context_parameter = next(
|
||||||
|
(
|
||||||
|
name
|
||||||
|
for name, parameter in signature.parameters.items()
|
||||||
|
if parameter.annotation is ProjectContext
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
injected_context_name = existing_context_parameter or "_rest_project_context"
|
||||||
|
injected_user_name = "_rest_current_user"
|
||||||
|
|
||||||
|
@wraps(endpoint)
|
||||||
|
async def wrapper(*args, **kwargs):
|
||||||
|
project_context = kwargs.get(injected_context_name)
|
||||||
|
if not isinstance(project_context, ProjectContext):
|
||||||
|
raise RuntimeError("REST project context was not resolved")
|
||||||
|
if not existing_context_parameter:
|
||||||
|
kwargs.pop(injected_context_name, None)
|
||||||
|
for parameter_name in network_parameters:
|
||||||
|
kwargs[parameter_name] = project_context.project_code
|
||||||
|
if username_parameter:
|
||||||
|
kwargs[username_parameter] = kwargs[injected_user_name].username
|
||||||
|
kwargs.pop(injected_user_name, None)
|
||||||
|
for internal_name, public_name in parameter_renames.items():
|
||||||
|
kwargs[internal_name] = kwargs.pop(public_name)
|
||||||
|
for parameter_name, (
|
||||||
|
original_model,
|
||||||
|
_public_model,
|
||||||
|
project_fields,
|
||||||
|
username_fields,
|
||||||
|
) in body_models.items():
|
||||||
|
data = kwargs[parameter_name].model_dump()
|
||||||
|
data.update(
|
||||||
|
{field_name: project_context.project_code for field_name in project_fields}
|
||||||
|
)
|
||||||
|
if username_fields:
|
||||||
|
current_user = kwargs[injected_user_name]
|
||||||
|
data.update(
|
||||||
|
{field_name: current_user.username for field_name in username_fields}
|
||||||
|
)
|
||||||
|
kwargs[parameter_name] = original_model.model_validate(data)
|
||||||
|
if model_has_username:
|
||||||
|
kwargs.pop(injected_user_name, None)
|
||||||
|
result = endpoint(*args, **kwargs)
|
||||||
|
if inspect.isawaitable(result):
|
||||||
|
return await result
|
||||||
|
return result
|
||||||
|
|
||||||
|
parameters = []
|
||||||
|
for name, parameter in signature.parameters.items():
|
||||||
|
if name in network_parameters or name == username_parameter:
|
||||||
|
continue
|
||||||
|
public_name = parameter_renames.get(name, name)
|
||||||
|
if public_name != name:
|
||||||
|
default = copy(parameter.default)
|
||||||
|
default.alias = public_name
|
||||||
|
default.validation_alias = public_name
|
||||||
|
default.serialization_alias = public_name
|
||||||
|
parameter = parameter.replace(name=public_name, default=default)
|
||||||
|
if name in body_models:
|
||||||
|
parameter = parameter.replace(annotation=body_models[name][1])
|
||||||
|
parameters.append(parameter)
|
||||||
|
if not existing_context_parameter:
|
||||||
|
parameters.append(
|
||||||
|
inspect.Parameter(
|
||||||
|
injected_context_name,
|
||||||
|
kind=inspect.Parameter.KEYWORD_ONLY,
|
||||||
|
annotation=ProjectContext,
|
||||||
|
default=Depends(get_project_context),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if username_parameter or model_has_username:
|
||||||
|
parameters.append(
|
||||||
|
inspect.Parameter(
|
||||||
|
injected_user_name,
|
||||||
|
kind=inspect.Parameter.KEYWORD_ONLY,
|
||||||
|
default=Depends(get_current_metadata_user),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
wrapper.__signature__ = signature.replace(parameters=parameters)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def _with_pagination(endpoint):
|
||||||
|
signature = inspect.signature(endpoint)
|
||||||
|
if "limit" in signature.parameters or "offset" in signature.parameters:
|
||||||
|
return endpoint
|
||||||
|
|
||||||
|
@wraps(endpoint)
|
||||||
|
async def wrapper(*args, **kwargs):
|
||||||
|
limit = kwargs.pop("_rest_limit")
|
||||||
|
offset = kwargs.pop("_rest_offset")
|
||||||
|
result = endpoint(*args, **kwargs)
|
||||||
|
if inspect.isawaitable(result):
|
||||||
|
result = await result
|
||||||
|
if not isinstance(result, list):
|
||||||
|
return result
|
||||||
|
return Page(
|
||||||
|
items=result[offset : offset + limit],
|
||||||
|
total=len(result),
|
||||||
|
limit=limit,
|
||||||
|
offset=offset,
|
||||||
|
)
|
||||||
|
|
||||||
|
parameters = list(signature.parameters.values())
|
||||||
|
parameters.extend(
|
||||||
|
[
|
||||||
|
inspect.Parameter(
|
||||||
|
"_rest_limit",
|
||||||
|
kind=inspect.Parameter.KEYWORD_ONLY,
|
||||||
|
annotation=int,
|
||||||
|
default=Query(100, ge=1, le=1000, alias="limit"),
|
||||||
|
),
|
||||||
|
inspect.Parameter(
|
||||||
|
"_rest_offset",
|
||||||
|
kind=inspect.Parameter.KEYWORD_ONLY,
|
||||||
|
annotation=int,
|
||||||
|
default=Query(0, ge=0, alias="offset"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
wrapper.__signature__ = signature.replace(parameters=parameters)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def _adapt_route(route: APIRoute) -> APIRoute:
|
||||||
|
methods = route.methods or set()
|
||||||
|
if len(methods) != 1:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"REST route {route.name!r} must declare exactly one HTTP method"
|
||||||
|
)
|
||||||
|
method = next(iter(methods))
|
||||||
|
responses = dict(route.responses or {})
|
||||||
|
for status_code, description in (
|
||||||
|
(401, "Authentication required"),
|
||||||
|
(403, "Insufficient permission"),
|
||||||
|
(404, "Resource not found"),
|
||||||
|
(409, "Resource conflict"),
|
||||||
|
(422, "Validation error"),
|
||||||
|
(503, "Dependency unavailable"),
|
||||||
|
):
|
||||||
|
responses.setdefault(
|
||||||
|
status_code,
|
||||||
|
{"model": ProblemDetails, "description": description},
|
||||||
|
)
|
||||||
|
|
||||||
|
endpoint = _with_header_project_context(route.endpoint, route.name)
|
||||||
|
response_model = route.response_model
|
||||||
|
if get_origin(response_model) is list:
|
||||||
|
item_type = get_args(response_model)[0] if get_args(response_model) else JsonValue
|
||||||
|
response_model = Page[item_type]
|
||||||
|
endpoint = _with_pagination(endpoint)
|
||||||
|
|
||||||
|
clean_name = _clean_name(route.name)
|
||||||
|
creates_resource = clean_name.startswith(
|
||||||
|
("add_", "create_", "copy_", "import_", "insert_", "store_", "take_", "upload_")
|
||||||
|
) or route.name == "fastapi_pressure_sensor_placement"
|
||||||
|
status_code = (
|
||||||
|
204
|
||||||
|
if method == "DELETE"
|
||||||
|
else 201
|
||||||
|
if method == "POST" and creates_resource
|
||||||
|
else route.status_code
|
||||||
|
)
|
||||||
|
if status_code == 204:
|
||||||
|
response_model = None
|
||||||
|
elif response_model is None:
|
||||||
|
response_model = JsonValue
|
||||||
|
|
||||||
|
return APIRoute(
|
||||||
|
path=route.path,
|
||||||
|
endpoint=endpoint,
|
||||||
|
response_model=response_model,
|
||||||
|
status_code=status_code,
|
||||||
|
tags=route.tags,
|
||||||
|
dependencies=route.dependencies,
|
||||||
|
summary=route.summary,
|
||||||
|
description=route.description,
|
||||||
|
response_description=route.response_description,
|
||||||
|
responses=responses,
|
||||||
|
deprecated=False,
|
||||||
|
name=route.name,
|
||||||
|
methods={method},
|
||||||
|
operation_id=f"{method.lower()}_{re.sub(r'[^a-z0-9]+', '_', route.path).strip('_')}",
|
||||||
|
response_model_include=route.response_model_include,
|
||||||
|
response_model_exclude=route.response_model_exclude,
|
||||||
|
response_model_by_alias=route.response_model_by_alias,
|
||||||
|
response_model_exclude_unset=route.response_model_exclude_unset,
|
||||||
|
response_model_exclude_defaults=route.response_model_exclude_defaults,
|
||||||
|
response_model_exclude_none=route.response_model_exclude_none,
|
||||||
|
include_in_schema=route.include_in_schema,
|
||||||
|
response_class=route.response_class,
|
||||||
|
callbacks=route.callbacks,
|
||||||
|
openapi_extra=route.openapi_extra,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_rest_router(routes: Iterable[Any]) -> APIRouter:
|
||||||
|
router = APIRouter()
|
||||||
|
seen: dict[tuple[str, str], APIRoute] = {}
|
||||||
|
operation_ids: set[str] = set()
|
||||||
|
|
||||||
|
for route in routes:
|
||||||
|
if not isinstance(route, APIRoute):
|
||||||
|
continue
|
||||||
|
|
||||||
|
methods = route.methods or set()
|
||||||
|
if len(methods) != 1:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"REST route {route.name!r} must declare exactly one HTTP method"
|
||||||
|
)
|
||||||
|
method = next(iter(methods))
|
||||||
|
key = (method, route.path)
|
||||||
|
if key in seen:
|
||||||
|
previous = seen[key]
|
||||||
|
raise RuntimeError(
|
||||||
|
"REST route collision for "
|
||||||
|
f"{method} {route.path}: {previous.name!r} and {route.name!r}."
|
||||||
|
)
|
||||||
|
|
||||||
|
adapted = _adapt_route(route)
|
||||||
|
if adapted.operation_id in operation_ids:
|
||||||
|
adapted.operation_id = f"{adapted.operation_id}_{route.name}"
|
||||||
|
seen[key] = route
|
||||||
|
operation_ids.add(adapted.operation_id or "")
|
||||||
|
router.routes.append(adapted)
|
||||||
|
|
||||||
|
return router
|
||||||
|
|
||||||
|
|
||||||
|
api_router = build_rest_router(handler_api_router.routes)
|
||||||
@@ -98,11 +98,10 @@ api_router.include_router(access.router, tags=["Access Control"])
|
|||||||
api_router.include_router(agent_auth.router, tags=["Agent Auth"])
|
api_router.include_router(agent_auth.router, tags=["Agent Auth"])
|
||||||
api_router.include_router(
|
api_router.include_router(
|
||||||
admin_metadata.router,
|
admin_metadata.router,
|
||||||
prefix="/admin",
|
|
||||||
tags=["Metadata Admin"],
|
tags=["Metadata Admin"],
|
||||||
)
|
)
|
||||||
api_router.include_router(model_import.router, tags=["Model Administration"])
|
api_router.include_router(model_import.router, tags=["Model Administration"])
|
||||||
api_router.include_router(audit.router, prefix="/audit", tags=["Audit Logs"])
|
api_router.include_router(audit.router, tags=["Audit Logs"])
|
||||||
api_router.include_router(meta.router, tags=["Metadata"])
|
api_router.include_router(meta.router, tags=["Metadata"])
|
||||||
api_router.include_router(
|
api_router.include_router(
|
||||||
project.router,
|
project.router,
|
||||||
@@ -190,19 +189,16 @@ api_router.include_router(
|
|||||||
)
|
)
|
||||||
api_router.include_router(
|
api_router.include_router(
|
||||||
leakage.router,
|
leakage.router,
|
||||||
prefix="/leakage",
|
|
||||||
tags=["Leakage"],
|
tags=["Leakage"],
|
||||||
dependencies=[burst_run_access],
|
dependencies=[burst_run_access],
|
||||||
)
|
)
|
||||||
api_router.include_router(
|
api_router.include_router(
|
||||||
burst_detection.router,
|
burst_detection.router,
|
||||||
prefix="/burst-detection",
|
|
||||||
tags=["Burst Detection"],
|
tags=["Burst Detection"],
|
||||||
dependencies=[burst_run_access],
|
dependencies=[burst_run_access],
|
||||||
)
|
)
|
||||||
api_router.include_router(
|
api_router.include_router(
|
||||||
burst_location.router,
|
burst_location.router,
|
||||||
prefix="/burst-location",
|
|
||||||
tags=["Burst Location"],
|
tags=["Burst Location"],
|
||||||
dependencies=[burst_run_access],
|
dependencies=[burst_run_access],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ async def get_current_metadata_user(
|
|||||||
)
|
)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
detail=f"Metadata database error: {exc}",
|
detail="Metadata database is unavailable",
|
||||||
) from exc
|
) from exc
|
||||||
if not user or not user.is_active:
|
if not user or not user.is_active:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -80,7 +80,7 @@ async def get_current_metadata_user(
|
|||||||
)
|
)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
detail=f"Metadata database error: {exc}",
|
detail="Metadata database is unavailable",
|
||||||
) from exc
|
) from exc
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ async def resolve_project_context(
|
|||||||
)
|
)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
detail=f"Metadata database error: {exc}",
|
detail="Metadata database is unavailable",
|
||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
return ProjectContext(
|
return ProjectContext(
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ class Settings(BaseSettings):
|
|||||||
PROJECT_NAME: str = "TJWater Server"
|
PROJECT_NAME: str = "TJWater Server"
|
||||||
ENVIRONMENT: str = "production"
|
ENVIRONMENT: str = "production"
|
||||||
API_V1_STR: str = "/api/v1"
|
API_V1_STR: str = "/api/v1"
|
||||||
|
|
||||||
NETWORK_NAME: str = "default_network"
|
NETWORK_NAME: str = "default_network"
|
||||||
|
|
||||||
# 敏感配置加密密钥 (Fernet)
|
# 敏感配置加密密钥 (Fernet)
|
||||||
|
|||||||
+4
-1
@@ -6,7 +6,8 @@ import logging
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import app.services.project_info as project_info
|
import app.services.project_info as project_info
|
||||||
from app.api.v1.router import api_router
|
from app.api.problem_details import install_problem_details_handlers
|
||||||
|
from app.api.v1.rest_router import api_router
|
||||||
from app.infra.db.timescaledb.database import db as tsdb
|
from app.infra.db.timescaledb.database import db as tsdb
|
||||||
from app.infra.db.postgresql.database import db as pgdb
|
from app.infra.db.postgresql.database import db as pgdb
|
||||||
from app.infra.db.dynamic_manager import project_connection_manager
|
from app.infra.db.dynamic_manager import project_connection_manager
|
||||||
@@ -64,11 +65,13 @@ app = FastAPI(
|
|||||||
docs_url=None if is_production else "/docs",
|
docs_url=None if is_production else "/docs",
|
||||||
redoc_url=None if is_production else "/redoc",
|
redoc_url=None if is_production else "/redoc",
|
||||||
openapi_url=None if is_production else "/openapi.json",
|
openapi_url=None if is_production else "/openapi.json",
|
||||||
|
redirect_slashes=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Include Routers
|
# Include Routers
|
||||||
app.include_router(api_router, prefix="/api/v1")
|
app.include_router(api_router, prefix="/api/v1")
|
||||||
|
install_problem_details_handlers(app)
|
||||||
# Legcy Routers without version prefix
|
# Legcy Routers without version prefix
|
||||||
# app.include_router(api_router)
|
# app.include_router(api_router)
|
||||||
|
|
||||||
|
|||||||
@@ -32,24 +32,18 @@ def test_load_auth_context_supports_aliases(monkeypatch):
|
|||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_PROJECT_ID", "p1")
|
monkeypatch.setenv("TJWATER_PROJECT_ID", "p1")
|
||||||
monkeypatch.setenv("TJWATER_USERNAME", "tester")
|
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "net1")
|
|
||||||
|
|
||||||
auth = core.load_auth_context(auth_stdin=False)
|
auth = core.load_auth_context(auth_stdin=False)
|
||||||
|
|
||||||
assert auth.server == "http://server"
|
assert auth.server == "http://server"
|
||||||
assert auth.access_token == "abc"
|
assert auth.access_token == "abc"
|
||||||
assert auth.project_id == "p1"
|
assert auth.project_id == "p1"
|
||||||
assert auth.username == "tester"
|
|
||||||
assert auth.network == "net1"
|
|
||||||
|
|
||||||
|
|
||||||
def test_build_runtime_context_uses_default_server(monkeypatch):
|
def test_build_runtime_context_uses_default_server(monkeypatch):
|
||||||
monkeypatch.delenv("TJWATER_SERVER", raising=False)
|
monkeypatch.delenv("TJWATER_SERVER", raising=False)
|
||||||
monkeypatch.delenv("TJWATER_ACCESS_TOKEN", raising=False)
|
monkeypatch.delenv("TJWATER_ACCESS_TOKEN", raising=False)
|
||||||
monkeypatch.delenv("TJWATER_PROJECT_ID", raising=False)
|
monkeypatch.delenv("TJWATER_PROJECT_ID", raising=False)
|
||||||
monkeypatch.delenv("TJWATER_USERNAME", raising=False)
|
|
||||||
monkeypatch.delenv("TJWATER_NETWORK", raising=False)
|
|
||||||
monkeypatch.delenv("TJWATER_EXTRA_HEADERS", raising=False)
|
monkeypatch.delenv("TJWATER_EXTRA_HEADERS", raising=False)
|
||||||
|
|
||||||
runtime = core.build_runtime_context(
|
runtime = core.build_runtime_context(
|
||||||
@@ -68,7 +62,7 @@ def test_auth_stdin_can_be_reused_with_runtime_context_cache(monkeypatch):
|
|||||||
def fake_request_json(ctx, **kwargs):
|
def fake_request_json(ctx, **kwargs):
|
||||||
observed_runtime_ids.append(id(ctx))
|
observed_runtime_ids.append(id(ctx))
|
||||||
assert ctx.auth.access_token == "token-1"
|
assert ctx.auth.access_token == "token-1"
|
||||||
assert kwargs["params"] == {"network": "tjwater", "junction": "11"}
|
assert kwargs["params"] == {"junction": "11"}
|
||||||
return {"id": "11"}, 5
|
return {"id": "11"}, 5
|
||||||
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
@@ -81,7 +75,6 @@ def test_auth_stdin_can_be_reused_with_runtime_context_cache(monkeypatch):
|
|||||||
"server": "http://server",
|
"server": "http://server",
|
||||||
"access_token": "token-1",
|
"access_token": "token-1",
|
||||||
"project_id": "project-1",
|
"project_id": "project-1",
|
||||||
"network": "tjwater",
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -105,7 +98,6 @@ def test_network_get_junction_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-junction-properties", "--junction", "J1"])
|
result = runner.invoke(app, ["network", "get-junction-properties", "--junction", "J1"])
|
||||||
@@ -116,8 +108,8 @@ def test_network_get_junction_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == {"id": "J1"}
|
assert payload["data"] == {"id": "J1"}
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getjunctionproperties/",
|
"path": "/junctions/properties",
|
||||||
"params": {"network": "tjwater", "junction": "J1"},
|
"params": {"junction": "J1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -132,7 +124,6 @@ def test_network_get_pipe_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-pipe-properties", "--pipe", "P1"])
|
result = runner.invoke(app, ["network", "get-pipe-properties", "--pipe", "P1"])
|
||||||
@@ -143,8 +134,8 @@ def test_network_get_pipe_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == {"id": "P1"}
|
assert payload["data"] == {"id": "P1"}
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getpipeproperties/",
|
"path": "/pipes/properties",
|
||||||
"params": {"network": "tjwater", "pipe": "P1"},
|
"params": {"pipe": "P1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -159,7 +150,6 @@ def test_network_get_all_pipes_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-all-pipes-properties"])
|
result = runner.invoke(app, ["network", "get-all-pipes-properties"])
|
||||||
@@ -170,8 +160,8 @@ def test_network_get_all_pipes_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == [{"id": "P1"}]
|
assert payload["data"] == [{"id": "P1"}]
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getallpipeproperties/",
|
"path": "/pipes",
|
||||||
"params": {"network": "tjwater"},
|
"params": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -186,7 +176,6 @@ def test_network_get_reservoir_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-reservoir-properties", "--reservoir", "R1"])
|
result = runner.invoke(app, ["network", "get-reservoir-properties", "--reservoir", "R1"])
|
||||||
@@ -197,8 +186,8 @@ def test_network_get_reservoir_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == {"id": "R1"}
|
assert payload["data"] == {"id": "R1"}
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getreservoirproperties/",
|
"path": "/reservoirs/properties",
|
||||||
"params": {"network": "tjwater", "reservoir": "R1"},
|
"params": {"reservoir": "R1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -213,7 +202,6 @@ def test_network_get_all_reservoir_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-all-reservoirs-properties"])
|
result = runner.invoke(app, ["network", "get-all-reservoirs-properties"])
|
||||||
@@ -224,8 +212,8 @@ def test_network_get_all_reservoir_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == [{"id": "R1"}]
|
assert payload["data"] == [{"id": "R1"}]
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getallreservoirproperties/",
|
"path": "/reservoirs",
|
||||||
"params": {"network": "tjwater"},
|
"params": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -240,7 +228,6 @@ def test_network_get_tank_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-tank-properties", "--tank", "T1"])
|
result = runner.invoke(app, ["network", "get-tank-properties", "--tank", "T1"])
|
||||||
@@ -251,8 +238,8 @@ def test_network_get_tank_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == {"id": "T1"}
|
assert payload["data"] == {"id": "T1"}
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/gettankproperties/",
|
"path": "/tanks/properties",
|
||||||
"params": {"network": "tjwater", "tank": "T1"},
|
"params": {"tank": "T1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -267,7 +254,6 @@ def test_network_get_all_tank_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-all-tanks-properties"])
|
result = runner.invoke(app, ["network", "get-all-tanks-properties"])
|
||||||
@@ -278,8 +264,8 @@ def test_network_get_all_tank_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == [{"id": "T1"}]
|
assert payload["data"] == [{"id": "T1"}]
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getalltankproperties/",
|
"path": "/tanks",
|
||||||
"params": {"network": "tjwater"},
|
"params": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -294,7 +280,6 @@ def test_network_get_pump_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-pump-properties", "--pump", "PU1"])
|
result = runner.invoke(app, ["network", "get-pump-properties", "--pump", "PU1"])
|
||||||
@@ -305,8 +290,8 @@ def test_network_get_pump_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == {"id": "PU1"}
|
assert payload["data"] == {"id": "PU1"}
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getpumpproperties/",
|
"path": "/pumps/properties",
|
||||||
"params": {"network": "tjwater", "pump": "PU1"},
|
"params": {"pump": "PU1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -321,7 +306,6 @@ def test_network_get_all_pump_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-all-pumps-properties"])
|
result = runner.invoke(app, ["network", "get-all-pumps-properties"])
|
||||||
@@ -332,8 +316,8 @@ def test_network_get_all_pump_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == [{"id": "PU1"}]
|
assert payload["data"] == [{"id": "PU1"}]
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getallpumpproperties/",
|
"path": "/pumps",
|
||||||
"params": {"network": "tjwater"},
|
"params": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -348,7 +332,6 @@ def test_network_get_valve_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-valve-properties", "--valve", "V1"])
|
result = runner.invoke(app, ["network", "get-valve-properties", "--valve", "V1"])
|
||||||
@@ -359,8 +342,8 @@ def test_network_get_valve_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == {"id": "V1"}
|
assert payload["data"] == {"id": "V1"}
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getvalveproperties/",
|
"path": "/valves/properties",
|
||||||
"params": {"network": "tjwater", "valve": "V1"},
|
"params": {"valve": "V1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -375,7 +358,6 @@ def test_network_get_all_valve_properties_uses_network_context(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "tjwater")
|
|
||||||
monkeypatch.setattr(common, "request_json", fake_request_json)
|
monkeypatch.setattr(common, "request_json", fake_request_json)
|
||||||
|
|
||||||
result = runner.invoke(app, ["network", "get-all-valves-properties"])
|
result = runner.invoke(app, ["network", "get-all-valves-properties"])
|
||||||
@@ -386,8 +368,8 @@ def test_network_get_all_valve_properties_uses_network_context(monkeypatch):
|
|||||||
assert payload["data"] == [{"id": "V1"}]
|
assert payload["data"] == [{"id": "V1"}]
|
||||||
assert captured == {
|
assert captured == {
|
||||||
"access_token": "abc",
|
"access_token": "abc",
|
||||||
"path": "/getallvalveproperties/",
|
"path": "/valves",
|
||||||
"params": {"network": "tjwater"},
|
"params": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -525,7 +507,6 @@ def test_realtime_property_help_lists_supported_fields():
|
|||||||
def test_analysis_burst_returns_next_step_to_fetch_scheme(monkeypatch, tmp_path: Path):
|
def test_analysis_burst_returns_next_step_to_fetch_scheme(monkeypatch, tmp_path: Path):
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
|
||||||
burst_path = tmp_path / "burst.json"
|
burst_path = tmp_path / "burst.json"
|
||||||
burst_path.write_text('[{"id":"P1","size":3.5}]', encoding="utf-8")
|
burst_path.write_text('[{"id":"P1","size":3.5}]', encoding="utf-8")
|
||||||
|
|
||||||
@@ -559,7 +540,6 @@ def test_analysis_burst_returns_next_step_to_fetch_scheme(monkeypatch, tmp_path:
|
|||||||
def test_analysis_contaminant_sends_required_scheme_name(monkeypatch):
|
def test_analysis_contaminant_sends_required_scheme_name(monkeypatch):
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
|
||||||
captured = {}
|
captured = {}
|
||||||
|
|
||||||
def fake_request(**kwargs):
|
def fake_request(**kwargs):
|
||||||
@@ -588,7 +568,6 @@ def test_analysis_contaminant_sends_required_scheme_name(monkeypatch):
|
|||||||
|
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert captured["params"] == {
|
assert captured["params"] == {
|
||||||
"network": "demo",
|
|
||||||
"start_time": "2025-01-02T03:04:05+08:00",
|
"start_time": "2025-01-02T03:04:05+08:00",
|
||||||
"source": "N1",
|
"source": "N1",
|
||||||
"concentration": 10.0,
|
"concentration": 10.0,
|
||||||
@@ -600,7 +579,6 @@ def test_analysis_contaminant_sends_required_scheme_name(monkeypatch):
|
|||||||
def test_analysis_flushing_sends_required_scheme_name(monkeypatch, tmp_path: Path):
|
def test_analysis_flushing_sends_required_scheme_name(monkeypatch, tmp_path: Path):
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
|
||||||
captured = {}
|
captured = {}
|
||||||
valve_path = tmp_path / "valve.json"
|
valve_path = tmp_path / "valve.json"
|
||||||
valve_path.write_text('[{"valve":"V1","opening":0.5}]', encoding="utf-8")
|
valve_path.write_text('[{"valve":"V1","opening":0.5}]', encoding="utf-8")
|
||||||
@@ -633,11 +611,10 @@ def test_analysis_flushing_sends_required_scheme_name(monkeypatch, tmp_path: Pat
|
|||||||
|
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert captured["params"] == {
|
assert captured["params"] == {
|
||||||
"network": "demo",
|
|
||||||
"start_time": "2025-01-02T03:04:05+08:00",
|
"start_time": "2025-01-02T03:04:05+08:00",
|
||||||
"valves": ["V1"],
|
"valves": ["V1"],
|
||||||
"valves_k": [0.5],
|
"valves_k": [0.5],
|
||||||
"drainage_node_ID": "N1",
|
"drainage_node_id": "N1",
|
||||||
"flush_flow": 100.0,
|
"flush_flow": 100.0,
|
||||||
"duration": 900,
|
"duration": 900,
|
||||||
"scheme_name": "flush_case_01",
|
"scheme_name": "flush_case_01",
|
||||||
@@ -647,7 +624,6 @@ def test_analysis_flushing_sends_required_scheme_name(monkeypatch, tmp_path: Pat
|
|||||||
def test_analysis_valve_close_sends_required_scheme_name(monkeypatch):
|
def test_analysis_valve_close_sends_required_scheme_name(monkeypatch):
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
|
||||||
captured = {}
|
captured = {}
|
||||||
|
|
||||||
def fake_request(**kwargs):
|
def fake_request(**kwargs):
|
||||||
@@ -676,7 +652,6 @@ def test_analysis_valve_close_sends_required_scheme_name(monkeypatch):
|
|||||||
|
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert captured["params"] == {
|
assert captured["params"] == {
|
||||||
"network": "demo",
|
|
||||||
"start_time": "2025-01-02T03:04:05+08:00",
|
"start_time": "2025-01-02T03:04:05+08:00",
|
||||||
"valves": ["V1"],
|
"valves": ["V1"],
|
||||||
"duration": 900,
|
"duration": 900,
|
||||||
@@ -687,7 +662,6 @@ def test_analysis_valve_close_sends_required_scheme_name(monkeypatch):
|
|||||||
def test_analysis_contaminant_requires_scheme(monkeypatch, capsys):
|
def test_analysis_contaminant_requires_scheme(monkeypatch, capsys):
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
|
||||||
|
|
||||||
exit_code = main(
|
exit_code = main(
|
||||||
[
|
[
|
||||||
@@ -713,7 +687,6 @@ def test_analysis_contaminant_requires_scheme(monkeypatch, capsys):
|
|||||||
def test_analysis_flushing_requires_scheme(monkeypatch, tmp_path: Path, capsys):
|
def test_analysis_flushing_requires_scheme(monkeypatch, tmp_path: Path, capsys):
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
|
||||||
valve_path = tmp_path / "valve.json"
|
valve_path = tmp_path / "valve.json"
|
||||||
valve_path.write_text('[{"valve":"V1","opening":0.5}]', encoding="utf-8")
|
valve_path.write_text('[{"valve":"V1","opening":0.5}]', encoding="utf-8")
|
||||||
|
|
||||||
@@ -741,7 +714,6 @@ def test_analysis_flushing_requires_scheme(monkeypatch, tmp_path: Path, capsys):
|
|||||||
def test_analysis_valve_close_requires_scheme(monkeypatch, capsys):
|
def test_analysis_valve_close_requires_scheme(monkeypatch, capsys):
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
|
||||||
|
|
||||||
exit_code = main(
|
exit_code = main(
|
||||||
[
|
[
|
||||||
@@ -921,7 +893,6 @@ def test_main_bare_analysis_returns_typer_help_without_json_error(capsys):
|
|||||||
def test_simulation_run_translates_rfc3339(monkeypatch):
|
def test_simulation_run_translates_rfc3339(monkeypatch):
|
||||||
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
monkeypatch.setenv("TJWATER_SERVER", "http://server")
|
||||||
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
monkeypatch.setenv("TJWATER_ACCESS_TOKEN", "abc")
|
||||||
monkeypatch.setenv("TJWATER_NETWORK", "demo")
|
|
||||||
captured = {}
|
captured = {}
|
||||||
|
|
||||||
def fake_request(**kwargs):
|
def fake_request(**kwargs):
|
||||||
@@ -944,7 +915,6 @@ def test_simulation_run_translates_rfc3339(monkeypatch):
|
|||||||
|
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert captured["json"] == {
|
assert captured["json"] == {
|
||||||
"name": "demo",
|
|
||||||
"start_time": "2025-01-02T03:04:05+08:00",
|
"start_time": "2025-01-02T03:04:05+08:00",
|
||||||
"duration": 30,
|
"duration": 30,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ from .core import (
|
|||||||
parse_time_with_timezone,
|
parse_time_with_timezone,
|
||||||
parse_valve_setting_file,
|
parse_valve_setting_file,
|
||||||
request_json,
|
request_json,
|
||||||
require_network,
|
|
||||||
require_username,
|
|
||||||
resolve_scheme,
|
resolve_scheme,
|
||||||
)
|
)
|
||||||
from .option_types import DataSource, ValveMode
|
from .option_types import DataSource, ValveMode
|
||||||
@@ -41,11 +39,9 @@ def simulation_run(
|
|||||||
duration: Annotated[int, typer.Option("--duration", help="持续分钟数")],
|
duration: Annotated[int, typer.Option("--duration", help="持续分钟数")],
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
network = require_network(runtime)
|
|
||||||
parsed = parse_time_with_timezone(start_time, option_name="--start-time")
|
parsed = parse_time_with_timezone(start_time, option_name="--start-time")
|
||||||
end_time = (parsed + timedelta(minutes=duration)).isoformat()
|
end_time = (parsed + timedelta(minutes=duration)).isoformat()
|
||||||
body = {
|
body = {
|
||||||
"name": network,
|
|
||||||
"start_time": parsed.replace(microsecond=0).isoformat(),
|
"start_time": parsed.replace(microsecond=0).isoformat(),
|
||||||
"duration": duration,
|
"duration": duration,
|
||||||
}
|
}
|
||||||
@@ -53,10 +49,9 @@ def simulation_run(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="触发模拟成功",
|
summary="触发模拟成功",
|
||||||
method="POST",
|
method="POST",
|
||||||
path="/simulations/run-by-date",
|
path="/simulation-runs",
|
||||||
json_body=body,
|
json_body=body,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
next_commands=[
|
next_commands=[
|
||||||
f"tjwater-cli data timeseries realtime links --start-time {parsed.isoformat()} --end-time {end_time}",
|
f"tjwater-cli data timeseries realtime links --start-time {parsed.isoformat()} --end-time {end_time}",
|
||||||
f"tjwater-cli data timeseries realtime nodes --start-time {parsed.isoformat()} --end-time {end_time}",
|
f"tjwater-cli data timeseries realtime nodes --start-time {parsed.isoformat()} --end-time {end_time}",
|
||||||
@@ -76,9 +71,8 @@ def analysis_burst(
|
|||||||
ids, sizes = parse_burst_file(burst_file)
|
ids, sizes = parse_burst_file(burst_file)
|
||||||
scheme_name = resolve_scheme(runtime, scheme, required=True)
|
scheme_name = resolve_scheme(runtime, scheme, required=True)
|
||||||
params = {
|
params = {
|
||||||
"network": require_network(runtime),
|
|
||||||
"modify_pattern_start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"modify_pattern_start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
"burst_ID": ids,
|
"burst_id": ids,
|
||||||
"burst_size": sizes,
|
"burst_size": sizes,
|
||||||
"modify_total_duration": duration,
|
"modify_total_duration": duration,
|
||||||
"scheme_name": scheme_name,
|
"scheme_name": scheme_name,
|
||||||
@@ -86,11 +80,10 @@ def analysis_burst(
|
|||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="爆管分析执行成功",
|
summary="爆管分析执行成功",
|
||||||
method="GET",
|
method="POST",
|
||||||
path="/burst-analysis",
|
path="/burst-analyses",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
next_commands=[
|
next_commands=[
|
||||||
f"tjwater-cli data scheme get --name {scheme_name}",
|
f"tjwater-cli data scheme get --name {scheme_name}",
|
||||||
"tjwater-cli data scheme list",
|
"tjwater-cli data scheme list",
|
||||||
@@ -110,7 +103,6 @@ def analysis_valve(
|
|||||||
scheme: Annotated[str | None, typer.Option("--scheme", help="close 模式的方案名称")] = None,
|
scheme: Annotated[str | None, typer.Option("--scheme", help="close 模式的方案名称")] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
network = require_network(runtime)
|
|
||||||
if mode == ValveMode.CLOSE:
|
if mode == ValveMode.CLOSE:
|
||||||
if not start_time or not valve:
|
if not start_time or not valve:
|
||||||
raise CLIError(
|
raise CLIError(
|
||||||
@@ -120,7 +112,6 @@ def analysis_valve(
|
|||||||
exit_code=2,
|
exit_code=2,
|
||||||
)
|
)
|
||||||
params = {
|
params = {
|
||||||
"network": network,
|
|
||||||
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
"valves": valve,
|
"valves": valve,
|
||||||
"duration": duration or 900,
|
"duration": duration or 900,
|
||||||
@@ -129,11 +120,10 @@ def analysis_valve(
|
|||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="阀门关闭分析执行成功",
|
summary="阀门关闭分析执行成功",
|
||||||
method="GET",
|
method="POST",
|
||||||
path="/valve_close_analysis/",
|
path="/valve-isolation-analyses",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if mode == ValveMode.ISOLATION:
|
if mode == ValveMode.ISOLATION:
|
||||||
@@ -144,17 +134,16 @@ def analysis_valve(
|
|||||||
message="isolation mode requires at least one --element",
|
message="isolation mode requires at least one --element",
|
||||||
exit_code=2,
|
exit_code=2,
|
||||||
)
|
)
|
||||||
params = {"network": network, "accident_element": element}
|
params = {"accident_element": element}
|
||||||
if disabled_valve:
|
if disabled_valve:
|
||||||
params["disabled_valves"] = disabled_valve
|
params["disabled_valves"] = disabled_valve
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="阀门隔离分析执行成功",
|
summary="阀门隔离分析执行成功",
|
||||||
method="GET",
|
method="POST",
|
||||||
path="/valve-isolation-analysis",
|
path="/valve-isolation-analyses",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
raise AssertionError(f"unreachable valve mode: {mode}")
|
raise AssertionError(f"unreachable valve mode: {mode}")
|
||||||
@@ -173,11 +162,10 @@ def analysis_flushing(
|
|||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
valves, openings = parse_valve_setting_file(valve_setting_file)
|
valves, openings = parse_valve_setting_file(valve_setting_file)
|
||||||
params = {
|
params = {
|
||||||
"network": require_network(runtime),
|
|
||||||
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
"valves": valves,
|
"valves": valves,
|
||||||
"valves_k": openings,
|
"valves_k": openings,
|
||||||
"drainage_node_ID": drainage_node,
|
"drainage_node_id": drainage_node,
|
||||||
"flush_flow": flow,
|
"flush_flow": flow,
|
||||||
"duration": duration or 900,
|
"duration": duration or 900,
|
||||||
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
||||||
@@ -185,11 +173,10 @@ def analysis_flushing(
|
|||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="冲洗分析执行成功",
|
summary="冲洗分析执行成功",
|
||||||
method="GET",
|
method="POST",
|
||||||
path="/flushing-analysis",
|
path="/flushing-analyses",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -203,15 +190,13 @@ def analysis_age(
|
|||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="水龄分析执行成功",
|
summary="水龄分析执行成功",
|
||||||
method="GET",
|
method="POST",
|
||||||
path="/age_analysis/",
|
path="/water-age-analyses",
|
||||||
params={
|
params={
|
||||||
"network": require_network(runtime),
|
|
||||||
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
"duration": duration,
|
"duration": duration,
|
||||||
},
|
},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -227,7 +212,6 @@ def analysis_contaminant(
|
|||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
params = {
|
params = {
|
||||||
"network": require_network(runtime),
|
|
||||||
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
"source": source_node,
|
"source": source_node,
|
||||||
"concentration": concentration,
|
"concentration": concentration,
|
||||||
@@ -239,11 +223,10 @@ def analysis_contaminant(
|
|||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="污染物模拟执行成功",
|
summary="污染物模拟执行成功",
|
||||||
method="GET",
|
method="POST",
|
||||||
path="/contaminant-simulation",
|
path="/contaminant-simulations",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -256,21 +239,17 @@ def analysis_sensor_placement_kmeans(
|
|||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
body = {
|
body = {
|
||||||
"name": require_network(runtime),
|
|
||||||
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
||||||
"sensor_number": count,
|
"sensor_number": count,
|
||||||
"min_diameter": min_diameter,
|
"min_diameter": min_diameter,
|
||||||
"username": require_username(runtime),
|
|
||||||
}
|
}
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="传感器选址执行成功",
|
summary="传感器选址执行成功",
|
||||||
method="POST",
|
method="POST",
|
||||||
path="/pressure_sensor_placement_kmeans/",
|
path="/pressure-sensor-placement-kmeans",
|
||||||
json_body=body,
|
json_body=body,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
require_username_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -283,7 +262,6 @@ def analysis_leakage_identify(
|
|||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
body = {
|
body = {
|
||||||
"network": require_network(runtime),
|
|
||||||
"scada_start": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"scada_start": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
"scada_end": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
"scada_end": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
||||||
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
||||||
@@ -292,10 +270,9 @@ def analysis_leakage_identify(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="漏损识别执行成功",
|
summary="漏损识别执行成功",
|
||||||
method="POST",
|
method="POST",
|
||||||
path="/leakage/identify/",
|
path="/leakage-identifications",
|
||||||
json_body=body,
|
json_body=body,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -308,11 +285,9 @@ def analysis_leakage_schemes_list(ctx: typer.Context) -> None:
|
|||||||
method="GET",
|
method="GET",
|
||||||
path="/schemes",
|
path="/schemes",
|
||||||
params={
|
params={
|
||||||
"network": require_network(runtime),
|
|
||||||
"scheme_type": "dma_leak_identification",
|
"scheme_type": "dma_leak_identification",
|
||||||
},
|
},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -328,11 +303,9 @@ def analysis_leakage_schemes_get(
|
|||||||
method="GET",
|
method="GET",
|
||||||
path=f"/schemes/{scheme_name}",
|
path=f"/schemes/{scheme_name}",
|
||||||
params={
|
params={
|
||||||
"network": require_network(runtime),
|
|
||||||
"scheme_type": "dma_leak_identification",
|
"scheme_type": "dma_leak_identification",
|
||||||
},
|
},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -345,7 +318,6 @@ def analysis_burst_detection_detect(
|
|||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
body = {
|
body = {
|
||||||
"network": require_network(runtime),
|
|
||||||
"scada_start": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"scada_start": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
"scada_end": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
"scada_end": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
||||||
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
||||||
@@ -354,10 +326,9 @@ def analysis_burst_detection_detect(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="爆管检测执行成功",
|
summary="爆管检测执行成功",
|
||||||
method="POST",
|
method="POST",
|
||||||
path="/burst-detection/detect/",
|
path="/burst-detections",
|
||||||
json_body=body,
|
json_body=body,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -370,11 +341,9 @@ def analysis_burst_detection_schemes_list(ctx: typer.Context) -> None:
|
|||||||
method="GET",
|
method="GET",
|
||||||
path="/schemes",
|
path="/schemes",
|
||||||
params={
|
params={
|
||||||
"network": require_network(runtime),
|
|
||||||
"scheme_type": "burst_detection",
|
"scheme_type": "burst_detection",
|
||||||
},
|
},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -390,11 +359,9 @@ def analysis_burst_detection_schemes_get(
|
|||||||
method="GET",
|
method="GET",
|
||||||
path=f"/schemes/{scheme_name}",
|
path=f"/schemes/{scheme_name}",
|
||||||
params={
|
params={
|
||||||
"network": require_network(runtime),
|
|
||||||
"scheme_type": "burst_detection",
|
"scheme_type": "burst_detection",
|
||||||
},
|
},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -416,7 +383,6 @@ def analysis_burst_location_locate(
|
|||||||
pressure_payload = parse_optional_dataset_file(pressure_file, label="pressure") or {}
|
pressure_payload = parse_optional_dataset_file(pressure_file, label="pressure") or {}
|
||||||
flow_payload = parse_optional_dataset_file(flow_file, label="flow") or {}
|
flow_payload = parse_optional_dataset_file(flow_file, label="flow") or {}
|
||||||
body = {
|
body = {
|
||||||
"network": require_network(runtime),
|
|
||||||
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
||||||
"data_source": data_source.value,
|
"data_source": data_source.value,
|
||||||
"scada_burst_start": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"scada_burst_start": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
@@ -436,10 +402,9 @@ def analysis_burst_location_locate(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="爆管定位执行成功",
|
summary="爆管定位执行成功",
|
||||||
method="POST",
|
method="POST",
|
||||||
path="/burst-location/locate/",
|
path="/burst-locations",
|
||||||
json_body=body,
|
json_body=body,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -452,11 +417,9 @@ def analysis_burst_location_schemes_list(ctx: typer.Context) -> None:
|
|||||||
method="GET",
|
method="GET",
|
||||||
path="/schemes",
|
path="/schemes",
|
||||||
params={
|
params={
|
||||||
"network": require_network(runtime),
|
|
||||||
"scheme_type": "burst_location",
|
"scheme_type": "burst_location",
|
||||||
},
|
},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -472,11 +435,9 @@ def analysis_burst_location_schemes_get(
|
|||||||
method="GET",
|
method="GET",
|
||||||
path=f"/schemes/{scheme_name}",
|
path=f"/schemes/{scheme_name}",
|
||||||
params={
|
params={
|
||||||
"network": require_network(runtime),
|
|
||||||
"scheme_type": "burst_location",
|
"scheme_type": "burst_location",
|
||||||
},
|
},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -490,10 +451,9 @@ def analysis_risk_pipe_now(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取当前管道风险成功",
|
summary="读取当前管道风险成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getpiperiskprobabilitynow/",
|
path="/pipes/risk-probability-now",
|
||||||
params={"network": require_network(runtime), "pipe_id": pipe},
|
params={"pipe_id": pipe},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -507,32 +467,26 @@ def analysis_risk_pipe_history(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取历史管道风险成功",
|
summary="读取历史管道风险成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getpiperiskprobability/",
|
path="/pipes/risk-probability",
|
||||||
params={"network": require_network(runtime), "pipe_id": pipe},
|
params={"pipe_id": pipe},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@analysis_risk_app.command("network")
|
@analysis_risk_app.command("network")
|
||||||
def analysis_risk_network(ctx: typer.Context) -> None:
|
def analysis_risk_network(ctx: typer.Context) -> None:
|
||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
network = require_network(runtime)
|
|
||||||
probabilities, duration_prob = request_json(
|
probabilities, duration_prob = request_json(
|
||||||
runtime,
|
runtime,
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getnetworkpiperiskprobabilitynow/",
|
path="/network-pipe-risk-probability-nows",
|
||||||
params={"network": network},
|
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
geometries, duration_geo = request_json(
|
geometries, duration_geo = request_json(
|
||||||
runtime,
|
runtime,
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getpiperiskprobabilitygeometries/",
|
path="/pipes/risk-probability-geometries",
|
||||||
params={"network": network},
|
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
emit_success(
|
emit_success(
|
||||||
summary="读取全网风险成功",
|
summary="读取全网风险成功",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from .apps import (
|
|||||||
data_timeseries_scheme_app,
|
data_timeseries_scheme_app,
|
||||||
)
|
)
|
||||||
from .common import emit_api, runtime_context
|
from .common import emit_api, runtime_context
|
||||||
from .core import CLIError, parse_time_with_timezone, require_network, resolve_scheme
|
from .core import CLIError, parse_time_with_timezone, resolve_scheme
|
||||||
from .option_types import (
|
from .option_types import (
|
||||||
CompositeKind,
|
CompositeKind,
|
||||||
ElementType,
|
ElementType,
|
||||||
@@ -73,7 +73,7 @@ def data_realtime_links(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取实时管道数据成功",
|
summary="读取实时管道数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/realtime/links",
|
path="/timeseries/realtime/links",
|
||||||
params={
|
params={
|
||||||
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
"end_time": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
"end_time": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
||||||
@@ -93,7 +93,7 @@ def data_realtime_nodes(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取实时节点数据成功",
|
summary="读取实时节点数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/realtime/nodes",
|
path="/timeseries/realtime/nodes",
|
||||||
params={
|
params={
|
||||||
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
"end_time": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
"end_time": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
||||||
@@ -114,7 +114,7 @@ def data_realtime_simulation_by_id_time(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取实时模拟数据成功",
|
summary="读取实时模拟数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/realtime/query/by-id-time",
|
path="/timeseries/realtime/simulation-results",
|
||||||
params={
|
params={
|
||||||
"id": id,
|
"id": id,
|
||||||
"type": type.value,
|
"type": type.value,
|
||||||
@@ -137,7 +137,7 @@ def data_realtime_simulation_by_time_property(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取实时属性聚合数据成功",
|
summary="读取实时属性聚合数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/realtime/query/by-time-property",
|
path="/timeseries/realtime/records",
|
||||||
params={
|
params={
|
||||||
"type": type.value,
|
"type": type.value,
|
||||||
"query_time": parse_time_with_timezone(time, option_name="--time").isoformat(),
|
"query_time": parse_time_with_timezone(time, option_name="--time").isoformat(),
|
||||||
@@ -161,7 +161,7 @@ def data_scheme_links(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取方案管道数据成功",
|
summary="读取方案管道数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/scheme/links",
|
path="/timeseries/schemes/links",
|
||||||
params={
|
params={
|
||||||
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
||||||
"scheme_type": _scheme_type_option(scheme_type),
|
"scheme_type": _scheme_type_option(scheme_type),
|
||||||
@@ -189,7 +189,7 @@ def data_scheme_node_field(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取方案节点字段成功",
|
summary="读取方案节点字段成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path=f"/scheme/nodes/{node}/field",
|
path=f"/timeseries/schemes/nodes/{node}/field",
|
||||||
params={
|
params={
|
||||||
"field": field,
|
"field": field,
|
||||||
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
"scheme_name": resolve_scheme(runtime, scheme, required=True),
|
||||||
@@ -233,7 +233,7 @@ def data_scheme_simulation(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取方案单点模拟数据成功",
|
summary="读取方案单点模拟数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/scheme/query/by-id-time",
|
path="/timeseries/schemes/simulation-results",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_project=True,
|
require_project=True,
|
||||||
@@ -253,7 +253,7 @@ def data_scheme_simulation(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取方案属性聚合数据成功",
|
summary="读取方案属性聚合数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/scheme/query/by-scheme-time-property",
|
path="/timeseries/schemes/records",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_project=True,
|
require_project=True,
|
||||||
@@ -270,7 +270,7 @@ def data_scada_query(
|
|||||||
end_time: Annotated[str, typer.Option("--end-time", help="结束时间")],
|
end_time: Annotated[str, typer.Option("--end-time", help="结束时间")],
|
||||||
field: Annotated[str | None, typer.Option("--field", help="字段名,仅支持 monitored_value|cleaned_value")] = None,
|
field: Annotated[str | None, typer.Option("--field", help="字段名,仅支持 monitored_value|cleaned_value")] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
path = "/scada/by-ids-field-time-range" if field else "/scada/by-ids-time-range"
|
path = "/timeseries/scada-readings/fields" if field else "/timeseries/scada-readings"
|
||||||
params = {
|
params = {
|
||||||
"device_ids": ",".join(device_id),
|
"device_ids": ",".join(device_id),
|
||||||
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
"start_time": parse_time_with_timezone(start_time, option_name="--start-time").isoformat(),
|
||||||
@@ -334,7 +334,7 @@ def data_timeseries_composite(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取复合 SCADA-模拟数据成功",
|
summary="读取复合 SCADA-模拟数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/composite/scada-simulation",
|
path="/timeseries/views/scada-simulations",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_project=True,
|
require_project=True,
|
||||||
@@ -357,7 +357,7 @@ def data_timeseries_composite(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取复合元素模拟数据成功",
|
summary="读取复合元素模拟数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/composite/element-simulation",
|
path="/timeseries/views/element-simulations",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_project=True,
|
require_project=True,
|
||||||
@@ -377,7 +377,7 @@ def data_timeseries_composite(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取元素关联 SCADA 数据成功",
|
summary="读取元素关联 SCADA 数据成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/composite/element-scada",
|
path="/timeseries/views/element-scada-readings",
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_project=True,
|
require_project=True,
|
||||||
@@ -398,21 +398,19 @@ def data_composite_pipeline_health(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取管道健康预测成功",
|
summary="读取管道健康预测成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/composite/pipeline-health-prediction",
|
path="/pipeline-health-predictions",
|
||||||
params={
|
params={
|
||||||
"network_name": require_network(runtime_context(ctx)),
|
|
||||||
"query_time": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
"query_time": parse_time_with_timezone(end_time, option_name="--end-time").isoformat(),
|
||||||
},
|
},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_project=True,
|
require_project=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _scada_mapping(kind: str, action: str) -> tuple[str, dict[str, str]]:
|
def _scada_mapping(kind: str, action: str) -> tuple[str, dict[str, str]]:
|
||||||
mapping = {
|
mapping = {
|
||||||
("info", "get"): ("/getscadainfo/", {"id_param": "id"}),
|
("info", "get"): ("/scada-info/detail", {"id_param": "id"}),
|
||||||
("info", "list"): ("/getallscadainfo/", {}),
|
("info", "list"): ("/scada-info", {}),
|
||||||
}
|
}
|
||||||
result = mapping.get((kind, action))
|
result = mapping.get((kind, action))
|
||||||
if result is None:
|
if result is None:
|
||||||
@@ -433,7 +431,7 @@ def data_scada_get(
|
|||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
path, meta = _scada_mapping(kind.value, "get")
|
path, meta = _scada_mapping(kind.value, "get")
|
||||||
params = {"network": require_network(runtime), meta["id_param"]: id}
|
params = {meta["id_param"]: id}
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取 SCADA 数据成功",
|
summary="读取 SCADA 数据成功",
|
||||||
@@ -441,7 +439,6 @@ def data_scada_get(
|
|||||||
path=path,
|
path=path,
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -457,9 +454,7 @@ def data_scada_list(
|
|||||||
summary="读取 SCADA 列表成功",
|
summary="读取 SCADA 列表成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path=path,
|
path=path,
|
||||||
params={"network": require_network(runtime)},
|
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -470,10 +465,8 @@ def data_scheme_schema(ctx: typer.Context) -> None:
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取方案 schema 成功",
|
summary="读取方案 schema 成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getschemeschema/",
|
path="/network-schemas/scheme",
|
||||||
params={"network": require_network(runtime)},
|
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -487,10 +480,9 @@ def data_scheme_get(
|
|||||||
ctx,
|
ctx,
|
||||||
summary="读取方案成功",
|
summary="读取方案成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getscheme/",
|
path="/schemes/detail",
|
||||||
params={"network": require_network(runtime), "schema_name": name},
|
params={"schema_name": name},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -502,7 +494,5 @@ def data_scheme_list(ctx: typer.Context) -> None:
|
|||||||
summary="读取方案列表成功",
|
summary="读取方案列表成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/schemes",
|
path="/schemes",
|
||||||
params={"network": require_network(runtime)},
|
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ from typing import Annotated
|
|||||||
import typer
|
import typer
|
||||||
|
|
||||||
from .apps import component_option_app, network_app
|
from .apps import component_option_app, network_app
|
||||||
from .common import emit_api, runtime_context
|
from .common import emit_api
|
||||||
from .core import CLIError, require_network
|
from .core import CLIError
|
||||||
from .option_types import ComponentOptionKind
|
from .option_types import ComponentOptionKind
|
||||||
|
|
||||||
|
|
||||||
@@ -15,15 +15,13 @@ def network_get_junction_properties(
|
|||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
junction: Annotated[str, typer.Option("--junction", help="节点 ID")],
|
junction: Annotated[str, typer.Option("--junction", help="节点 ID")],
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取节点属性成功",
|
summary="读取节点属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getjunctionproperties/",
|
path="/junctions/properties",
|
||||||
params={"network": require_network(runtime), "junction": junction},
|
params={"junction": junction},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -32,29 +30,25 @@ def network_get_pipe_properties(
|
|||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
pipe: Annotated[str, typer.Option("--pipe", help="管道 ID")],
|
pipe: Annotated[str, typer.Option("--pipe", help="管道 ID")],
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取管道属性成功",
|
summary="读取管道属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getpipeproperties/",
|
path="/pipes/properties",
|
||||||
params={"network": require_network(runtime), "pipe": pipe},
|
params={"pipe": pipe},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@network_app.command("get-all-pipes-properties")
|
@network_app.command("get-all-pipes-properties")
|
||||||
def network_get_all_pipes_properties(ctx: typer.Context) -> None:
|
def network_get_all_pipes_properties(ctx: typer.Context) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取全部管道属性成功",
|
summary="读取全部管道属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getallpipeproperties/",
|
path="/pipes",
|
||||||
params={"network": require_network(runtime)},
|
params={},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -63,29 +57,25 @@ def network_get_reservoir_properties(
|
|||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
reservoir: Annotated[str, typer.Option("--reservoir", help="水库 ID")],
|
reservoir: Annotated[str, typer.Option("--reservoir", help="水库 ID")],
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取水库属性成功",
|
summary="读取水库属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getreservoirproperties/",
|
path="/reservoirs/properties",
|
||||||
params={"network": require_network(runtime), "reservoir": reservoir},
|
params={"reservoir": reservoir},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@network_app.command("get-all-reservoirs-properties")
|
@network_app.command("get-all-reservoirs-properties")
|
||||||
def network_get_all_reservoir_properties(ctx: typer.Context) -> None:
|
def network_get_all_reservoir_properties(ctx: typer.Context) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取全部水库属性成功",
|
summary="读取全部水库属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getallreservoirproperties/",
|
path="/reservoirs",
|
||||||
params={"network": require_network(runtime)},
|
params={},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -94,29 +84,25 @@ def network_get_tank_properties(
|
|||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
tank: Annotated[str, typer.Option("--tank", help="水箱 ID")],
|
tank: Annotated[str, typer.Option("--tank", help="水箱 ID")],
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取水箱属性成功",
|
summary="读取水箱属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/gettankproperties/",
|
path="/tanks/properties",
|
||||||
params={"network": require_network(runtime), "tank": tank},
|
params={"tank": tank},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@network_app.command("get-all-tanks-properties")
|
@network_app.command("get-all-tanks-properties")
|
||||||
def network_get_all_tank_properties(ctx: typer.Context) -> None:
|
def network_get_all_tank_properties(ctx: typer.Context) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取全部水箱属性成功",
|
summary="读取全部水箱属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getalltankproperties/",
|
path="/tanks",
|
||||||
params={"network": require_network(runtime)},
|
params={},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -125,29 +111,25 @@ def network_get_pump_properties(
|
|||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
pump: Annotated[str, typer.Option("--pump", help="水泵 ID")],
|
pump: Annotated[str, typer.Option("--pump", help="水泵 ID")],
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取水泵属性成功",
|
summary="读取水泵属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getpumpproperties/",
|
path="/pumps/properties",
|
||||||
params={"network": require_network(runtime), "pump": pump},
|
params={"pump": pump},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@network_app.command("get-all-pumps-properties")
|
@network_app.command("get-all-pumps-properties")
|
||||||
def network_get_all_pump_properties(ctx: typer.Context) -> None:
|
def network_get_all_pump_properties(ctx: typer.Context) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取全部水泵属性成功",
|
summary="读取全部水泵属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getallpumpproperties/",
|
path="/pumps",
|
||||||
params={"network": require_network(runtime)},
|
params={},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -156,29 +138,25 @@ def network_get_valve_properties(
|
|||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
valve: Annotated[str, typer.Option("--valve", help="阀门 ID")],
|
valve: Annotated[str, typer.Option("--valve", help="阀门 ID")],
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取阀门属性成功",
|
summary="读取阀门属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getvalveproperties/",
|
path="/valves/properties",
|
||||||
params={"network": require_network(runtime), "valve": valve},
|
params={"valve": valve},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@network_app.command("get-all-valves-properties")
|
@network_app.command("get-all-valves-properties")
|
||||||
def network_get_all_valve_properties(ctx: typer.Context) -> None:
|
def network_get_all_valve_properties(ctx: typer.Context) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
emit_api(
|
emit_api(
|
||||||
ctx,
|
ctx,
|
||||||
summary="读取全部阀门属性成功",
|
summary="读取全部阀门属性成功",
|
||||||
method="GET",
|
method="GET",
|
||||||
path="/getallvalveproperties/",
|
path="/valves",
|
||||||
params={"network": require_network(runtime)},
|
params={},
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -188,9 +166,8 @@ def component_option_schema(
|
|||||||
kind: Annotated[ComponentOptionKind, typer.Option("--kind", help="选项类型,仅支持 time|energy|pump-energy|network")],
|
kind: Annotated[ComponentOptionKind, typer.Option("--kind", help="选项类型,仅支持 time|energy|pump-energy|network")],
|
||||||
pump: Annotated[str | None, typer.Option("--pump", help="pump-energy 时需要的泵 ID")] = None,
|
pump: Annotated[str | None, typer.Option("--pump", help="pump-energy 时需要的泵 ID")] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
path = _component_option_path(kind.value, schema=True)
|
path = _component_option_path(kind.value, schema=True)
|
||||||
params = {"network": require_network(runtime)}
|
params: dict[str, str] = {}
|
||||||
if kind == ComponentOptionKind.PUMP_ENERGY and pump:
|
if kind == ComponentOptionKind.PUMP_ENERGY and pump:
|
||||||
params["pump"] = pump
|
params["pump"] = pump
|
||||||
emit_api(
|
emit_api(
|
||||||
@@ -200,7 +177,6 @@ def component_option_schema(
|
|||||||
path=path,
|
path=path,
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -210,9 +186,8 @@ def component_option_get(
|
|||||||
kind: Annotated[ComponentOptionKind, typer.Option("--kind", help="选项类型,仅支持 time|energy|pump-energy|network")],
|
kind: Annotated[ComponentOptionKind, typer.Option("--kind", help="选项类型,仅支持 time|energy|pump-energy|network")],
|
||||||
pump: Annotated[str | None, typer.Option("--pump", help="pump-energy 时需要的泵 ID")] = None,
|
pump: Annotated[str | None, typer.Option("--pump", help="pump-energy 时需要的泵 ID")] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
|
||||||
path = _component_option_path(kind.value, schema=False)
|
path = _component_option_path(kind.value, schema=False)
|
||||||
params = {"network": require_network(runtime)}
|
params: dict[str, str] = {}
|
||||||
if kind == ComponentOptionKind.PUMP_ENERGY:
|
if kind == ComponentOptionKind.PUMP_ENERGY:
|
||||||
if not pump:
|
if not pump:
|
||||||
raise CLIError(
|
raise CLIError(
|
||||||
@@ -229,20 +204,19 @@ def component_option_get(
|
|||||||
path=path,
|
path=path,
|
||||||
params=params,
|
params=params,
|
||||||
require_auth=True,
|
require_auth=True,
|
||||||
require_network_ctx=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _component_option_path(kind: str, *, schema: bool) -> str:
|
def _component_option_path(kind: str, *, schema: bool) -> str:
|
||||||
routes = {
|
routes = {
|
||||||
("time", True): "/gettimeschema",
|
("time", True): "/network-schemas/time",
|
||||||
("time", False): "/gettimeproperties/",
|
("time", False): "/network-options/time",
|
||||||
("energy", True): "/getenergyschema/",
|
("energy", True): "/network-schemas/energy",
|
||||||
("energy", False): "/getenergyproperties/",
|
("energy", False): "/network-options/energy",
|
||||||
("pump-energy", True): "/getpumpenergyschema/",
|
("pump-energy", True): "/network-schemas/pump-energy",
|
||||||
("pump-energy", False): "/getpumpenergyproperties//",
|
("pump-energy", False): "/network-options/pump-energy",
|
||||||
("network", True): "/getoptionschema/",
|
("network", True): "/network-schemas/option",
|
||||||
("network", False): "/getoptionproperties/",
|
("network", False): "/network-options",
|
||||||
}
|
}
|
||||||
path = routes.get((kind, schema))
|
path = routes.get((kind, schema))
|
||||||
if path is None:
|
if path is None:
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ def emit_api(
|
|||||||
json_body: Any = None,
|
json_body: Any = None,
|
||||||
require_auth: bool = True,
|
require_auth: bool = True,
|
||||||
require_project: bool = False,
|
require_project: bool = False,
|
||||||
require_network_ctx: bool = False,
|
|
||||||
require_username_ctx: bool = False,
|
|
||||||
next_commands: list[str] | None = None,
|
next_commands: list[str] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
runtime = runtime_context(ctx)
|
runtime = runtime_context(ctx)
|
||||||
@@ -51,8 +49,6 @@ def emit_api(
|
|||||||
json_body=json_body,
|
json_body=json_body,
|
||||||
require_auth=require_auth,
|
require_auth=require_auth,
|
||||||
require_project=require_project,
|
require_project=require_project,
|
||||||
require_network_ctx=require_network_ctx,
|
|
||||||
require_username_ctx=require_username_ctx,
|
|
||||||
)
|
)
|
||||||
emit_success(
|
emit_success(
|
||||||
summary=summary,
|
summary=summary,
|
||||||
|
|||||||
+23
-59
@@ -17,8 +17,6 @@ SCHEMA_VERSION = "tjwater-cli/v1"
|
|||||||
CLI_NAME = "tjwater-cli"
|
CLI_NAME = "tjwater-cli"
|
||||||
DEFAULT_TIMEOUT = 180
|
DEFAULT_TIMEOUT = 180
|
||||||
DEFAULT_SERVER = "http://192.168.1.114:8000"
|
DEFAULT_SERVER = "http://192.168.1.114:8000"
|
||||||
|
|
||||||
|
|
||||||
class CLIError(Exception):
|
class CLIError(Exception):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -46,8 +44,6 @@ class AuthContext:
|
|||||||
server: str | None = None
|
server: str | None = None
|
||||||
access_token: str | None = None
|
access_token: str | None = None
|
||||||
project_id: str | None = None
|
project_id: str | None = None
|
||||||
username: str | None = None
|
|
||||||
network: str | None = None
|
|
||||||
headers: dict[str, str] = field(default_factory=dict)
|
headers: dict[str, str] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
@@ -97,8 +93,6 @@ def load_auth_context(auth_stdin: bool = False) -> AuthContext:
|
|||||||
"server": os.getenv("TJWATER_SERVER"),
|
"server": os.getenv("TJWATER_SERVER"),
|
||||||
"access_token": os.getenv("TJWATER_ACCESS_TOKEN"),
|
"access_token": os.getenv("TJWATER_ACCESS_TOKEN"),
|
||||||
"project_id": os.getenv("TJWATER_PROJECT_ID"),
|
"project_id": os.getenv("TJWATER_PROJECT_ID"),
|
||||||
"username": os.getenv("TJWATER_USERNAME"),
|
|
||||||
"network": os.getenv("TJWATER_NETWORK"),
|
|
||||||
"headers": json.loads(extra_headers) if extra_headers else {},
|
"headers": json.loads(extra_headers) if extra_headers else {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +109,6 @@ def load_auth_context(auth_stdin: bool = False) -> AuthContext:
|
|||||||
server=_pick(raw, "server", "base_url"),
|
server=_pick(raw, "server", "base_url"),
|
||||||
access_token=_pick(raw, "access_token", "token", "accessToken"),
|
access_token=_pick(raw, "access_token", "token", "accessToken"),
|
||||||
project_id=_pick(raw, "project_id", "projectId", "x_project_id"),
|
project_id=_pick(raw, "project_id", "projectId", "x_project_id"),
|
||||||
username=_pick(raw, "username", "preferred_username"),
|
|
||||||
network=_pick(raw, "network", "project_code", "projectCode", "project"),
|
|
||||||
headers={str(key): str(value) for key, value in headers.items()},
|
headers={str(key): str(value) for key, value in headers.items()},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -175,30 +167,6 @@ def require_project_id(ctx: RuntimeContext) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def require_network(ctx: RuntimeContext) -> str:
|
|
||||||
if ctx.auth.network:
|
|
||||||
return ctx.auth.network
|
|
||||||
raise CLIError(
|
|
||||||
"认证失败",
|
|
||||||
code="NETWORK_CONTEXT_REQUIRED",
|
|
||||||
message="missing network in auth context for legacy network-based endpoints",
|
|
||||||
exit_code=3,
|
|
||||||
next_commands=["add network to auth context"],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def require_username(ctx: RuntimeContext) -> str:
|
|
||||||
if ctx.auth.username:
|
|
||||||
return ctx.auth.username
|
|
||||||
raise CLIError(
|
|
||||||
"认证失败",
|
|
||||||
code="USERNAME_CONTEXT_REQUIRED",
|
|
||||||
message="missing username in auth context",
|
|
||||||
exit_code=3,
|
|
||||||
next_commands=["add username to auth context"],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_scheme(ctx: RuntimeContext, explicit_scheme: str | None, *, required: bool = False) -> str | None:
|
def resolve_scheme(ctx: RuntimeContext, explicit_scheme: str | None, *, required: bool = False) -> str | None:
|
||||||
scheme = explicit_scheme or ctx.scheme
|
scheme = explicit_scheme or ctx.scheme
|
||||||
if required and not scheme:
|
if required and not scheme:
|
||||||
@@ -254,14 +222,14 @@ def parse_burst_file(path: Path) -> tuple[list[str], list[float]]:
|
|||||||
raw = read_json_input(path, label="burst")
|
raw = read_json_input(path, label="burst")
|
||||||
if isinstance(raw, dict) and "bursts" in raw:
|
if isinstance(raw, dict) and "bursts" in raw:
|
||||||
raw = raw["bursts"]
|
raw = raw["bursts"]
|
||||||
if isinstance(raw, dict) and "burst_ID" in raw and "burst_size" in raw:
|
if isinstance(raw, dict) and "burst_id" in raw and "burst_size" in raw:
|
||||||
ids = [str(item) for item in raw["burst_ID"]]
|
ids = [str(item) for item in raw["burst_id"]]
|
||||||
sizes = [float(item) for item in raw["burst_size"]]
|
sizes = [float(item) for item in raw["burst_size"]]
|
||||||
if len(ids) != len(sizes):
|
if len(ids) != len(sizes):
|
||||||
raise CLIError(
|
raise CLIError(
|
||||||
"CLI 参数错误",
|
"CLI 参数错误",
|
||||||
code="BURST_FILE_INVALID",
|
code="BURST_FILE_INVALID",
|
||||||
message="burst file burst_ID and burst_size must have the same length",
|
message="burst file burst_id and burst_size must have the same length",
|
||||||
exit_code=2,
|
exit_code=2,
|
||||||
)
|
)
|
||||||
return ids, sizes
|
return ids, sizes
|
||||||
@@ -282,7 +250,7 @@ def parse_burst_file(path: Path) -> tuple[list[str], list[float]]:
|
|||||||
raise CLIError(
|
raise CLIError(
|
||||||
"CLI 参数错误",
|
"CLI 参数错误",
|
||||||
code="BURST_FILE_INVALID",
|
code="BURST_FILE_INVALID",
|
||||||
message="burst file must be a JSON array or object with burst_ID/burst_size",
|
message="burst file must be a JSON array or object with burst_id/burst_size",
|
||||||
exit_code=2,
|
exit_code=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -404,12 +372,13 @@ def _parse_response_body(response: requests.Response) -> Any:
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def _with_network_param(params: dict[str, Any] | None, network: str) -> dict[str, Any]:
|
def _prepare_public_request(
|
||||||
params = dict(params or {})
|
method: str,
|
||||||
if "network" in params or "network_name" in params or "name" in params:
|
path: str,
|
||||||
return params
|
params: dict[str, Any] | None,
|
||||||
params["network"] = network
|
json_body: Any,
|
||||||
return params
|
) -> tuple[str, str, dict[str, Any] | None, Any]:
|
||||||
|
return method.upper(), path.rstrip("/") or "/", params or None, json_body
|
||||||
|
|
||||||
|
|
||||||
def request_json(
|
def request_json(
|
||||||
@@ -421,18 +390,14 @@ def request_json(
|
|||||||
json_body: Any = None,
|
json_body: Any = None,
|
||||||
require_auth: bool = True,
|
require_auth: bool = True,
|
||||||
require_project: bool = False,
|
require_project: bool = False,
|
||||||
require_network_ctx: bool = False,
|
|
||||||
require_username_ctx: bool = False,
|
|
||||||
) -> tuple[Any, int]:
|
) -> tuple[Any, int]:
|
||||||
require_server(ctx)
|
require_server(ctx)
|
||||||
network = None
|
method, path, params, json_body = _prepare_public_request(
|
||||||
if require_network_ctx:
|
method,
|
||||||
network = require_network(ctx)
|
path,
|
||||||
if require_username_ctx:
|
params,
|
||||||
require_username(ctx)
|
json_body,
|
||||||
if network and (params is not None or json_body is None):
|
)
|
||||||
params = _with_network_param(params, network)
|
|
||||||
|
|
||||||
url = f"{require_server(ctx)}/api/v1{path}"
|
url = f"{require_server(ctx)}/api/v1{path}"
|
||||||
headers = build_headers(ctx, require_auth=require_auth, require_project=require_project)
|
headers = build_headers(ctx, require_auth=require_auth, require_project=require_project)
|
||||||
started = time.monotonic()
|
started = time.monotonic()
|
||||||
@@ -482,15 +447,14 @@ def request_bytes(
|
|||||||
params: dict[str, Any] | None = None,
|
params: dict[str, Any] | None = None,
|
||||||
require_auth: bool = True,
|
require_auth: bool = True,
|
||||||
require_project: bool = False,
|
require_project: bool = False,
|
||||||
require_network_ctx: bool = False,
|
|
||||||
) -> tuple[bytes, int]:
|
) -> tuple[bytes, int]:
|
||||||
require_server(ctx)
|
require_server(ctx)
|
||||||
network = None
|
method, path, params, _ = _prepare_public_request(
|
||||||
if require_network_ctx:
|
method,
|
||||||
network = require_network(ctx)
|
path,
|
||||||
if network:
|
params,
|
||||||
params = _with_network_param(params, network)
|
None,
|
||||||
|
)
|
||||||
url = f"{require_server(ctx)}/api/v1{path}"
|
url = f"{require_server(ctx)}/api/v1{path}"
|
||||||
headers = build_headers(ctx, require_auth=require_auth, require_project=require_project)
|
headers = build_headers(ctx, require_auth=require_auth, require_project=require_project)
|
||||||
started = time.monotonic()
|
started = time.monotonic()
|
||||||
|
|||||||
+35
-35
@@ -35,73 +35,73 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("network", "get-junction-properties"): CommandDoc(
|
("network", "get-junction-properties"): CommandDoc(
|
||||||
path=("network", "get-junction-properties"),
|
path=("network", "get-junction-properties"),
|
||||||
summary="读取节点属性",
|
summary="读取节点属性",
|
||||||
description="调用 /getjunctionproperties/。",
|
description="调用 GET /api/v1/junctions/{junction_id}/properties。",
|
||||||
options=(CommandOptionDoc("junction", "节点 ID", required=True),),
|
options=(CommandOptionDoc("junction", "节点 ID", required=True),),
|
||||||
examples=("tjwater-cli network get-junction-properties --junction J1",),
|
examples=("tjwater-cli network get-junction-properties --junction J1",),
|
||||||
),
|
),
|
||||||
("network", "get-pipe-properties"): CommandDoc(
|
("network", "get-pipe-properties"): CommandDoc(
|
||||||
path=("network", "get-pipe-properties"),
|
path=("network", "get-pipe-properties"),
|
||||||
summary="读取管道属性",
|
summary="读取管道属性",
|
||||||
description="调用 /getpipeproperties/。",
|
description="调用 GET /api/v1/pipes/{pipe_id}/properties。",
|
||||||
options=(CommandOptionDoc("pipe", "管道 ID", required=True),),
|
options=(CommandOptionDoc("pipe", "管道 ID", required=True),),
|
||||||
examples=("tjwater-cli network get-pipe-properties --pipe P1",),
|
examples=("tjwater-cli network get-pipe-properties --pipe P1",),
|
||||||
),
|
),
|
||||||
("network", "get-all-pipes-properties"): CommandDoc(
|
("network", "get-all-pipes-properties"): CommandDoc(
|
||||||
path=("network", "get-all-pipes-properties"),
|
path=("network", "get-all-pipes-properties"),
|
||||||
summary="读取全部管道属性",
|
summary="读取全部管道属性",
|
||||||
description="调用 /getallpipeproperties/。",
|
description="调用 GET /api/v1/pipes/properties。",
|
||||||
examples=("tjwater-cli network get-all-pipes-properties",),
|
examples=("tjwater-cli network get-all-pipes-properties",),
|
||||||
),
|
),
|
||||||
("network", "get-reservoir-properties"): CommandDoc(
|
("network", "get-reservoir-properties"): CommandDoc(
|
||||||
path=("network", "get-reservoir-properties"),
|
path=("network", "get-reservoir-properties"),
|
||||||
summary="读取水库属性",
|
summary="读取水库属性",
|
||||||
description="调用 /getreservoirproperties/。",
|
description="调用 GET /api/v1/reservoirs/{reservoir_id}/properties。",
|
||||||
options=(CommandOptionDoc("reservoir", "水库 ID", required=True),),
|
options=(CommandOptionDoc("reservoir", "水库 ID", required=True),),
|
||||||
examples=("tjwater-cli network get-reservoir-properties --reservoir R1",),
|
examples=("tjwater-cli network get-reservoir-properties --reservoir R1",),
|
||||||
),
|
),
|
||||||
("network", "get-all-reservoirs-properties"): CommandDoc(
|
("network", "get-all-reservoirs-properties"): CommandDoc(
|
||||||
path=("network", "get-all-reservoirs-properties"),
|
path=("network", "get-all-reservoirs-properties"),
|
||||||
summary="读取全部水库属性",
|
summary="读取全部水库属性",
|
||||||
description="调用 /getallreservoirproperties/。",
|
description="调用 GET /api/v1/reservoirs/properties。",
|
||||||
examples=("tjwater-cli network get-all-reservoirs-properties",),
|
examples=("tjwater-cli network get-all-reservoirs-properties",),
|
||||||
),
|
),
|
||||||
("network", "get-tank-properties"): CommandDoc(
|
("network", "get-tank-properties"): CommandDoc(
|
||||||
path=("network", "get-tank-properties"),
|
path=("network", "get-tank-properties"),
|
||||||
summary="读取水箱属性",
|
summary="读取水箱属性",
|
||||||
description="调用 /gettankproperties/。",
|
description="调用 GET /api/v1/tanks/{tank_id}/properties。",
|
||||||
options=(CommandOptionDoc("tank", "水箱 ID", required=True),),
|
options=(CommandOptionDoc("tank", "水箱 ID", required=True),),
|
||||||
examples=("tjwater-cli network get-tank-properties --tank T1",),
|
examples=("tjwater-cli network get-tank-properties --tank T1",),
|
||||||
),
|
),
|
||||||
("network", "get-all-tanks-properties"): CommandDoc(
|
("network", "get-all-tanks-properties"): CommandDoc(
|
||||||
path=("network", "get-all-tanks-properties"),
|
path=("network", "get-all-tanks-properties"),
|
||||||
summary="读取全部水箱属性",
|
summary="读取全部水箱属性",
|
||||||
description="调用 /getalltankproperties/。",
|
description="调用 GET /api/v1/tanks/properties。",
|
||||||
examples=("tjwater-cli network get-all-tanks-properties",),
|
examples=("tjwater-cli network get-all-tanks-properties",),
|
||||||
),
|
),
|
||||||
("network", "get-pump-properties"): CommandDoc(
|
("network", "get-pump-properties"): CommandDoc(
|
||||||
path=("network", "get-pump-properties"),
|
path=("network", "get-pump-properties"),
|
||||||
summary="读取水泵属性",
|
summary="读取水泵属性",
|
||||||
description="调用 /getpumpproperties/。",
|
description="调用 GET /api/v1/pumps/{pump_id}/properties。",
|
||||||
options=(CommandOptionDoc("pump", "水泵 ID", required=True),),
|
options=(CommandOptionDoc("pump", "水泵 ID", required=True),),
|
||||||
examples=("tjwater-cli network get-pump-properties --pump PU1",),
|
examples=("tjwater-cli network get-pump-properties --pump PU1",),
|
||||||
),
|
),
|
||||||
("network", "get-all-pumps-properties"): CommandDoc(
|
("network", "get-all-pumps-properties"): CommandDoc(
|
||||||
path=("network", "get-all-pumps-properties"),
|
path=("network", "get-all-pumps-properties"),
|
||||||
summary="读取全部水泵属性",
|
summary="读取全部水泵属性",
|
||||||
description="调用 /getallpumpproperties/。",
|
description="调用 GET /api/v1/pumps/properties。",
|
||||||
examples=("tjwater-cli network get-all-pumps-properties",),
|
examples=("tjwater-cli network get-all-pumps-properties",),
|
||||||
),
|
),
|
||||||
("network", "get-valve-properties"): CommandDoc(
|
("network", "get-valve-properties"): CommandDoc(
|
||||||
path=("network", "get-valve-properties"),
|
path=("network", "get-valve-properties"),
|
||||||
summary="读取阀门属性",
|
summary="读取阀门属性",
|
||||||
description="调用 /getvalveproperties/。",
|
description="调用 GET /api/v1/valves/{valve_id}/properties。",
|
||||||
options=(CommandOptionDoc("valve", "阀门 ID", required=True),),
|
options=(CommandOptionDoc("valve", "阀门 ID", required=True),),
|
||||||
examples=("tjwater-cli network get-valve-properties --valve V1",),
|
examples=("tjwater-cli network get-valve-properties --valve V1",),
|
||||||
),
|
),
|
||||||
("network", "get-all-valves-properties"): CommandDoc(
|
("network", "get-all-valves-properties"): CommandDoc(
|
||||||
path=("network", "get-all-valves-properties"),
|
path=("network", "get-all-valves-properties"),
|
||||||
summary="读取全部阀门属性",
|
summary="读取全部阀门属性",
|
||||||
description="调用 /getallvalveproperties/。",
|
description="调用 GET /api/v1/valves/properties。",
|
||||||
examples=("tjwater-cli network get-all-valves-properties",),
|
examples=("tjwater-cli network get-all-valves-properties",),
|
||||||
),
|
),
|
||||||
("component", "option", "schema"): CommandDoc(
|
("component", "option", "schema"): CommandDoc(
|
||||||
@@ -137,7 +137,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("simulation", "run"): CommandDoc(
|
("simulation", "run"): CommandDoc(
|
||||||
path=("simulation", "run"),
|
path=("simulation", "run"),
|
||||||
summary="触发指定绝对时间的模拟运行",
|
summary="触发指定绝对时间的模拟运行",
|
||||||
description="把显式带时区的 RFC3339 start-time 直接传给 /simulations/run-by-date;服务端按带时区时间处理并统一按 UTC 存储结果,实时数据需后续通过 data timeseries 在对应时间段查询。duration 单位为分钟。",
|
description="把显式带时区的 RFC3339 start-time 直接传给 POST /api/v1/simulation-runs;服务端按带时区时间处理并统一按 UTC 存储结果,实时数据需后续通过 data timeseries 在对应时间段查询。duration 单位为分钟。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
||||||
CommandOptionDoc("duration", "持续分钟数", required=True),
|
CommandOptionDoc("duration", "持续分钟数", required=True),
|
||||||
@@ -152,7 +152,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("analysis", "burst"): CommandDoc(
|
("analysis", "burst"): CommandDoc(
|
||||||
path=("analysis", "burst"),
|
path=("analysis", "burst"),
|
||||||
summary="执行爆管分析",
|
summary="执行爆管分析",
|
||||||
description="读取 burst-file 并转换为 burst_ID[] / burst_size[];接口本身只返回分析执行结果,方案数据需后续通过 data scheme 命令获取。duration 单位为秒。",
|
description="读取 burst-file 的 burst_id[] / burst_size[] 并调用 POST /api/v1/burst-analyses;接口本身只返回分析执行结果,方案数据需后续通过 data scheme 命令获取。duration 单位为秒。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
||||||
CommandOptionDoc("duration", "持续秒数", required=True),
|
CommandOptionDoc("duration", "持续秒数", required=True),
|
||||||
@@ -201,7 +201,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("analysis", "age"): CommandDoc(
|
("analysis", "age"): CommandDoc(
|
||||||
path=("analysis", "age"),
|
path=("analysis", "age"),
|
||||||
summary="执行水龄分析",
|
summary="执行水龄分析",
|
||||||
description="调用 /age_analysis/。duration 单位为秒。",
|
description="调用 POST /api/v1/water-age-analyses。duration 单位为秒。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
||||||
CommandOptionDoc("duration", "持续秒数", required=True),
|
CommandOptionDoc("duration", "持续秒数", required=True),
|
||||||
@@ -211,7 +211,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("analysis", "contaminant"): CommandDoc(
|
("analysis", "contaminant"): CommandDoc(
|
||||||
path=("analysis", "contaminant"),
|
path=("analysis", "contaminant"),
|
||||||
summary="执行污染物模拟",
|
summary="执行污染物模拟",
|
||||||
description="调用 /contaminant-simulation。duration 单位为秒。",
|
description="调用 POST /api/v1/contaminant-simulations。duration 单位为秒。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
||||||
CommandOptionDoc("duration", "持续秒数", required=True),
|
CommandOptionDoc("duration", "持续秒数", required=True),
|
||||||
@@ -247,19 +247,19 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("analysis", "leakage", "schemes", "list"): CommandDoc(
|
("analysis", "leakage", "schemes", "list"): CommandDoc(
|
||||||
path=("analysis", "leakage", "schemes", "list"),
|
path=("analysis", "leakage", "schemes", "list"),
|
||||||
summary="列出漏损方案",
|
summary="列出漏损方案",
|
||||||
description="调用 /schemes,并传入 scheme_type=dma_leak_identification。",
|
description="调用 GET /api/v1/schemes,并传入 scheme_type=dma_leak_identification。",
|
||||||
examples=("tjwater-cli analysis leakage schemes list",),
|
examples=("tjwater-cli analysis leakage schemes list",),
|
||||||
),
|
),
|
||||||
("analysis", "leakage", "schemes", "get"): CommandDoc(
|
("analysis", "leakage", "schemes", "get"): CommandDoc(
|
||||||
path=("analysis", "leakage", "schemes", "get"),
|
path=("analysis", "leakage", "schemes", "get"),
|
||||||
summary="读取漏损方案详情",
|
summary="读取漏损方案详情",
|
||||||
description="调用 /schemes/{scheme_name},并传入 scheme_type=dma_leak_identification。",
|
description="调用 GET /api/v1/schemes/{scheme_name},并传入 scheme_type=dma_leak_identification。",
|
||||||
examples=("tjwater-cli analysis leakage schemes get my_scheme",),
|
examples=("tjwater-cli analysis leakage schemes get my_scheme",),
|
||||||
),
|
),
|
||||||
("analysis", "burst-detection", "detect"): CommandDoc(
|
("analysis", "burst-detection", "detect"): CommandDoc(
|
||||||
path=("analysis", "burst-detection", "detect"),
|
path=("analysis", "burst-detection", "detect"),
|
||||||
summary="执行爆管检测",
|
summary="执行爆管检测",
|
||||||
description="调用 /burst-detection/detect/。",
|
description="调用 POST /api/v1/burst-detections。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("start-time", "显式带时区的 SCADA 开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的 SCADA 开始时间", required=True),
|
||||||
CommandOptionDoc("end-time", "显式带时区的 SCADA 结束时间", required=True),
|
CommandOptionDoc("end-time", "显式带时区的 SCADA 结束时间", required=True),
|
||||||
@@ -270,19 +270,19 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("analysis", "burst-detection", "schemes", "list"): CommandDoc(
|
("analysis", "burst-detection", "schemes", "list"): CommandDoc(
|
||||||
path=("analysis", "burst-detection", "schemes", "list"),
|
path=("analysis", "burst-detection", "schemes", "list"),
|
||||||
summary="列出爆管检测方案",
|
summary="列出爆管检测方案",
|
||||||
description="调用 /schemes,并传入 scheme_type=burst_detection。",
|
description="调用 GET /api/v1/schemes,并传入 scheme_type=burst_detection。",
|
||||||
examples=("tjwater-cli analysis burst-detection schemes list",),
|
examples=("tjwater-cli analysis burst-detection schemes list",),
|
||||||
),
|
),
|
||||||
("analysis", "burst-detection", "schemes", "get"): CommandDoc(
|
("analysis", "burst-detection", "schemes", "get"): CommandDoc(
|
||||||
path=("analysis", "burst-detection", "schemes", "get"),
|
path=("analysis", "burst-detection", "schemes", "get"),
|
||||||
summary="读取爆管检测方案详情",
|
summary="读取爆管检测方案详情",
|
||||||
description="调用 /schemes/{scheme_name},并传入 scheme_type=burst_detection。",
|
description="调用 GET /api/v1/schemes/{scheme_name},并传入 scheme_type=burst_detection。",
|
||||||
examples=("tjwater-cli analysis burst-detection schemes get my_scheme",),
|
examples=("tjwater-cli analysis burst-detection schemes get my_scheme",),
|
||||||
),
|
),
|
||||||
("analysis", "burst-location", "locate"): CommandDoc(
|
("analysis", "burst-location", "locate"): CommandDoc(
|
||||||
path=("analysis", "burst-location", "locate"),
|
path=("analysis", "burst-location", "locate"),
|
||||||
summary="执行爆管定位",
|
summary="执行爆管定位",
|
||||||
description="调用 /burst-location/locate/;需要 burst-leakage。支持 monitoring 和 simulation 两种数据源。",
|
description="调用 POST /api/v1/burst-locations;需要 burst-leakage。支持 monitoring 和 simulation 两种数据源。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("start-time", "显式带时区的 SCADA 开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的 SCADA 开始时间", required=True),
|
||||||
CommandOptionDoc("end-time", "显式带时区的 SCADA 结束时间", required=True),
|
CommandOptionDoc("end-time", "显式带时区的 SCADA 结束时间", required=True),
|
||||||
@@ -303,26 +303,26 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("analysis", "burst-location", "schemes", "list"): CommandDoc(
|
("analysis", "burst-location", "schemes", "list"): CommandDoc(
|
||||||
path=("analysis", "burst-location", "schemes", "list"),
|
path=("analysis", "burst-location", "schemes", "list"),
|
||||||
summary="列出爆管定位方案",
|
summary="列出爆管定位方案",
|
||||||
description="调用 /schemes,并传入 scheme_type=burst_location。",
|
description="调用 GET /api/v1/schemes,并传入 scheme_type=burst_location。",
|
||||||
examples=("tjwater-cli analysis burst-location schemes list",),
|
examples=("tjwater-cli analysis burst-location schemes list",),
|
||||||
),
|
),
|
||||||
("analysis", "burst-location", "schemes", "get"): CommandDoc(
|
("analysis", "burst-location", "schemes", "get"): CommandDoc(
|
||||||
path=("analysis", "burst-location", "schemes", "get"),
|
path=("analysis", "burst-location", "schemes", "get"),
|
||||||
summary="读取爆管定位方案详情",
|
summary="读取爆管定位方案详情",
|
||||||
description="调用 /schemes/{scheme_name},并传入 scheme_type=burst_location。",
|
description="调用 GET /api/v1/schemes/{scheme_name},并传入 scheme_type=burst_location。",
|
||||||
examples=("tjwater-cli analysis burst-location schemes get my_scheme",),
|
examples=("tjwater-cli analysis burst-location schemes get my_scheme",),
|
||||||
),
|
),
|
||||||
("analysis", "risk", "pipe-now"): CommandDoc(
|
("analysis", "risk", "pipe-now"): CommandDoc(
|
||||||
path=("analysis", "risk", "pipe-now"),
|
path=("analysis", "risk", "pipe-now"),
|
||||||
summary="读取单条管道当前风险",
|
summary="读取单条管道当前风险",
|
||||||
description="调用 /getpiperiskprobabilitynow/。",
|
description="调用 GET /api/v1/pipes/risk-probability-now。",
|
||||||
options=(CommandOptionDoc("pipe", "管道 ID", required=True),),
|
options=(CommandOptionDoc("pipe", "管道 ID", required=True),),
|
||||||
examples=("tjwater-cli analysis risk pipe-now --pipe P1",),
|
examples=("tjwater-cli analysis risk pipe-now --pipe P1",),
|
||||||
),
|
),
|
||||||
("analysis", "risk", "pipe-history"): CommandDoc(
|
("analysis", "risk", "pipe-history"): CommandDoc(
|
||||||
path=("analysis", "risk", "pipe-history"),
|
path=("analysis", "risk", "pipe-history"),
|
||||||
summary="读取单条管道历史风险",
|
summary="读取单条管道历史风险",
|
||||||
description="调用 /getpiperiskprobability/。",
|
description="调用 GET /api/v1/pipes/risk-probability。",
|
||||||
options=(CommandOptionDoc("pipe", "管道 ID", required=True),),
|
options=(CommandOptionDoc("pipe", "管道 ID", required=True),),
|
||||||
examples=("tjwater-cli analysis risk pipe-history --pipe P1",),
|
examples=("tjwater-cli analysis risk pipe-history --pipe P1",),
|
||||||
),
|
),
|
||||||
@@ -335,7 +335,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("data", "timeseries", "realtime", "links"): CommandDoc(
|
("data", "timeseries", "realtime", "links"): CommandDoc(
|
||||||
path=("data", "timeseries", "realtime", "links"),
|
path=("data", "timeseries", "realtime", "links"),
|
||||||
summary="查询实时管道时序",
|
summary="查询实时管道时序",
|
||||||
description="调用 /realtime/links。",
|
description="调用 GET /api/v1/timeseries/realtime/links。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
||||||
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
|
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
|
||||||
@@ -345,7 +345,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("data", "timeseries", "realtime", "nodes"): CommandDoc(
|
("data", "timeseries", "realtime", "nodes"): CommandDoc(
|
||||||
path=("data", "timeseries", "realtime", "nodes"),
|
path=("data", "timeseries", "realtime", "nodes"),
|
||||||
summary="查询实时节点时序",
|
summary="查询实时节点时序",
|
||||||
description="调用 /realtime/nodes。",
|
description="调用 GET /api/v1/timeseries/realtime/nodes。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
||||||
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
|
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
|
||||||
@@ -355,7 +355,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("data", "timeseries", "realtime", "simulation-by-id-time"): CommandDoc(
|
("data", "timeseries", "realtime", "simulation-by-id-time"): CommandDoc(
|
||||||
path=("data", "timeseries", "realtime", "simulation-by-id-time"),
|
path=("data", "timeseries", "realtime", "simulation-by-id-time"),
|
||||||
summary="按元素和时间查询实时模拟结果",
|
summary="按元素和时间查询实时模拟结果",
|
||||||
description="调用 /realtime/query/by-id-time。",
|
description="调用 GET /api/v1/timeseries/realtime/by-element。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("id", "元素 ID", required=True),
|
CommandOptionDoc("id", "元素 ID", required=True),
|
||||||
CommandOptionDoc("type", "元素类型:pipe 或 junction;links/nodes 是独立子命令,不是 type 取值", required=True),
|
CommandOptionDoc("type", "元素类型:pipe 或 junction;links/nodes 是独立子命令,不是 type 取值", required=True),
|
||||||
@@ -369,7 +369,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("data", "timeseries", "realtime", "simulation-by-time-property"): CommandDoc(
|
("data", "timeseries", "realtime", "simulation-by-time-property"): CommandDoc(
|
||||||
path=("data", "timeseries", "realtime", "simulation-by-time-property"),
|
path=("data", "timeseries", "realtime", "simulation-by-time-property"),
|
||||||
summary="按时间和属性查询实时模拟结果",
|
summary="按时间和属性查询实时模拟结果",
|
||||||
description="调用 /realtime/query/by-time-property。pipe 属性:flow、friction、headloss、quality、reaction、setting、status、velocity;junction 属性:actual_demand、total_head、pressure、quality。",
|
description="调用 GET /api/v1/timeseries/realtime/by-property。pipe 属性:flow、friction、headloss、quality、reaction、setting、status、velocity;junction 属性:actual_demand、total_head、pressure、quality。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("type", "元素类型:pipe 或 junction;links/nodes 是独立子命令,不是 type 取值", required=True),
|
CommandOptionDoc("type", "元素类型:pipe 或 junction;links/nodes 是独立子命令,不是 type 取值", required=True),
|
||||||
CommandOptionDoc("time", "显式带时区的查询时间", required=True),
|
CommandOptionDoc("time", "显式带时区的查询时间", required=True),
|
||||||
@@ -380,7 +380,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("data", "timeseries", "scheme", "links"): CommandDoc(
|
("data", "timeseries", "scheme", "links"): CommandDoc(
|
||||||
path=("data", "timeseries", "scheme", "links"),
|
path=("data", "timeseries", "scheme", "links"),
|
||||||
summary="查询方案管道时序",
|
summary="查询方案管道时序",
|
||||||
description="调用 /scheme/links。",
|
description="调用 GET /api/v1/timeseries/schemes/links。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
||||||
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
|
CommandOptionDoc("end-time", "显式带时区的结束时间", required=True),
|
||||||
@@ -392,7 +392,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("data", "timeseries", "scheme", "node-field"): CommandDoc(
|
("data", "timeseries", "scheme", "node-field"): CommandDoc(
|
||||||
path=("data", "timeseries", "scheme", "node-field"),
|
path=("data", "timeseries", "scheme", "node-field"),
|
||||||
summary="查询方案节点字段时序",
|
summary="查询方案节点字段时序",
|
||||||
description="调用 /scheme/nodes/{node_id}/field。field 仅支持 actual_demand、total_head、pressure、quality。",
|
description="调用 GET /api/v1/timeseries/schemes/nodes/{node_id}/{field}。field 仅支持 actual_demand、total_head、pressure、quality。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("node", "节点 ID", required=True),
|
CommandOptionDoc("node", "节点 ID", required=True),
|
||||||
CommandOptionDoc("field", "字段名:actual_demand、total_head、pressure、quality", required=True),
|
CommandOptionDoc("field", "字段名:actual_demand、total_head、pressure、quality", required=True),
|
||||||
@@ -458,7 +458,7 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("data", "timeseries", "composite", "pipeline-health"): CommandDoc(
|
("data", "timeseries", "composite", "pipeline-health"): CommandDoc(
|
||||||
path=("data", "timeseries", "composite", "pipeline-health"),
|
path=("data", "timeseries", "composite", "pipeline-health"),
|
||||||
summary="查询管道健康预测",
|
summary="查询管道健康预测",
|
||||||
description="调用 /composite/pipeline-health-prediction。",
|
description="调用 GET /api/v1/pipeline-health-predictions。",
|
||||||
options=(
|
options=(
|
||||||
CommandOptionDoc("pipe", "管道 ID", required=True),
|
CommandOptionDoc("pipe", "管道 ID", required=True),
|
||||||
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
CommandOptionDoc("start-time", "显式带时区的开始时间", required=True),
|
||||||
@@ -486,20 +486,20 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
|||||||
("data", "scheme", "schema"): CommandDoc(
|
("data", "scheme", "schema"): CommandDoc(
|
||||||
path=("data", "scheme", "schema"),
|
path=("data", "scheme", "schema"),
|
||||||
summary="读取方案 schema",
|
summary="读取方案 schema",
|
||||||
description="调用 /getschemeschema/。",
|
description="调用 GET /api/v1/network-schemas/scheme。",
|
||||||
examples=("tjwater-cli data scheme schema",),
|
examples=("tjwater-cli data scheme schema",),
|
||||||
),
|
),
|
||||||
("data", "scheme", "get"): CommandDoc(
|
("data", "scheme", "get"): CommandDoc(
|
||||||
path=("data", "scheme", "get"),
|
path=("data", "scheme", "get"),
|
||||||
summary="读取单条方案",
|
summary="读取单条方案",
|
||||||
description="调用 /getscheme/。",
|
description="调用 GET /api/v1/schemes/detail。",
|
||||||
options=(CommandOptionDoc("name", "方案名称", required=True),),
|
options=(CommandOptionDoc("name", "方案名称", required=True),),
|
||||||
examples=("tjwater-cli data scheme get --name my_scheme",),
|
examples=("tjwater-cli data scheme get --name my_scheme",),
|
||||||
),
|
),
|
||||||
("data", "scheme", "list"): CommandDoc(
|
("data", "scheme", "list"): CommandDoc(
|
||||||
path=("data", "scheme", "list"),
|
path=("data", "scheme", "list"),
|
||||||
summary="列出方案",
|
summary="列出方案",
|
||||||
description="调用 /schemes。",
|
description="调用 GET /api/v1/schemes。",
|
||||||
examples=("tjwater-cli data scheme list",),
|
examples=("tjwater-cli data scheme list",),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"contract_version": "1.0.0",
|
||||||
|
"contracts": {
|
||||||
|
"server": {
|
||||||
|
"file": "server-v1.openapi.json",
|
||||||
|
"sha256": "d80a968d281fdb2953364a5979c2d61fda5151a1e1759c01cc96780b11a6d56c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
|||||||
|
# TJWater REST API v1
|
||||||
|
|
||||||
|
This contract is the public API contract for coordinated TJWater
|
||||||
|
Server, Agent, Frontend, and CLI releases.
|
||||||
|
|
||||||
|
- Paths use lowercase kebab-case, have no trailing slash, and identify
|
||||||
|
resources rather than handler actions.
|
||||||
|
- JSON fields, query parameters, and path parameter names use snake_case.
|
||||||
|
- Project-scoped requests use `X-Project-Id`; `network` query parameters
|
||||||
|
are not part of the public contract.
|
||||||
|
- `GET` is read-only. Synchronous analysis and simulation requests use
|
||||||
|
`POST` and clients must not retry them automatically.
|
||||||
|
- JSON errors use `application/problem+json`.
|
||||||
|
- The static OpenAPI file and `contracts/manifest.json` are release
|
||||||
|
artifacts even when production runtime documentation is disabled.
|
||||||
|
|
||||||
|
Generate and validate the contract with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
conda run -n server python scripts/export_openapi.py
|
||||||
|
conda run -n server python scripts/check_openapi.py
|
||||||
|
```
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Keycloak 登录主题
|
||||||
|
|
||||||
|
`themes/tjwater` 是 TJWater 智慧水务平台的 Keycloak 登录主题。主题继承
|
||||||
|
`keycloak.v2`,只覆盖样式、消息和本地 SVG 资源,不修改认证模板或认证流程。
|
||||||
|
|
||||||
|
## 在管理控制台切换登录主题
|
||||||
|
|
||||||
|
确认 `themes/tjwater` 已挂载到容器的
|
||||||
|
`/opt/keycloak/themes/tjwater`,然后按以下步骤切换:
|
||||||
|
|
||||||
|
1. 打开 Keycloak 管理控制台:
|
||||||
|
`http://<Keycloak 地址>:<端口>/admin/`。
|
||||||
|
2. 使用管理员账号登录。
|
||||||
|
3. 在左上角选择需要应用主题的 realm,例如 `tjwater`。不要停留在
|
||||||
|
`master`,除非确实要修改 `master` realm。
|
||||||
|
4. 在左侧菜单进入 `Realm settings`,打开 `Themes` 标签页。
|
||||||
|
5. 在 `Login theme` 下拉框中选择 `tjwater`。
|
||||||
|
6. 点击 `Save` 保存。
|
||||||
|
7. 继续检查业务客户端是否单独指定了登录主题,再从业务前端重新进入登录页。
|
||||||
|
|
||||||
|
切回 Keycloak 默认登录页时,将 `Login theme` 改为 `keycloak` 并保存。
|
||||||
|
|
||||||
|
### 检查业务客户端的主题配置
|
||||||
|
|
||||||
|
Keycloak 的 realm 和 client 都可以设置登录主题。client 的配置优先于 realm。
|
||||||
|
因此,即使 `Realm settings > Themes > Login theme` 已选择 `tjwater`,业务
|
||||||
|
客户端如果仍指定 `keycloak`,从业务系统跳转后看到的还是默认登录页。
|
||||||
|
|
||||||
|
以授权地址中包含 `client_id=tjwater` 的业务系统为例:
|
||||||
|
|
||||||
|
1. 确认左上角当前 realm 是 `tjwater`。
|
||||||
|
2. 在左侧菜单进入 `Clients`。
|
||||||
|
3. 打开 `Client ID` 为 `tjwater` 的客户端。
|
||||||
|
4. 在 `Settings` 页面找到 `Login settings > Login theme`。
|
||||||
|
5. 将该字段设置为以下任一选项:
|
||||||
|
- `Choose...`:不在 client 层指定主题,继承 realm 的 `tjwater` 主题,
|
||||||
|
推荐使用此方式。
|
||||||
|
- `tjwater`:在 client 层明确指定 `tjwater` 主题。
|
||||||
|
6. 不要保留 `keycloak`,否则它会覆盖 realm 的主题。
|
||||||
|
7. 点击 `Save`,关闭旧登录页,再从业务前端重新发起一次登录。
|
||||||
|
|
||||||
|
`Choose...` 不是未配置完成,而是表示当前 client 继承 realm 配置。管理控制台
|
||||||
|
登录、账户中心和业务系统可能使用不同的 client。某一个入口已经显示
|
||||||
|
`tjwater` 主题,并不能证明业务 client 也已正确配置。
|
||||||
|
|
||||||
|
验证时以业务系统实际生成的 OpenID Connect 授权地址为准,并检查其中的
|
||||||
|
`client_id`。浏览器加载的主题资源路径应包含
|
||||||
|
`/resources/<版本>/login/tjwater/`;如果路径仍包含
|
||||||
|
`/resources/<版本>/login/keycloak/`,说明该 client 仍在使用默认主题。
|
||||||
|
|
||||||
|
如果 `Login theme` 下拉框中没有 `tjwater`,先检查容器内的主题文件:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose \
|
||||||
|
--env-file .env \
|
||||||
|
-f infra/docker/docker-compose.yml \
|
||||||
|
exec -T keycloak \
|
||||||
|
test -f /opt/keycloak/themes/tjwater/login/theme.properties
|
||||||
|
```
|
||||||
|
|
||||||
|
命令成功但控制台仍未显示主题时,重新创建 Keycloak 容器后再检查:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose \
|
||||||
|
--env-file .env \
|
||||||
|
-f infra/docker/docker-compose.yml \
|
||||||
|
up -d --force-recreate keycloak
|
||||||
|
```
|
||||||
|
|
||||||
|
主题名称已经正确,但页面仍显示旧样式时,也执行上述命令,并在容器启动后使用
|
||||||
|
`Ctrl+F5` 强制刷新登录页,避免继续使用浏览器缓存的 CSS。
|
||||||
|
|
||||||
|
## 启用
|
||||||
|
|
||||||
|
先启动 `infra/docker/docker-compose.yml` 中的 Keycloak,再从仓库根目录执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash infra/docker/keycloak/configure-theme.sh apply
|
||||||
|
```
|
||||||
|
|
||||||
|
脚本默认配置 `tjwater` realm、简体中文默认语言、中英文切换和
|
||||||
|
`TJWater 智慧水务平台` 品牌名,并清除 `tjwater` client 对登录主题的覆盖,
|
||||||
|
使其继承 realm 主题。其他环境可临时覆盖:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
TJWATER_KEYCLOAK_REALM=example \
|
||||||
|
TJWATER_KEYCLOAK_CLIENT_ID=example-web \
|
||||||
|
TJWATER_KEYCLOAK_DISPLAY_NAME="示例智慧水务平台" \
|
||||||
|
bash infra/docker/keycloak/configure-theme.sh apply
|
||||||
|
```
|
||||||
|
|
||||||
|
管理员凭据继续使用 Compose 已注入的 `KC_BOOTSTRAP_ADMIN_USERNAME` /
|
||||||
|
`KC_BOOTSTRAP_ADMIN_PASSWORD`,并兼容现有的 `KEYCLOAK_ADMIN` /
|
||||||
|
`KEYCLOAK_ADMIN_PASSWORD`。
|
||||||
|
|
||||||
|
## 验证与回滚
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash infra/docker/keycloak/configure-theme.sh verify
|
||||||
|
bash infra/docker/keycloak/configure-theme.sh rollback
|
||||||
|
```
|
||||||
|
|
||||||
|
使用 `latest` 镜像时,每次重新拉取 Keycloak 后都应重新执行 `verify`,并在
|
||||||
|
1280px、375px 和 320px 视口检查登录、错误提示、忘记密码和 OTP 页面。
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
action="${1:-apply}"
|
||||||
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
repo_root="$(cd -- "${script_dir}/../../.." && pwd)"
|
||||||
|
compose_file="${repo_root}/infra/docker/docker-compose.yml"
|
||||||
|
realm="${TJWATER_KEYCLOAK_REALM:-tjwater}"
|
||||||
|
client_id="${TJWATER_KEYCLOAK_CLIENT_ID:-tjwater}"
|
||||||
|
display_name="${TJWATER_KEYCLOAK_DISPLAY_NAME:-TJWater 智慧水务平台}"
|
||||||
|
|
||||||
|
case "${action}" in
|
||||||
|
apply|verify|rollback) ;;
|
||||||
|
*)
|
||||||
|
echo "用法: bash infra/docker/keycloak/configure-theme.sh [apply|verify|rollback]" >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
compose_args=(docker compose)
|
||||||
|
if [[ -f "${repo_root}/.env" ]]; then
|
||||||
|
compose_args+=(--env-file "${repo_root}/.env")
|
||||||
|
fi
|
||||||
|
compose_args+=(-f "${compose_file}")
|
||||||
|
|
||||||
|
"${compose_args[@]}" exec -T \
|
||||||
|
-e TJWATER_KEYCLOAK_ACTION="${action}" \
|
||||||
|
-e TJWATER_KEYCLOAK_REALM="${realm}" \
|
||||||
|
-e TJWATER_KEYCLOAK_CLIENT_ID="${client_id}" \
|
||||||
|
-e TJWATER_KEYCLOAK_DISPLAY_NAME="${display_name}" \
|
||||||
|
keycloak sh -s <<'KEYCLOAK_SCRIPT'
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
action="${TJWATER_KEYCLOAK_ACTION}"
|
||||||
|
realm="${TJWATER_KEYCLOAK_REALM}"
|
||||||
|
client_id="${TJWATER_KEYCLOAK_CLIENT_ID}"
|
||||||
|
display_name="${TJWATER_KEYCLOAK_DISPLAY_NAME}"
|
||||||
|
server_url="${TJWATER_KEYCLOAK_SERVER_URL:-http://127.0.0.1:8080}"
|
||||||
|
admin_user="${KC_BOOTSTRAP_ADMIN_USERNAME:-${KEYCLOAK_ADMIN:-}}"
|
||||||
|
admin_password="${KC_BOOTSTRAP_ADMIN_PASSWORD:-${KEYCLOAK_ADMIN_PASSWORD:-}}"
|
||||||
|
config_file="/tmp/tjwater-kcadm-$$.config"
|
||||||
|
kcadm="/opt/keycloak/bin/kcadm.sh"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -f "${config_file}"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if [ -z "${admin_user}" ] || [ -z "${admin_password}" ]; then
|
||||||
|
echo "缺少 Keycloak 管理员用户名或密码环境变量。" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${action}" = "apply" ] && [ ! -f /opt/keycloak/themes/tjwater/login/theme.properties ]; then
|
||||||
|
echo "未找到 tjwater 登录主题,请检查主题目录挂载。" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${kcadm}" config credentials \
|
||||||
|
--config "${config_file}" \
|
||||||
|
--server "${server_url}" \
|
||||||
|
--realm master \
|
||||||
|
--user "${admin_user}" \
|
||||||
|
--password "${admin_password}" >/dev/null
|
||||||
|
|
||||||
|
client_uuid="$(
|
||||||
|
"${kcadm}" get clients \
|
||||||
|
--config "${config_file}" \
|
||||||
|
--target-realm "${realm}" \
|
||||||
|
--query "clientId=${client_id}" \
|
||||||
|
--fields id \
|
||||||
|
--format csv \
|
||||||
|
--noquotes |
|
||||||
|
sed -n '1p'
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [ -z "${client_uuid}" ]; then
|
||||||
|
echo "realm ${realm} 中未找到 client ${client_id}。" >&2
|
||||||
|
echo "可通过 TJWATER_KEYCLOAK_CLIENT_ID 指定实际的 client ID。" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${action}" in
|
||||||
|
apply)
|
||||||
|
"${kcadm}" update "realms/${realm}" \
|
||||||
|
--config "${config_file}" \
|
||||||
|
-s "displayName=${display_name}" \
|
||||||
|
-s "displayNameHtml=${display_name}" \
|
||||||
|
-s "loginTheme=tjwater" \
|
||||||
|
-s "internationalizationEnabled=true" \
|
||||||
|
-s 'supportedLocales=["zh-CN","en"]' \
|
||||||
|
-s "defaultLocale=zh-CN" >/dev/null
|
||||||
|
"${kcadm}" update "clients/${client_uuid}" \
|
||||||
|
--config "${config_file}" \
|
||||||
|
--target-realm "${realm}" \
|
||||||
|
--set attributes.login_theme= >/dev/null
|
||||||
|
echo "已为 realm ${realm} 启用 tjwater 登录主题。"
|
||||||
|
echo "client ${client_id} 已改为继承 realm 登录主题。"
|
||||||
|
;;
|
||||||
|
rollback)
|
||||||
|
"${kcadm}" update "realms/${realm}" \
|
||||||
|
--config "${config_file}" \
|
||||||
|
-s "loginTheme=keycloak" >/dev/null
|
||||||
|
echo "已将 realm ${realm} 恢复为 Keycloak 默认登录主题。"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
"${kcadm}" get "realms/${realm}" \
|
||||||
|
--config "${config_file}" \
|
||||||
|
--fields realm,displayName,loginTheme,internationalizationEnabled,supportedLocales,defaultLocale
|
||||||
|
"${kcadm}" get "clients/${client_uuid}" \
|
||||||
|
--config "${config_file}" \
|
||||||
|
--target-realm "${realm}" \
|
||||||
|
--fields 'clientId,attributes(login_theme)'
|
||||||
|
KEYCLOAK_SCRIPT
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
loginAccountTitle=Account sign in
|
||||||
|
doLogIn=Sign in
|
||||||
|
doForgotPassword=Forgot password
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
loginAccountTitle=账号登录
|
||||||
|
usernameOrEmail=用户名或邮箱
|
||||||
|
doLogIn=登录
|
||||||
|
doForgotPassword=忘记密码
|
||||||
|
rememberMe=记住我
|
||||||
|
invalidUserMessage=用户名或密码错误
|
||||||
|
invalidUsernameOrPasswordMessage=用户名或密码错误
|
||||||
|
expiredCodeMessage=登录已超时,请重新登录
|
||||||
|
loginTimeout=登录已超时,请重新开始登录
|
||||||
@@ -0,0 +1,535 @@
|
|||||||
|
:root {
|
||||||
|
--tjwater-canvas: oklch(0.965 0.014 205);
|
||||||
|
--tjwater-surface: oklch(0.995 0.004 205);
|
||||||
|
--tjwater-surface-soft: oklch(0.982 0.008 205);
|
||||||
|
--tjwater-ink: oklch(0.3 0.055 215);
|
||||||
|
--tjwater-muted: oklch(0.52 0.035 215);
|
||||||
|
--tjwater-line: oklch(0.86 0.025 210);
|
||||||
|
--tjwater-blue: oklch(0.57 0.16 242);
|
||||||
|
--tjwater-blue-dark: oklch(0.49 0.15 242);
|
||||||
|
--tjwater-teal: oklch(0.58 0.12 180);
|
||||||
|
--tjwater-danger: oklch(0.55 0.19 27);
|
||||||
|
--tjwater-radius-sm: 6px;
|
||||||
|
--tjwater-radius-md: 12px;
|
||||||
|
--tjwater-radius-lg: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.login-pf {
|
||||||
|
height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background: var(--tjwater-canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
body#keycloak-bg,
|
||||||
|
.login-pf body {
|
||||||
|
min-height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--tjwater-ink);
|
||||||
|
background: var(--tjwater-canvas);
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
|
||||||
|
"PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login,
|
||||||
|
.pf-v5-c-login * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login {
|
||||||
|
min-height: 100svh;
|
||||||
|
padding: 0;
|
||||||
|
background-color: var(--tjwater-canvas);
|
||||||
|
background-image:
|
||||||
|
linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent 0%,
|
||||||
|
transparent 50%,
|
||||||
|
oklch(0.975 0.01 205 / 62%) 68%,
|
||||||
|
oklch(0.975 0.01 205 / 82%) 100%
|
||||||
|
),
|
||||||
|
url("../img/network-blueprint.svg");
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__container {
|
||||||
|
display: grid;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1720px;
|
||||||
|
min-height: 100svh;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: clamp(32px, 4.5vw, 76px) clamp(40px, 5vw, 88px);
|
||||||
|
grid-template-columns: minmax(360px, 1fr) minmax(400px, 460px);
|
||||||
|
grid-template-areas: "header main";
|
||||||
|
align-items: center;
|
||||||
|
gap: clamp(64px, 8vw, 152px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header {
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
grid-area: header;
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 100%;
|
||||||
|
align-self: center;
|
||||||
|
justify-self: start;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
isolation: isolate;
|
||||||
|
animation: tjwater-enter 480ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header::before {
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
inset: -54px -72px;
|
||||||
|
background: radial-gradient(
|
||||||
|
ellipse at center,
|
||||||
|
oklch(0.925 0.018 205 / 98%) 0%,
|
||||||
|
oklch(0.925 0.018 205 / 94%) 48%,
|
||||||
|
oklch(0.925 0.018 205 / 62%) 65%,
|
||||||
|
transparent 82%
|
||||||
|
);
|
||||||
|
pointer-events: none;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper {
|
||||||
|
display: flex;
|
||||||
|
max-width: 680px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
color: var(--tjwater-ink) !important;
|
||||||
|
font-size: clamp(34px, 3vw, 46px);
|
||||||
|
font-weight: 720;
|
||||||
|
line-height: 1.28;
|
||||||
|
letter-spacing: 0;
|
||||||
|
text-align: left;
|
||||||
|
text-transform: none;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper::before {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
background: url("../img/logo-mark.svg") center / contain no-repeat;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper::after {
|
||||||
|
width: 64px;
|
||||||
|
height: 3px;
|
||||||
|
margin-top: 26px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--tjwater-teal);
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main {
|
||||||
|
grid-area: main;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 460px;
|
||||||
|
margin: 0;
|
||||||
|
align-self: center;
|
||||||
|
justify-self: stretch;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 0;
|
||||||
|
border-radius: var(--tjwater-radius-lg);
|
||||||
|
background: oklch(0.995 0.004 205 / 97%);
|
||||||
|
box-shadow:
|
||||||
|
0 32px 80px rgb(22 65 75 / 16%),
|
||||||
|
0 5px 18px rgb(22 65 75 / 9%);
|
||||||
|
animation: tjwater-enter 520ms 70ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-header {
|
||||||
|
display: grid;
|
||||||
|
margin: 0;
|
||||||
|
padding: 36px 36px 18px;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: center;
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-page-title {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--tjwater-ink);
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 720;
|
||||||
|
line-height: 1.4;
|
||||||
|
letter-spacing: 0;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-header-utilities {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 36px 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form {
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form__group {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form__group-label {
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form__label-text {
|
||||||
|
color: var(--tjwater-ink);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 650;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form-control {
|
||||||
|
min-height: 48px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--tjwater-line);
|
||||||
|
border-radius: var(--tjwater-radius-sm);
|
||||||
|
background: var(--tjwater-surface-soft);
|
||||||
|
box-shadow: none;
|
||||||
|
transition-property: border-color, box-shadow, background-color;
|
||||||
|
transition-duration: 160ms;
|
||||||
|
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form-control::before,
|
||||||
|
.pf-v5-c-form-control::after {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form-control:focus-within {
|
||||||
|
border-color: var(--tjwater-blue);
|
||||||
|
background: var(--tjwater-surface);
|
||||||
|
box-shadow: 0 0 0 3px oklch(0.78 0.1 235 / 28%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form-control > input,
|
||||||
|
.pf-v5-c-form-control > select {
|
||||||
|
min-height: 46px;
|
||||||
|
padding-inline: 14px;
|
||||||
|
color: var(--tjwater-ink);
|
||||||
|
font-size: 16px;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-header-utilities .pf-v5-c-form-control {
|
||||||
|
width: 116px;
|
||||||
|
min-height: 40px;
|
||||||
|
background: var(--tjwater-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
#login-select-toggle {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 38px;
|
||||||
|
padding-inline: 12px 32px;
|
||||||
|
color: var(--tjwater-muted);
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form-control.pf-m-error {
|
||||||
|
border-color: var(--tjwater-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-input-group {
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-input-group__item.pf-m-fill {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-button.pf-m-control {
|
||||||
|
min-width: 48px;
|
||||||
|
min-height: 48px;
|
||||||
|
border: 1px solid var(--tjwater-line);
|
||||||
|
border-radius: var(--tjwater-radius-sm);
|
||||||
|
color: var(--tjwater-muted);
|
||||||
|
background: var(--tjwater-surface-soft);
|
||||||
|
touch-action: manipulation;
|
||||||
|
transition-property: color, border-color, background-color, transform;
|
||||||
|
transition-duration: 160ms;
|
||||||
|
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-button.pf-m-control:active {
|
||||||
|
transform: scale(0.96);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-button.pf-m-control:focus-visible,
|
||||||
|
.pf-v5-c-button.pf-m-primary:focus-visible,
|
||||||
|
.pf-v5-c-button.pf-m-secondary:focus-visible,
|
||||||
|
a:focus-visible {
|
||||||
|
outline: 3px solid oklch(0.74 0.12 235 / 60%);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form__helper-text {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-helper-text {
|
||||||
|
min-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-helper-text__item-text {
|
||||||
|
color: var(--tjwater-muted);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-helper-text__item-text a,
|
||||||
|
#kc-registration a,
|
||||||
|
.pf-v5-c-login__main-footer a {
|
||||||
|
color: var(--tjwater-blue-dark);
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
text-underline-offset: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kc-feedback-text.pf-m-error,
|
||||||
|
.pf-v5-c-helper-text__item.pf-m-error .kc-feedback-text {
|
||||||
|
color: var(--tjwater-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-check__input {
|
||||||
|
accent-color: var(--tjwater-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-check__label {
|
||||||
|
color: var(--tjwater-muted);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-form__actions {
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-button.pf-m-primary {
|
||||||
|
min-height: 48px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: var(--tjwater-radius-md);
|
||||||
|
color: oklch(0.99 0.004 230);
|
||||||
|
background: var(--tjwater-blue);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
box-shadow: 0 8px 18px oklch(0.48 0.15 242 / 20%);
|
||||||
|
touch-action: manipulation;
|
||||||
|
transition-property: transform, background-color, box-shadow;
|
||||||
|
transition-duration: 160ms;
|
||||||
|
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-button.pf-m-primary:active {
|
||||||
|
transform: scale(0.96);
|
||||||
|
background: var(--tjwater-blue-dark);
|
||||||
|
box-shadow: 0 4px 10px oklch(0.48 0.15 242 / 18%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-button.pf-m-secondary {
|
||||||
|
min-height: 44px;
|
||||||
|
border-radius: var(--tjwater-radius-md);
|
||||||
|
color: var(--tjwater-blue-dark);
|
||||||
|
border-color: var(--tjwater-line);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-alert {
|
||||||
|
border-radius: var(--tjwater-radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-footer {
|
||||||
|
color: var(--tjwater-muted);
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-footer-band {
|
||||||
|
margin-top: 26px;
|
||||||
|
padding: 18px 0 0;
|
||||||
|
border-top: 1px solid var(--tjwater-line);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes tjwater-enter {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.pf-v5-c-button.pf-m-primary:hover {
|
||||||
|
background: var(--tjwater-blue-dark);
|
||||||
|
box-shadow: 0 10px 22px oklch(0.48 0.15 242 / 25%);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-button.pf-m-control:hover {
|
||||||
|
color: var(--tjwater-blue-dark);
|
||||||
|
border-color: oklch(0.69 0.08 230);
|
||||||
|
background: var(--tjwater-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-helper-text__item-text a:hover,
|
||||||
|
#kc-registration a:hover,
|
||||||
|
.pf-v5-c-login__main-footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.pf-v5-c-login {
|
||||||
|
background-image:
|
||||||
|
linear-gradient(oklch(0.965 0.014 205 / 34%), oklch(0.965 0.014 205 / 34%)),
|
||||||
|
url("../img/network-blueprint.svg");
|
||||||
|
background-position: 34% center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__container {
|
||||||
|
max-width: 560px;
|
||||||
|
padding:
|
||||||
|
max(28px, env(safe-area-inset-top))
|
||||||
|
max(24px, env(safe-area-inset-right))
|
||||||
|
max(32px, env(safe-area-inset-bottom))
|
||||||
|
max(24px, env(safe-area-inset-left));
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
grid-template-areas:
|
||||||
|
"header"
|
||||||
|
"main";
|
||||||
|
align-content: center;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper {
|
||||||
|
max-width: none;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
font-size: clamp(22px, 5vw, 28px);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header::before {
|
||||||
|
inset: -24px -20px;
|
||||||
|
background: radial-gradient(
|
||||||
|
ellipse at center,
|
||||||
|
oklch(0.965 0.014 205 / 98%) 0%,
|
||||||
|
oklch(0.965 0.014 205 / 88%) 58%,
|
||||||
|
transparent 84%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper::before {
|
||||||
|
width: 46px;
|
||||||
|
height: 46px;
|
||||||
|
margin: 0;
|
||||||
|
flex: 0 0 46px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 520px) {
|
||||||
|
.pf-v5-c-login__container {
|
||||||
|
gap: 18px;
|
||||||
|
padding-inline:
|
||||||
|
max(14px, env(safe-area-inset-left))
|
||||||
|
max(14px, env(safe-area-inset-right));
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper {
|
||||||
|
gap: 12px;
|
||||||
|
font-size: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper::before {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
flex-basis: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-header {
|
||||||
|
gap: 12px;
|
||||||
|
padding: 26px 22px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-page-title {
|
||||||
|
font-size: 23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-header-utilities .pf-v5-c-form-control {
|
||||||
|
width: 108px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-body {
|
||||||
|
padding: 0 22px 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height: 680px) and (min-width: 901px) {
|
||||||
|
.pf-v5-c-login__container {
|
||||||
|
padding-block: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper::before {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kc-header-wrapper::after {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-header {
|
||||||
|
padding-top: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-login__main-body {
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
#kc-header,
|
||||||
|
.pf-v5-c-login__main {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-v5-c-button,
|
||||||
|
.pf-v5-c-form-control {
|
||||||
|
transition-duration: 0.01ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72" role="img" aria-labelledby="title">
|
||||||
|
<title id="title">TJWater</title>
|
||||||
|
<rect width="72" height="72" rx="18" fill="#1478d4"/>
|
||||||
|
<path d="M36 13c-7.8 11.1-16.1 19.4-16.1 29.3A16.1 16.1 0 0 0 36 58.4a16.1 16.1 0 0 0 16.1-16.1C52.1 32.4 43.8 24.1 36 13Z" fill="#f5fbfc"/>
|
||||||
|
<path d="M26.5 43.5h19M31 36.5l5 7 5-7" fill="none" stroke="#0b8f82" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/>
|
||||||
|
<circle cx="26.5" cy="43.5" r="2.7" fill="#0b8f82"/>
|
||||||
|
<circle cx="45.5" cy="43.5" r="2.7" fill="#0b8f82"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 585 B |
@@ -0,0 +1,39 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 900" preserveAspectRatio="xMidYMid slice">
|
||||||
|
<rect width="1440" height="900" fill="#edf5f5"/>
|
||||||
|
<path d="M0 0h790L650 900H0Z" fill="#dceced"/>
|
||||||
|
<path d="M0 182c187-78 303-73 451-16 153 59 288 47 409-25M0 552c193-45 327-19 461 74 128 90 266 100 410 47" fill="none" stroke="#c8dddd" stroke-width="2"/>
|
||||||
|
<g fill="none" stroke="#a9ccce" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M-32 725 178 608l142 51 155-190 184 67 176-205" stroke-width="5"/>
|
||||||
|
<path d="m178 608 25-222 151-92 121 175" stroke-width="3"/>
|
||||||
|
<path d="m203 386-92-95 55-161M354 294l92-141 152 58 98-106" stroke-width="3"/>
|
||||||
|
<path d="m320 659-4 132 171 81M659 536l88 116 124-42" stroke-width="3"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#edf5f5" stroke="#1478d4" stroke-width="4">
|
||||||
|
<circle cx="178" cy="608" r="10"/>
|
||||||
|
<circle cx="203" cy="386" r="9"/>
|
||||||
|
<circle cx="354" cy="294" r="9"/>
|
||||||
|
<circle cx="475" cy="469" r="11"/>
|
||||||
|
<circle cx="659" cy="536" r="10"/>
|
||||||
|
<circle cx="747" cy="652" r="9"/>
|
||||||
|
<circle cx="320" cy="659" r="8"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#0b8f82">
|
||||||
|
<circle cx="111" cy="291" r="6"/>
|
||||||
|
<circle cx="166" cy="130" r="6"/>
|
||||||
|
<circle cx="446" cy="153" r="7"/>
|
||||||
|
<circle cx="598" cy="211" r="6"/>
|
||||||
|
<circle cx="696" cy="105" r="6"/>
|
||||||
|
<circle cx="316" cy="791" r="6"/>
|
||||||
|
<circle cx="487" cy="872" r="6"/>
|
||||||
|
<circle cx="835" cy="331" r="7"/>
|
||||||
|
</g>
|
||||||
|
<g fill="none" stroke="#86b8bb" stroke-width="2" opacity=".72">
|
||||||
|
<circle cx="615" cy="448" r="222"/>
|
||||||
|
<circle cx="615" cy="448" r="276"/>
|
||||||
|
<circle cx="615" cy="448" r="334"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#1478d4" opacity=".08">
|
||||||
|
<rect x="40" y="40" width="118" height="10" rx="5"/>
|
||||||
|
<rect x="40" y="62" width="72" height="6" rx="3"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
const localizeLocaleOptions = () => {
|
||||||
|
const localeSelect = document.querySelector("#login-select-toggle");
|
||||||
|
|
||||||
|
if (!(localeSelect instanceof HTMLSelectElement)) return;
|
||||||
|
|
||||||
|
for (const option of localeSelect.options) {
|
||||||
|
const optionUrl = new URL(option.value, window.location.origin);
|
||||||
|
const locale = optionUrl.searchParams.get("kc_locale");
|
||||||
|
|
||||||
|
if (locale === "zh-CN") option.textContent = "简体中文";
|
||||||
|
if (locale === "en") option.textContent = "English";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", localizeLocaleOptions, {
|
||||||
|
once: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
localizeLocaleOptions();
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
parent=keycloak.v2
|
||||||
|
import=common/keycloak
|
||||||
|
|
||||||
|
styles=css/styles.css css/tjwater-login.css
|
||||||
|
scripts=js/locale-labels.js
|
||||||
|
locales=zh-CN,en
|
||||||
|
darkMode=false
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||||
|
|
||||||
|
HTTP_METHODS = {"get", "post", "put", "patch", "delete", "head", "options"}
|
||||||
|
KEBAB_SEGMENT = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
||||||
|
SNAKE_PARAMETER = re.compile(r"^[a-z][a-z0-9_]*$")
|
||||||
|
|
||||||
|
|
||||||
|
def canonical_json(document: dict[str, Any]) -> bytes:
|
||||||
|
return (
|
||||||
|
json.dumps(document, ensure_ascii=False, indent=2, sort_keys=True).encode("utf-8")
|
||||||
|
+ b"\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def current_contract_bytes() -> bytes:
|
||||||
|
os.environ.setdefault("ENVIRONMENT", "development")
|
||||||
|
from app.main import app
|
||||||
|
|
||||||
|
document = app.openapi()
|
||||||
|
document["info"]["version"] = "1.0.0"
|
||||||
|
return canonical_json(document)
|
||||||
|
|
||||||
|
|
||||||
|
def _iter_operations(document: dict[str, Any]):
|
||||||
|
for path, path_item in document.get("paths", {}).items():
|
||||||
|
for method, operation in path_item.items():
|
||||||
|
if method in HTTP_METHODS and isinstance(operation, dict):
|
||||||
|
yield path, method, operation
|
||||||
|
|
||||||
|
|
||||||
|
def validate(document: dict[str, Any]) -> list[str]:
|
||||||
|
errors: list[str] = []
|
||||||
|
operation_ids: set[str] = set()
|
||||||
|
|
||||||
|
for path, method, operation in _iter_operations(document):
|
||||||
|
if path != path.rstrip("/"):
|
||||||
|
errors.append(f"{method.upper()} {path}: trailing slash")
|
||||||
|
if "//" in path:
|
||||||
|
errors.append(f"{method.upper()} {path}: double slash")
|
||||||
|
for segment in path.split("/"):
|
||||||
|
if not segment or (segment.startswith("{") and segment.endswith("}")):
|
||||||
|
continue
|
||||||
|
if not KEBAB_SEGMENT.fullmatch(segment):
|
||||||
|
errors.append(f"{method.upper()} {path}: non-kebab segment {segment!r}")
|
||||||
|
|
||||||
|
operation_id = operation.get("operationId")
|
||||||
|
if not operation_id:
|
||||||
|
errors.append(f"{method.upper()} {path}: missing operationId")
|
||||||
|
elif operation_id in operation_ids:
|
||||||
|
errors.append(f"{method.upper()} {path}: duplicate operationId {operation_id}")
|
||||||
|
else:
|
||||||
|
operation_ids.add(operation_id)
|
||||||
|
|
||||||
|
if not operation.get("tags"):
|
||||||
|
errors.append(f"{method.upper()} {path}: missing tags")
|
||||||
|
if not operation.get("summary"):
|
||||||
|
errors.append(f"{method.upper()} {path}: missing summary")
|
||||||
|
for parameter in operation.get("parameters", []):
|
||||||
|
if (
|
||||||
|
parameter.get("in") in {"query", "path"}
|
||||||
|
and not SNAKE_PARAMETER.fullmatch(str(parameter.get("name", "")))
|
||||||
|
):
|
||||||
|
errors.append(
|
||||||
|
f"{method.upper()} {path}: non-snake parameter "
|
||||||
|
f"{parameter.get('name')!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
success_responses = [
|
||||||
|
(status, response)
|
||||||
|
for status, response in operation.get("responses", {}).items()
|
||||||
|
if str(status).startswith("2")
|
||||||
|
]
|
||||||
|
if not success_responses:
|
||||||
|
errors.append(f"{method.upper()} {path}: missing success response")
|
||||||
|
for status, response in success_responses:
|
||||||
|
if str(status) == "204":
|
||||||
|
continue
|
||||||
|
if "content" not in response:
|
||||||
|
errors.append(f"{method.upper()} {path}: success response has no content schema")
|
||||||
|
for media in response.get("content", {}).values():
|
||||||
|
if media.get("schema") == {}:
|
||||||
|
errors.append(f"{method.upper()} {path}: empty success schema")
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="Validate TJWater REST OpenAPI invariants")
|
||||||
|
parser.add_argument(
|
||||||
|
"contract",
|
||||||
|
nargs="?",
|
||||||
|
type=Path,
|
||||||
|
default=Path("contracts/server-v1.openapi.json"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--manifest",
|
||||||
|
type=Path,
|
||||||
|
default=Path("contracts/manifest.json"),
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
raw = args.contract.read_bytes()
|
||||||
|
document = json.loads(raw)
|
||||||
|
errors = validate(document)
|
||||||
|
|
||||||
|
manifest = json.loads(args.manifest.read_text(encoding="utf-8"))
|
||||||
|
expected_hash = manifest["contracts"]["server"]["sha256"]
|
||||||
|
actual_hash = hashlib.sha256(raw).hexdigest()
|
||||||
|
if expected_hash != actual_hash:
|
||||||
|
errors.append(
|
||||||
|
f"contract hash mismatch: manifest={expected_hash}, actual={actual_hash}"
|
||||||
|
)
|
||||||
|
current = current_contract_bytes()
|
||||||
|
if raw != current:
|
||||||
|
errors.append(
|
||||||
|
"contract is stale: run "
|
||||||
|
"`python scripts/export_openapi.py` and commit the regenerated files"
|
||||||
|
)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
print("\n".join(f"- {error}" for error in errors))
|
||||||
|
return 1
|
||||||
|
print(
|
||||||
|
f"validated {len(document['paths'])} paths; "
|
||||||
|
f"sha256={actual_hash}; version={document['info']['version']}"
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
def _canonical_json(document: dict[str, Any]) -> bytes:
|
||||||
|
return (
|
||||||
|
json.dumps(document, ensure_ascii=False, indent=2, sort_keys=True).encode("utf-8")
|
||||||
|
+ b"\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="Export the TJWater REST v1 OpenAPI contract")
|
||||||
|
parser.add_argument(
|
||||||
|
"--output",
|
||||||
|
type=Path,
|
||||||
|
default=Path("contracts/server-v1.openapi.json"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--manifest",
|
||||||
|
type=Path,
|
||||||
|
default=Path("contracts/manifest.json"),
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
os.environ.setdefault("ENVIRONMENT", "development")
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||||
|
|
||||||
|
from app.main import app
|
||||||
|
|
||||||
|
document = app.openapi()
|
||||||
|
document["info"]["version"] = "1.0.0"
|
||||||
|
payload = _canonical_json(document)
|
||||||
|
digest = hashlib.sha256(payload).hexdigest()
|
||||||
|
|
||||||
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
args.manifest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
args.output.write_bytes(payload)
|
||||||
|
args.manifest.write_bytes(
|
||||||
|
_canonical_json(
|
||||||
|
{
|
||||||
|
"contract_version": "1.0.0",
|
||||||
|
"contracts": {
|
||||||
|
"server": {
|
||||||
|
"file": args.output.name,
|
||||||
|
"sha256": digest,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(f"exported {len(document['paths'])} paths to {args.output} ({digest})")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -34,7 +34,7 @@ def test_access_context_returns_global_admin_permissions_without_project():
|
|||||||
repo = SimpleNamespace()
|
repo = SimpleNamespace()
|
||||||
client = _build_client(user, repo)
|
client = _build_client(user, repo)
|
||||||
|
|
||||||
response = client.get("/api/v1/access/context")
|
response = client.get("/api/v1/access-context")
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
payload = response.json()
|
payload = response.json()
|
||||||
@@ -64,7 +64,7 @@ def test_access_context_returns_project_member_permissions():
|
|||||||
client = _build_client(user, repo)
|
client = _build_client(user, repo)
|
||||||
|
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/access/context",
|
"/api/v1/access-context",
|
||||||
headers={"X-Project-Id": str(project_id)},
|
headers={"X-Project-Id": str(project_id)},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def test_agent_auth_context_returns_metadata_user_and_project_context():
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
response = client.get("/api/v1/agent/auth/context")
|
response = client.get("/api/v1/agent-auth-context")
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json() == {
|
assert response.json() == {
|
||||||
@@ -90,7 +90,7 @@ def test_agent_auth_context_propagates_project_auth_failures():
|
|||||||
app.dependency_overrides[get_current_keycloak_payload] = lambda: {"exp": 1781183400}
|
app.dependency_overrides[get_current_keycloak_payload] = lambda: {"exp": 1781183400}
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
|
|
||||||
response = client.get("/api/v1/agent/auth/context")
|
response = client.get("/api/v1/agent-auth-context")
|
||||||
|
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
assert response.json()["detail"] == "No access to project"
|
assert response.json()["detail"] == "No access to project"
|
||||||
|
|||||||
@@ -48,9 +48,9 @@ def test_router_configuration():
|
|||||||
routes = [r.path for r in api_router.routes if hasattr(r, "path")]
|
routes = [r.path for r in api_router.routes if hasattr(r, "path")]
|
||||||
|
|
||||||
# 验证基础路径是否存在
|
# 验证基础路径是否存在
|
||||||
assert any("/agent/auth/context" in r for r in routes), "缺少 Agent 认证上下文路由"
|
assert "/agent-auth-context" in routes, "缺少 Agent 认证上下文路由"
|
||||||
assert any("/meta" in r for r in routes), "缺少 Metadata 路由"
|
assert "/projects/current/metadata" in routes, "缺少 Metadata 路由"
|
||||||
assert any("/audit" in r for r in routes), "缺少审计日志路由 (/audit)"
|
assert "/audit-logs" in routes, "缺少审计日志路由"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pytest.fail(f"路由配置检查失败: {e}")
|
pytest.fail(f"路由配置检查失败: {e}")
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def _build_client(
|
|||||||
metadata_admin=None,
|
metadata_admin=None,
|
||||||
metadata_user=None,
|
metadata_user=None,
|
||||||
) -> TestClient:
|
) -> TestClient:
|
||||||
app = build_test_app(audit_endpoint.router, "/audit")
|
app = build_test_app(audit_endpoint.router, "/api/v1")
|
||||||
app.dependency_overrides[audit_endpoint.get_audit_repository] = lambda: repo
|
app.dependency_overrides[audit_endpoint.get_audit_repository] = lambda: repo
|
||||||
if metadata_admin is not None:
|
if metadata_admin is not None:
|
||||||
app.dependency_overrides[get_current_metadata_admin] = lambda: metadata_admin
|
app.dependency_overrides[get_current_metadata_admin] = lambda: metadata_admin
|
||||||
@@ -38,7 +38,7 @@ def test_get_audit_logs_passes_filters():
|
|||||||
client = _build_client(repo, metadata_admin=object())
|
client = _build_client(repo, metadata_admin=object())
|
||||||
|
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/audit/logs",
|
"/api/v1/audit-logs",
|
||||||
params={
|
params={
|
||||||
"action": "LOGIN",
|
"action": "LOGIN",
|
||||||
"resource_type": "user",
|
"resource_type": "user",
|
||||||
@@ -68,7 +68,7 @@ def test_get_audit_logs_count_returns_count_payload():
|
|||||||
)()
|
)()
|
||||||
client = _build_client(repo, metadata_admin=object())
|
client = _build_client(repo, metadata_admin=object())
|
||||||
|
|
||||||
response = client.get("/audit/logs/count", params={"action": "DELETE_USER"})
|
response = client.get("/api/v1/audit-logs/count", params={"action": "DELETE_USER"})
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json() == {"count": 7}
|
assert response.json() == {"count": 7}
|
||||||
@@ -88,7 +88,7 @@ def test_get_my_audit_logs_forces_current_user_id():
|
|||||||
)()
|
)()
|
||||||
client = _build_client(repo, metadata_user=current_user)
|
client = _build_client(repo, metadata_user=current_user)
|
||||||
|
|
||||||
response = client.get("/audit/logs/my", params={"limit": 3})
|
response = client.get("/api/v1/audit-logs/mine", params={"limit": 3})
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
repo.get_logs.assert_awaited_once()
|
repo.get_logs.assert_awaited_once()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from app.api.v1.endpoints import leakage as leakage_endpoint
|
|||||||
|
|
||||||
def _build_client() -> TestClient:
|
def _build_client() -> TestClient:
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
app.include_router(leakage_endpoint.router, prefix="/api/v1/leakage")
|
app.include_router(leakage_endpoint.router, prefix="/api/v1")
|
||||||
app.dependency_overrides[leakage_endpoint.get_current_keycloak_username] = (
|
app.dependency_overrides[leakage_endpoint.get_current_keycloak_username] = (
|
||||||
lambda: "tester"
|
lambda: "tester"
|
||||||
)
|
)
|
||||||
@@ -23,7 +23,7 @@ def test_identify_leakage_success(monkeypatch):
|
|||||||
)
|
)
|
||||||
client = _build_client()
|
client = _build_client()
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/leakage/identify/",
|
"/api/v1/leakage-identifications",
|
||||||
json={
|
json={
|
||||||
"network": "demo",
|
"network": "demo",
|
||||||
"scada_start": "2026-01-01T00:00:00+08:00",
|
"scada_start": "2026-01-01T00:00:00+08:00",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ def test_meta_project_returns_map_extent(monkeypatch):
|
|||||||
app.dependency_overrides[module.get_metadata_repository] = lambda: repo
|
app.dependency_overrides[module.get_metadata_repository] = lambda: repo
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
|
|
||||||
response = client.get("/api/v1/meta/project")
|
response = client.get("/api/v1/projects/current/metadata")
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json()["map_extent"] == {"xmin": 1, "ymin": 2, "xmax": 3, "ymax": 4}
|
assert response.json()["map_extent"] == {"xmin": 1, "ymin": 2, "xmax": 3, "ymax": 4}
|
||||||
@@ -81,7 +81,7 @@ def test_meta_db_health_returns_503_for_postgres_errors(monkeypatch):
|
|||||||
app.dependency_overrides[module.get_project_timescale_connection] = lambda: DummyTimescaleConnection()
|
app.dependency_overrides[module.get_project_timescale_connection] = lambda: DummyTimescaleConnection()
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
|
|
||||||
response = client.get("/api/v1/meta/db/health")
|
response = client.get("/api/v1/projects/current/database-health")
|
||||||
|
|
||||||
assert response.status_code == 503
|
assert response.status_code == 503
|
||||||
assert response.json()["detail"] == "Project PostgreSQL health check failed: pg unavailable"
|
assert response.json()["detail"] == "Project PostgreSQL health check failed: pg unavailable"
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ def test_system_admin_can_import_model_without_project_membership(
|
|||||||
client = _client(admin=admin, repo=repo)
|
client = _client(admin=admin, repo=repo)
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
f"/api/v1/admin/projects/{project_id}/model/import",
|
f"/api/v1/admin/projects/{project_id}/model-imports",
|
||||||
files={"file": ("desktop-model.inp", VALID_INP)},
|
files={"file": ("desktop-model.inp", VALID_INP)},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ def test_non_admin_is_denied_model_import():
|
|||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
f"/api/v1/admin/projects/{uuid4()}/model/import",
|
f"/api/v1/admin/projects/{uuid4()}/model-imports",
|
||||||
files={"file": ("desktop-model.inp", VALID_INP)},
|
files={"file": ("desktop-model.inp", VALID_INP)},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ def test_model_import_rejects_non_inp_file(monkeypatch):
|
|||||||
)
|
)
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
f"/api/v1/admin/projects/{project_id}/model/import",
|
f"/api/v1/admin/projects/{project_id}/model-imports",
|
||||||
files={"file": ("desktop-model.txt", VALID_INP)},
|
files={"file": ("desktop-model.txt", VALID_INP)},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.routing import APIRoute
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from app.api.v1.endpoints import schemes as schemes_endpoint
|
||||||
|
from app.api.v1.endpoints import simulation as simulation_endpoint
|
||||||
|
from app.api.v1.rest_router import api_router, build_rest_router
|
||||||
|
from app.api.v1.router import api_router as source_api_router
|
||||||
|
from app.auth.project_dependencies import ProjectContext, get_project_context
|
||||||
|
from scripts.check_openapi import current_contract_bytes, validate
|
||||||
|
|
||||||
|
|
||||||
|
def test_rest_router_preserves_every_distinct_source_operation() -> None:
|
||||||
|
skipped_names = {"fastapi_get_json", "fastapi_test_dict"}
|
||||||
|
source_names = {
|
||||||
|
route.name
|
||||||
|
for route in source_api_router.routes
|
||||||
|
if isinstance(route, APIRoute) and route.name not in skipped_names
|
||||||
|
}
|
||||||
|
rest_names = {
|
||||||
|
route.name for route in api_router.routes if isinstance(route, APIRoute)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert rest_names == source_names
|
||||||
|
|
||||||
|
|
||||||
|
def test_rest_router_has_unique_method_path_pairs() -> None:
|
||||||
|
pairs: list[tuple[str, str]] = []
|
||||||
|
for route in api_router.routes:
|
||||||
|
if not isinstance(route, APIRoute):
|
||||||
|
continue
|
||||||
|
pairs.extend((method, route.path) for method in route.methods or set())
|
||||||
|
assert len(pairs) == len(set(pairs))
|
||||||
|
|
||||||
|
|
||||||
|
def test_rest_router_rejects_duplicate_method_path_pairs() -> None:
|
||||||
|
first = APIRoute(
|
||||||
|
"/duplicate",
|
||||||
|
lambda: None,
|
||||||
|
methods={"POST"},
|
||||||
|
name="first_endpoint",
|
||||||
|
)
|
||||||
|
second = APIRoute(
|
||||||
|
"/duplicate",
|
||||||
|
lambda: None,
|
||||||
|
methods={"POST"},
|
||||||
|
name="second_endpoint",
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError, match="REST route collision"):
|
||||||
|
build_rest_router([first, second])
|
||||||
|
|
||||||
|
|
||||||
|
def test_handler_router_defines_only_the_public_rest_operations() -> None:
|
||||||
|
source_operations = {
|
||||||
|
(method, route.path)
|
||||||
|
for route in source_api_router.routes
|
||||||
|
if isinstance(route, APIRoute)
|
||||||
|
for method in route.methods or set()
|
||||||
|
}
|
||||||
|
public_operations = {
|
||||||
|
(method, route.path)
|
||||||
|
for route in api_router.routes
|
||||||
|
if isinstance(route, APIRoute)
|
||||||
|
for method in route.methods or set()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert source_operations == public_operations
|
||||||
|
assert ("POST", "/burst-analysis") not in source_operations
|
||||||
|
assert ("GET", "/getpipeproperties/") not in source_operations
|
||||||
|
|
||||||
|
|
||||||
|
def test_rest_openapi_satisfies_contract_invariants() -> None:
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI(redirect_slashes=False)
|
||||||
|
app.include_router(api_router, prefix="/api/v1")
|
||||||
|
errors = validate(app.openapi())
|
||||||
|
assert errors == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_snapshot_matches_current_application() -> None:
|
||||||
|
contract = Path(__file__).resolve().parents[2] / "contracts/server-v1.openapi.json"
|
||||||
|
|
||||||
|
assert contract.read_bytes() == current_contract_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
def test_rest_contract_uses_header_project_context() -> None:
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI(redirect_slashes=False)
|
||||||
|
app.include_router(api_router, prefix="/api/v1")
|
||||||
|
document = app.openapi()
|
||||||
|
|
||||||
|
for path_item in document["paths"].values():
|
||||||
|
for operation in path_item.values():
|
||||||
|
if not isinstance(operation, dict):
|
||||||
|
continue
|
||||||
|
query_names = {
|
||||||
|
parameter["name"]
|
||||||
|
for parameter in operation.get("parameters", [])
|
||||||
|
if parameter.get("in") == "query"
|
||||||
|
}
|
||||||
|
assert "network" not in query_names
|
||||||
|
assert "network_name" not in query_names
|
||||||
|
|
||||||
|
for name, schema in document["components"]["schemas"].items():
|
||||||
|
if name.endswith("Rest"):
|
||||||
|
assert "network" not in schema.get("properties", {})
|
||||||
|
assert "network_name" not in schema.get("properties", {})
|
||||||
|
|
||||||
|
assert "/api/v1/burst-analysis" not in document["paths"]
|
||||||
|
assert "/api/v1/getpipeproperties/" not in document["paths"]
|
||||||
|
|
||||||
|
pipes_collection = document["paths"]["/api/v1/pipes"]["get"]
|
||||||
|
query_names = {
|
||||||
|
parameter["name"]
|
||||||
|
for parameter in pipes_collection["parameters"]
|
||||||
|
if parameter["in"] == "query"
|
||||||
|
}
|
||||||
|
assert {"limit", "offset"} <= query_names
|
||||||
|
assert "204" in document["paths"]["/api/v1/pipes"]["delete"]["responses"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_side_effecting_analysis_routes_are_post() -> None:
|
||||||
|
methods_by_path = {
|
||||||
|
route.path: route.methods
|
||||||
|
for route in api_router.routes
|
||||||
|
if isinstance(route, APIRoute)
|
||||||
|
}
|
||||||
|
assert methods_by_path["/burst-analyses"] == {"POST"}
|
||||||
|
assert methods_by_path["/flushing-analyses"] == {"POST"}
|
||||||
|
assert methods_by_path["/contaminant-simulations"] == {"POST"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_valve_isolation_route_uses_the_isolation_handler() -> None:
|
||||||
|
route = next(
|
||||||
|
route
|
||||||
|
for route in api_router.routes
|
||||||
|
if isinstance(route, APIRoute)
|
||||||
|
and route.path == "/valve-isolation-analyses"
|
||||||
|
and route.methods == {"POST"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert route.name == "valve_isolation_endpoint"
|
||||||
|
|
||||||
|
|
||||||
|
def test_valve_isolation_runtime_accepts_frontend_query(monkeypatch) -> None:
|
||||||
|
captured: dict[str, object] = {}
|
||||||
|
|
||||||
|
def fake_analyze_valve_isolation(network, accident_element, disabled_valves):
|
||||||
|
captured.update(
|
||||||
|
network=network,
|
||||||
|
accident_element=accident_element,
|
||||||
|
disabled_valves=disabled_valves,
|
||||||
|
)
|
||||||
|
return {"isolatable": True, "must_close_valves": ["V-1"]}
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
simulation_endpoint,
|
||||||
|
"analyze_valve_isolation",
|
||||||
|
fake_analyze_valve_isolation,
|
||||||
|
)
|
||||||
|
app = FastAPI(redirect_slashes=False)
|
||||||
|
app.include_router(api_router, prefix="/api/v1")
|
||||||
|
app.dependency_overrides[get_project_context] = lambda: ProjectContext(
|
||||||
|
project_id=uuid4(),
|
||||||
|
project_code="fengyang",
|
||||||
|
user_id=uuid4(),
|
||||||
|
project_role="member",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = TestClient(app, raise_server_exceptions=False).post(
|
||||||
|
"/api/v1/valve-isolation-analyses",
|
||||||
|
params=[
|
||||||
|
("accident_element", "P-1"),
|
||||||
|
("accident_element", "P-2"),
|
||||||
|
("disabled_valves", "V-9"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json() == {"isolatable": True, "must_close_valves": ["V-1"]}
|
||||||
|
assert captured == {
|
||||||
|
"network": "fengyang",
|
||||||
|
"accident_element": ["P-1", "P-2"],
|
||||||
|
"disabled_valves": ["V-9"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_scada_cleaning_runs_are_post() -> None:
|
||||||
|
methods_by_path = {
|
||||||
|
route.path: route.methods
|
||||||
|
for route in api_router.routes
|
||||||
|
if isinstance(route, APIRoute)
|
||||||
|
}
|
||||||
|
assert methods_by_path["/timeseries/scada-cleaning-runs"] == {"POST"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_sensor_placement_excel_export_is_post() -> None:
|
||||||
|
methods_by_path = {
|
||||||
|
route.path: route.methods
|
||||||
|
for route in api_router.routes
|
||||||
|
if isinstance(route, APIRoute)
|
||||||
|
}
|
||||||
|
assert methods_by_path[
|
||||||
|
"/sensor-placement-schemes/{scheme_id}/exports/excel"
|
||||||
|
] == {"POST"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_rest_runtime_consumes_injected_project_context(monkeypatch) -> None:
|
||||||
|
captured: dict[str, object] = {}
|
||||||
|
|
||||||
|
def fake_get_all_schemes(network, scheme_type=None, query_date=None):
|
||||||
|
captured.update(
|
||||||
|
network=network,
|
||||||
|
scheme_type=scheme_type,
|
||||||
|
query_date=query_date,
|
||||||
|
)
|
||||||
|
return [{"scheme_name": "burst_case", "scheme_type": scheme_type}]
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
schemes_endpoint,
|
||||||
|
"get_all_schemes",
|
||||||
|
fake_get_all_schemes,
|
||||||
|
)
|
||||||
|
app = FastAPI(redirect_slashes=False)
|
||||||
|
app.include_router(api_router, prefix="/api/v1")
|
||||||
|
project_context = ProjectContext(
|
||||||
|
project_id=uuid4(),
|
||||||
|
project_code="fengyang",
|
||||||
|
user_id=uuid4(),
|
||||||
|
project_role="viewer",
|
||||||
|
)
|
||||||
|
app.dependency_overrides[get_project_context] = lambda: project_context
|
||||||
|
|
||||||
|
response = TestClient(app, raise_server_exceptions=False).get(
|
||||||
|
"/api/v1/schemes",
|
||||||
|
params={"scheme_type": "burst_analysis"},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert captured == {
|
||||||
|
"network": "fengyang",
|
||||||
|
"scheme_type": "burst_analysis",
|
||||||
|
"query_date": None,
|
||||||
|
}
|
||||||
|
assert response.json()["items"] == [
|
||||||
|
{"scheme_name": "burst_case", "scheme_type": "burst_analysis"}
|
||||||
|
]
|
||||||
@@ -78,7 +78,7 @@ def test_project_info_returns_404_when_missing(monkeypatch):
|
|||||||
app.dependency_overrides[module.get_metadata_repository] = lambda: repo
|
app.dependency_overrides[module.get_metadata_repository] = lambda: repo
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
|
|
||||||
response = client.get("/api/v1/project_info/", params={"network": "missing"})
|
response = client.get("/api/v1/projects/current", params={"network": "missing"})
|
||||||
|
|
||||||
assert response.status_code == 404
|
assert response.status_code == 404
|
||||||
assert response.json()["detail"] == "Project missing not found"
|
assert response.json()["detail"] == "Project missing not found"
|
||||||
@@ -100,7 +100,7 @@ def test_project_info_returns_project_workspace(monkeypatch):
|
|||||||
app.dependency_overrides[module.get_metadata_repository] = lambda: repo
|
app.dependency_overrides[module.get_metadata_repository] = lambda: repo
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
|
|
||||||
response = client.get("/api/v1/project_info/", params={"network": "demo"})
|
response = client.get("/api/v1/projects/current", params={"network": "demo"})
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
payload = response.json()
|
payload = response.json()
|
||||||
@@ -121,7 +121,7 @@ def test_open_project_returns_network_even_when_db_connection_fails(monkeypatch)
|
|||||||
monkeypatch.setattr(module, "get_pg_db", failing_get_pg_db)
|
monkeypatch.setattr(module, "get_pg_db", failing_get_pg_db)
|
||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.post("/api/v1/openproject/", params={"network": "demo"})
|
response = client.post("/api/v1/projects/current", params={"network": "demo"})
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json() == "demo"
|
assert response.json() == "demo"
|
||||||
@@ -133,11 +133,17 @@ def test_project_lock_lifecycle(monkeypatch):
|
|||||||
module.lockedPrjs.clear()
|
module.lockedPrjs.clear()
|
||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
first_lock = client.post("/api/v1/lockproject/", params={"network": "demo"})
|
first_lock = client.post("/api/v1/projects/current/lock", params={"network": "demo"})
|
||||||
second_lock = client.post("/api/v1/lockproject/", params={"network": "demo"})
|
second_lock = client.post("/api/v1/projects/current/lock", params={"network": "demo"})
|
||||||
locked_by_me = client.get("/api/v1/isprojectlockedbyme/", params={"network": "demo"})
|
locked_by_me = client.get(
|
||||||
unlock = client.post("/api/v1/unlockproject/", params={"network": "demo"})
|
"/api/v1/projects/current/lock/ownership",
|
||||||
locked = client.get("/api/v1/isprojectlocked/", params={"network": "demo"})
|
params={"network": "demo"},
|
||||||
|
)
|
||||||
|
unlock = client.delete(
|
||||||
|
"/api/v1/projects/current/lock",
|
||||||
|
params={"network": "demo"},
|
||||||
|
)
|
||||||
|
locked = client.get("/api/v1/projects/current/lock", params={"network": "demo"})
|
||||||
|
|
||||||
assert first_lock.json() == 0
|
assert first_lock.json() == 0
|
||||||
assert second_lock.json() == 1
|
assert second_lock.json() == 1
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ def test_calculate_service_area_contract_uses_only_network(monkeypatch):
|
|||||||
)
|
)
|
||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.get(
|
response = client.post(
|
||||||
"/api/v1/calculateservicearea/",
|
"/api/v1/service-area-calculations",
|
||||||
params={"network": "demo", "time_index": 5},
|
params={"network": "demo", "time_index": 5},
|
||||||
)
|
)
|
||||||
schema = client.get("/openapi.json").json()
|
schema = client.get("/openapi.json").json()
|
||||||
@@ -103,7 +103,7 @@ def test_calculate_service_area_contract_uses_only_network(monkeypatch):
|
|||||||
assert calls == ["demo"]
|
assert calls == ["demo"]
|
||||||
parameter_names = [
|
parameter_names = [
|
||||||
item["name"]
|
item["name"]
|
||||||
for item in schema["paths"]["/api/v1/calculateservicearea/"]["get"]["parameters"]
|
for item in schema["paths"]["/api/v1/service-area-calculations"]["post"]["parameters"]
|
||||||
]
|
]
|
||||||
assert parameter_names == ["network"]
|
assert parameter_names == ["network"]
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ def test_add_district_metering_area_converts_boundary_to_tuples(monkeypatch):
|
|||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/adddistrictmeteringarea/",
|
"/api/v1/district-metering-areas",
|
||||||
params={"network": "demo"},
|
params={"network": "demo"},
|
||||||
json={"id": "dma-1", "boundary": [[1, 2], [3, 4], [1, 2]]},
|
json={"id": "dma-1", "boundary": [[1, 2], [3, 4], [1, 2]]},
|
||||||
)
|
)
|
||||||
@@ -145,7 +145,7 @@ def test_generate_virtual_district_reads_centers_from_body(monkeypatch):
|
|||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/generatevirtualdistrict/",
|
"/api/v1/virtual-district-generation-runs",
|
||||||
params={"network": "demo", "inflate_delta": 0.75},
|
params={"network": "demo", "inflate_delta": 0.75},
|
||||||
json={"centers": ["J1", "J2"]},
|
json={"centers": ["J1", "J2"]},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ def test_optimize_returns_created_scheme(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setattr(module, "pressure_sensor_placement_kmeans", optimize)
|
monkeypatch.setattr(module, "pressure_sensor_placement_kmeans", optimize)
|
||||||
response = _client(module).post(
|
response = _client(module).post(
|
||||||
"/api/v1/sensor-placement-schemes/optimize",
|
"/api/v1/sensor-placement-optimization-runs",
|
||||||
json={
|
json={
|
||||||
"network": "tjwater",
|
"network": "tjwater",
|
||||||
"scheme_name": "北区测压点",
|
"scheme_name": "北区测压点",
|
||||||
@@ -173,7 +173,7 @@ def test_optimize_returns_created_scheme(monkeypatch):
|
|||||||
def test_optimize_rejects_unsupported_sensor_type(monkeypatch):
|
def test_optimize_rejects_unsupported_sensor_type(monkeypatch):
|
||||||
module = _load_module(monkeypatch)
|
module = _load_module(monkeypatch)
|
||||||
response = _client(module).post(
|
response = _client(module).post(
|
||||||
"/api/v1/sensor-placement-schemes/optimize",
|
"/api/v1/sensor-placement-optimization-runs",
|
||||||
json={
|
json={
|
||||||
"network": "tjwater",
|
"network": "tjwater",
|
||||||
"scheme_name": "北区测流点",
|
"scheme_name": "北区测流点",
|
||||||
@@ -190,7 +190,7 @@ def test_optimize_rejects_unsupported_sensor_type(monkeypatch):
|
|||||||
def test_optimize_rejects_network_outside_project_context(monkeypatch):
|
def test_optimize_rejects_network_outside_project_context(monkeypatch):
|
||||||
module = _load_module(monkeypatch)
|
module = _load_module(monkeypatch)
|
||||||
response = _client(module).post(
|
response = _client(module).post(
|
||||||
"/api/v1/sensor-placement-schemes/optimize",
|
"/api/v1/sensor-placement-optimization-runs",
|
||||||
json={
|
json={
|
||||||
"network": "other_project",
|
"network": "other_project",
|
||||||
"scheme_name": "越权方案",
|
"scheme_name": "越权方案",
|
||||||
@@ -207,7 +207,7 @@ def test_optimize_rejects_network_outside_project_context(monkeypatch):
|
|||||||
def test_optimize_rejects_network_path_traversal(monkeypatch):
|
def test_optimize_rejects_network_path_traversal(monkeypatch):
|
||||||
module = _load_module(monkeypatch)
|
module = _load_module(monkeypatch)
|
||||||
response = _client(module).post(
|
response = _client(module).post(
|
||||||
"/api/v1/sensor-placement-schemes/optimize",
|
"/api/v1/sensor-placement-optimization-runs",
|
||||||
json={
|
json={
|
||||||
"network": "../other_project",
|
"network": "../other_project",
|
||||||
"scheme_name": "非法路径",
|
"scheme_name": "非法路径",
|
||||||
@@ -224,7 +224,7 @@ def test_optimize_rejects_network_path_traversal(monkeypatch):
|
|||||||
def test_optimize_rejects_unbounded_sensor_count(monkeypatch):
|
def test_optimize_rejects_unbounded_sensor_count(monkeypatch):
|
||||||
module = _load_module(monkeypatch)
|
module = _load_module(monkeypatch)
|
||||||
response = _client(module).post(
|
response = _client(module).post(
|
||||||
"/api/v1/sensor-placement-schemes/optimize",
|
"/api/v1/sensor-placement-optimization-runs",
|
||||||
json={
|
json={
|
||||||
"network": "tjwater",
|
"network": "tjwater",
|
||||||
"scheme_name": "超大方案",
|
"scheme_name": "超大方案",
|
||||||
@@ -241,7 +241,7 @@ def test_optimize_rejects_unbounded_sensor_count(monkeypatch):
|
|||||||
def test_optimize_rejects_viewer_project_role(monkeypatch):
|
def test_optimize_rejects_viewer_project_role(monkeypatch):
|
||||||
module = _load_module(monkeypatch)
|
module = _load_module(monkeypatch)
|
||||||
response = _client(module, project_role="viewer").post(
|
response = _client(module, project_role="viewer").post(
|
||||||
"/api/v1/sensor-placement-schemes/optimize",
|
"/api/v1/sensor-placement-optimization-runs",
|
||||||
json={
|
json={
|
||||||
"network": "tjwater",
|
"network": "tjwater",
|
||||||
"scheme_name": "只读成员方案",
|
"scheme_name": "只读成员方案",
|
||||||
@@ -262,7 +262,7 @@ def test_optimize_rejects_viewer_project_role(monkeypatch):
|
|||||||
def test_legacy_project_roles_cannot_optimize(monkeypatch, project_role):
|
def test_legacy_project_roles_cannot_optimize(monkeypatch, project_role):
|
||||||
module = _load_module(monkeypatch)
|
module = _load_module(monkeypatch)
|
||||||
response = _client(module, project_role=project_role).post(
|
response = _client(module, project_role=project_role).post(
|
||||||
"/api/v1/sensor-placement-schemes/optimize",
|
"/api/v1/sensor-placement-optimization-runs",
|
||||||
json={
|
json={
|
||||||
"network": "tjwater",
|
"network": "tjwater",
|
||||||
"scheme_name": f"{project_role}方案",
|
"scheme_name": f"{project_role}方案",
|
||||||
@@ -284,7 +284,7 @@ def test_optimize_maps_running_project_job_to_409(monkeypatch):
|
|||||||
|
|
||||||
monkeypatch.setattr(module, "pressure_sensor_placement_kmeans", conflict)
|
monkeypatch.setattr(module, "pressure_sensor_placement_kmeans", conflict)
|
||||||
response = _client(module).post(
|
response = _client(module).post(
|
||||||
"/api/v1/sensor-placement-schemes/optimize",
|
"/api/v1/sensor-placement-optimization-runs",
|
||||||
json={
|
json={
|
||||||
"network": "tjwater",
|
"network": "tjwater",
|
||||||
"scheme_name": "并发方案",
|
"scheme_name": "并发方案",
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ def test_run_project_endpoint_returns_plain_text(monkeypatch):
|
|||||||
monkeypatch.setattr(module, "run_project", lambda network: f"report::{network}")
|
monkeypatch.setattr(module, "run_project", lambda network: f"report::{network}")
|
||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.get("/api/v1/runproject/", params={"network": "demo"})
|
response = client.post("/api/v1/project-runs", params={"network": "demo"})
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.text == "report::demo"
|
assert response.text == "report::demo"
|
||||||
@@ -143,7 +143,7 @@ def test_scheduling_analysis_maps_request_body(monkeypatch):
|
|||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/scheduling_analysis/",
|
"/api/v1/scheduling-analyses",
|
||||||
json={
|
json={
|
||||||
"network": "demo",
|
"network": "demo",
|
||||||
"start_time": "2025-01-01T08:00:00+08:00",
|
"start_time": "2025-01-01T08:00:00+08:00",
|
||||||
@@ -177,7 +177,7 @@ def test_project_management_maps_named_arguments(monkeypatch):
|
|||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/project_management/",
|
"/api/v1/project-managements",
|
||||||
json={
|
json={
|
||||||
"network": "demo",
|
"network": "demo",
|
||||||
"start_time": "2025-01-01T08:00:00+08:00",
|
"start_time": "2025-01-01T08:00:00+08:00",
|
||||||
@@ -260,7 +260,7 @@ def test_runsimulationmanuallybydate_endpoint_accepts_timezone_aware_start_time(
|
|||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/runsimulationmanuallybydate/",
|
"/api/v1/simulation-runs",
|
||||||
json={
|
json={
|
||||||
"name": "demo",
|
"name": "demo",
|
||||||
"start_time": "2025-01-02T03:04:05+08:00",
|
"start_time": "2025-01-02T03:04:05+08:00",
|
||||||
@@ -280,7 +280,7 @@ def test_runsimulationmanuallybydate_endpoint_rejects_naive_start_time(monkeypat
|
|||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/runsimulationmanuallybydate/",
|
"/api/v1/simulation-runs",
|
||||||
json={
|
json={
|
||||||
"name": "demo",
|
"name": "demo",
|
||||||
"start_time": "2025-01-02T03:04:05",
|
"start_time": "2025-01-02T03:04:05",
|
||||||
@@ -302,8 +302,8 @@ def test_valve_close_endpoint_passes_scheme_name(monkeypatch):
|
|||||||
monkeypatch.setattr(module, "valve_close_analysis", fake_valve_close_analysis)
|
monkeypatch.setattr(module, "valve_close_analysis", fake_valve_close_analysis)
|
||||||
client = TestClient(build_test_app(module.router, "/api/v1"))
|
client = TestClient(build_test_app(module.router, "/api/v1"))
|
||||||
|
|
||||||
response = client.get(
|
response = client.post(
|
||||||
"/api/v1/valve_close_analysis/",
|
"/api/v1/valve-closure-analyses",
|
||||||
params={
|
params={
|
||||||
"network": "demo",
|
"network": "demo",
|
||||||
"start_time": "2025-01-02T03:04:05+08:00",
|
"start_time": "2025-01-02T03:04:05+08:00",
|
||||||
@@ -334,8 +334,8 @@ def test_burst_endpoint_passes_current_username(monkeypatch):
|
|||||||
monkeypatch.setattr(module, "burst_analysis", fake_burst_analysis)
|
monkeypatch.setattr(module, "burst_analysis", fake_burst_analysis)
|
||||||
client = _build_authenticated_client(module)
|
client = _build_authenticated_client(module)
|
||||||
|
|
||||||
response = client.get(
|
response = client.post(
|
||||||
"/api/v1/burst_analysis/",
|
"/api/v1/burst-analyses",
|
||||||
params={
|
params={
|
||||||
"network": "demo",
|
"network": "demo",
|
||||||
"modify_pattern_start_time": "2025-01-02T03:04:05+08:00",
|
"modify_pattern_start_time": "2025-01-02T03:04:05+08:00",
|
||||||
@@ -362,8 +362,8 @@ def test_flushing_endpoint_passes_required_scheme_name(monkeypatch):
|
|||||||
monkeypatch.setattr(module, "flushing_analysis", fake_flushing_analysis)
|
monkeypatch.setattr(module, "flushing_analysis", fake_flushing_analysis)
|
||||||
client = _build_authenticated_client(module)
|
client = _build_authenticated_client(module)
|
||||||
|
|
||||||
response = client.get(
|
response = client.post(
|
||||||
"/api/v1/flushing_analysis/",
|
"/api/v1/flushing-analyses",
|
||||||
params={
|
params={
|
||||||
"network": "demo",
|
"network": "demo",
|
||||||
"start_time": "2025-01-02T03:04:05+08:00",
|
"start_time": "2025-01-02T03:04:05+08:00",
|
||||||
@@ -401,8 +401,8 @@ def test_contaminant_endpoint_passes_current_username(monkeypatch):
|
|||||||
monkeypatch.setattr(module, "contaminant_simulation", fake_contaminant_simulation)
|
monkeypatch.setattr(module, "contaminant_simulation", fake_contaminant_simulation)
|
||||||
client = _build_authenticated_client(module)
|
client = _build_authenticated_client(module)
|
||||||
|
|
||||||
response = client.get(
|
response = client.post(
|
||||||
"/api/v1/contaminant_simulation/",
|
"/api/v1/contaminant-simulations",
|
||||||
params={
|
params={
|
||||||
"network": "demo",
|
"network": "demo",
|
||||||
"start_time": "2025-01-02T03:04:05+08:00",
|
"start_time": "2025-01-02T03:04:05+08:00",
|
||||||
@@ -422,8 +422,8 @@ def test_contaminant_endpoint_requires_scheme_name(monkeypatch):
|
|||||||
module = _load_simulation_module(monkeypatch)
|
module = _load_simulation_module(monkeypatch)
|
||||||
client = _build_authenticated_client(module)
|
client = _build_authenticated_client(module)
|
||||||
|
|
||||||
response = client.get(
|
response = client.post(
|
||||||
"/api/v1/contaminant_simulation/",
|
"/api/v1/contaminant-simulations",
|
||||||
params={
|
params={
|
||||||
"network": "demo",
|
"network": "demo",
|
||||||
"start_time": "2025-01-02T03:04:05+08:00",
|
"start_time": "2025-01-02T03:04:05+08:00",
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
THEME_SCRIPT = (
|
||||||
|
Path(__file__).resolve().parents[2]
|
||||||
|
/ "infra"
|
||||||
|
/ "docker"
|
||||||
|
/ "keycloak"
|
||||||
|
/ "configure-theme.sh"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_theme_script_clears_client_login_theme_override() -> None:
|
||||||
|
script = THEME_SCRIPT.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert "TJWATER_KEYCLOAK_CLIENT_ID" in script
|
||||||
|
assert "sed -n '1p'" in script
|
||||||
|
assert "--set attributes.login_theme=" in script
|
||||||
|
assert "client ${client_id} 已改为继承 realm 登录主题。" in script
|
||||||
Reference in New Issue
Block a user