refactor(db)!: adopt project-routed pooled databases
Reorganize WNDB by responsibility and remove legacy scheme endpoints.\n\nRoute analysis and time-series access through project pools, preserve transactional realtime replacement, and refresh GIS materialized views after writes.\n\nAdd database architecture documentation, live pooling coverage, API contract updates, and executable container verification.\n\nBREAKING CHANGE: legacy scheme APIs and flat app.native.wndb module imports are removed.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
from datetime import datetime
|
||||
from io import BytesIO
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Alignment, Font, PatternFill
|
||||
@@ -8,7 +9,7 @@ from openpyxl.worksheet.worksheet import Worksheet
|
||||
from openpyxl.utils import get_column_letter
|
||||
from pyproj import Transformer
|
||||
|
||||
from app.native import wndb
|
||||
from app.infra.db.postgresql import sensor_placement as sensor_placement_repository
|
||||
|
||||
|
||||
class SensorPlacementNotFoundError(LookupError):
|
||||
@@ -61,7 +62,9 @@ def _sensor_points(
|
||||
network: str,
|
||||
sensor_location: list[str],
|
||||
) -> list[dict[str, Any]]:
|
||||
nodes = wndb.get_sensor_placement_nodes(network, sensor_location)
|
||||
nodes = sensor_placement_repository.get_sensor_placement_nodes(
|
||||
network, sensor_location
|
||||
)
|
||||
by_id = {str(node["node_id"]): node for node in nodes}
|
||||
missing = [node_id for node_id in sensor_location if node_id not in by_id]
|
||||
if missing:
|
||||
@@ -113,49 +116,59 @@ def validate_sensor_placement_nodes(
|
||||
_sensor_points(network, _normalize_locations(sensor_location))
|
||||
|
||||
|
||||
def get_sensor_placement_scheme(network: str, scheme_id: int) -> dict[str, Any]:
|
||||
scheme = wndb.get_sensor_placement(network, scheme_id)
|
||||
if scheme is None:
|
||||
raise SensorPlacementNotFoundError("监测点方案不存在")
|
||||
def get_sensor_placement_run(network: str, run_id: UUID) -> dict[str, Any]:
|
||||
run = sensor_placement_repository.get_sensor_placement(network, run_id)
|
||||
if run is None:
|
||||
raise SensorPlacementNotFoundError("监测点优化运行不存在")
|
||||
|
||||
locations = [str(item) for item in (scheme.get("sensor_location") or [])]
|
||||
locations = [str(item) for item in (run.get("sensor_locations") or [])]
|
||||
return {
|
||||
**scheme,
|
||||
"sensor_number": len(locations),
|
||||
"sensor_location": locations,
|
||||
**run,
|
||||
"sensor_count": len(locations),
|
||||
"sensor_locations": locations,
|
||||
"sensor_points": _sensor_points(network, locations),
|
||||
}
|
||||
|
||||
|
||||
def update_sensor_placement_scheme(
|
||||
def list_sensor_placement_runs(network: str) -> list[dict[str, Any]]:
|
||||
return [
|
||||
{
|
||||
**run,
|
||||
"sensor_points": _sensor_points(network, run["sensor_locations"]),
|
||||
}
|
||||
for run in sensor_placement_repository.get_all_sensor_placements(network)
|
||||
]
|
||||
|
||||
|
||||
def update_sensor_placement_run(
|
||||
network: str,
|
||||
scheme_id: int,
|
||||
run_id: UUID,
|
||||
*,
|
||||
expected_sensor_location: list[str],
|
||||
sensor_location: list[str],
|
||||
expected_sensor_locations: list[str],
|
||||
sensor_locations: list[str],
|
||||
) -> dict[str, Any]:
|
||||
expected = _normalize_locations(expected_sensor_location)
|
||||
next_locations = _normalize_locations(sensor_location)
|
||||
expected = _normalize_locations(expected_sensor_locations)
|
||||
next_locations = _normalize_locations(sensor_locations)
|
||||
_sensor_points(network, next_locations)
|
||||
|
||||
updated = wndb.update_sensor_placement(
|
||||
updated = sensor_placement_repository.update_sensor_placement(
|
||||
network,
|
||||
scheme_id,
|
||||
expected_sensor_location=expected,
|
||||
sensor_location=next_locations,
|
||||
run_id,
|
||||
expected_sensor_locations=expected,
|
||||
sensor_locations=next_locations,
|
||||
)
|
||||
if updated is None:
|
||||
if wndb.get_sensor_placement(network, scheme_id) is None:
|
||||
raise SensorPlacementNotFoundError("监测点方案不存在")
|
||||
raise SensorPlacementConflictError("方案已被其他用户修改,请重新加载")
|
||||
return get_sensor_placement_scheme(network, scheme_id)
|
||||
if sensor_placement_repository.get_sensor_placement(network, run_id) is None:
|
||||
raise SensorPlacementNotFoundError("监测点优化运行不存在")
|
||||
raise SensorPlacementConflictError("运行结果已被其他用户修改,请重新加载")
|
||||
return get_sensor_placement_run(network, run_id)
|
||||
|
||||
|
||||
def can_edit_sensor_placement(user: Any, scheme: dict[str, Any]) -> bool:
|
||||
def can_edit_sensor_placement(user: Any, run: dict[str, Any]) -> bool:
|
||||
return bool(
|
||||
getattr(user, "is_superuser", False)
|
||||
or getattr(user, "role", None) == "admin"
|
||||
or getattr(user, "username", None) == scheme.get("username")
|
||||
or getattr(user, "username", None) == run.get("created_by")
|
||||
)
|
||||
|
||||
|
||||
@@ -174,16 +187,16 @@ def _populate_info_sheet(
|
||||
location_count: int,
|
||||
is_draft: bool,
|
||||
) -> None:
|
||||
created_at = scheme["create_time"]
|
||||
created_at = scheme["created_at"]
|
||||
if isinstance(created_at, datetime):
|
||||
created_at = created_at.isoformat(timespec="minutes")
|
||||
|
||||
rows = [
|
||||
("项目", network),
|
||||
("方案名称", scheme["scheme_name"]),
|
||||
("运行名称", scheme["name"]),
|
||||
("监测点数量", location_count),
|
||||
("最小管径", scheme["min_diameter"]),
|
||||
("创建人", scheme["username"]),
|
||||
("创建人", scheme["created_by"]),
|
||||
("创建时间", created_at),
|
||||
("导出时间", datetime.now().astimezone().isoformat(timespec="minutes")),
|
||||
("文档状态", "未保存草稿" if is_draft else "当前方案"),
|
||||
@@ -245,7 +258,7 @@ def build_sensor_placement_workbook(
|
||||
) -> BytesIO:
|
||||
locations = _normalize_locations(sensor_location)
|
||||
points = _sensor_points(network, locations)
|
||||
is_draft = locations != list(scheme["sensor_location"])
|
||||
is_draft = locations != list(scheme["sensor_locations"])
|
||||
|
||||
workbook = Workbook()
|
||||
info_sheet = workbook.active
|
||||
|
||||
Reference in New Issue
Block a user