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:
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
import pandas as pd
|
||||
|
||||
@@ -11,7 +12,7 @@ from app.infra.db.timescaledb.internal_queries import InternalQueries
|
||||
from app.services.scheme_management import (
|
||||
query_burst_location_scheme_detail,
|
||||
query_burst_location_schemes,
|
||||
query_scheme_list,
|
||||
get_analysis_run,
|
||||
scheme_name_exists,
|
||||
store_scheme_info,
|
||||
)
|
||||
@@ -21,7 +22,6 @@ from app.services.time_api import extract_date, parse_utc_time, utc_now
|
||||
SeriesInput = pd.Series | dict[str, Any] | list[dict[str, Any]]
|
||||
FLOW_SCADA_TYPES = {"pipe_flow", "flow", "demand"}
|
||||
SIMULATION_DATA_SOURCES = {"monitoring", "simulation"}
|
||||
DEFAULT_SIMULATION_SCHEME_TYPE = "burst_analysis"
|
||||
|
||||
|
||||
def _normalize_series(data: SeriesInput, field_name: str) -> pd.Series:
|
||||
@@ -65,16 +65,12 @@ def run_burst_location_by_network(
|
||||
scada_normal_end: datetime | str | None = None,
|
||||
use_scada_flow: bool = False,
|
||||
scheme_name: str | None = None,
|
||||
simulation_scheme_name: str | None = None,
|
||||
simulation_scheme_type: str | None = None,
|
||||
simulation_run_id: UUID | None = None,
|
||||
) -> dict[str, Any]:
|
||||
if not network:
|
||||
raise ValueError("network is required.")
|
||||
normalized_data_source = _normalize_data_source(
|
||||
data_source, simulation_scheme_name=simulation_scheme_name
|
||||
)
|
||||
resolved_simulation_scheme_type = (
|
||||
simulation_scheme_type or DEFAULT_SIMULATION_SCHEME_TYPE
|
||||
data_source, simulation_run_id=simulation_run_id
|
||||
)
|
||||
|
||||
selected_pressure_ids = (
|
||||
@@ -123,8 +119,8 @@ def run_burst_location_by_network(
|
||||
else None
|
||||
)
|
||||
if normalized_data_source == "simulation":
|
||||
if not simulation_scheme_name:
|
||||
raise ValueError("模拟方案模式必须提供 simulation_scheme_name。")
|
||||
if not simulation_run_id:
|
||||
raise ValueError("模拟数据模式必须提供 simulation_run_id。")
|
||||
normal_start_dt = burst_start_dt
|
||||
normal_end_dt = burst_end_dt
|
||||
(
|
||||
@@ -137,9 +133,8 @@ def run_burst_location_by_network(
|
||||
end_dt=burst_end_dt,
|
||||
data_type="pressure",
|
||||
series_name="burst_pressure",
|
||||
simulation_source="scheme",
|
||||
simulation_scheme_name=simulation_scheme_name,
|
||||
simulation_scheme_type=resolved_simulation_scheme_type,
|
||||
simulation_source="analysis",
|
||||
simulation_run_id=simulation_run_id,
|
||||
)
|
||||
(
|
||||
normal_pressure_series,
|
||||
@@ -152,10 +147,9 @@ def run_burst_location_by_network(
|
||||
data_type="pressure",
|
||||
series_name="normal_pressure",
|
||||
simulation_source="realtime",
|
||||
simulation_scheme_name=None,
|
||||
simulation_scheme_type=resolved_simulation_scheme_type,
|
||||
simulation_run_id=None,
|
||||
)
|
||||
observed_source = "simulation_scheme_burst_realtime_normal_timerange"
|
||||
observed_source = "analysis_run_burst_realtime_normal_timerange"
|
||||
else:
|
||||
if normal_pressure_from_payload is None and (
|
||||
normal_start_dt is None or normal_end_dt is None
|
||||
@@ -230,8 +224,8 @@ def run_burst_location_by_network(
|
||||
else None
|
||||
)
|
||||
if normalized_data_source == "simulation":
|
||||
if not simulation_scheme_name:
|
||||
raise ValueError("模拟方案模式必须提供 simulation_scheme_name。")
|
||||
if not simulation_run_id:
|
||||
raise ValueError("模拟数据模式必须提供 simulation_run_id。")
|
||||
burst_flow_series, burst_flow_samples = (
|
||||
_build_observed_series_from_simulation(
|
||||
network=network,
|
||||
@@ -240,9 +234,8 @@ def run_burst_location_by_network(
|
||||
end_dt=burst_end_dt,
|
||||
data_type="flow",
|
||||
series_name="burst_flow",
|
||||
simulation_source="scheme",
|
||||
simulation_scheme_name=simulation_scheme_name,
|
||||
simulation_scheme_type=resolved_simulation_scheme_type,
|
||||
simulation_source="analysis",
|
||||
simulation_run_id=simulation_run_id,
|
||||
)
|
||||
)
|
||||
normal_flow_series, normal_flow_samples = (
|
||||
@@ -254,8 +247,7 @@ def run_burst_location_by_network(
|
||||
data_type="flow",
|
||||
series_name="normal_flow",
|
||||
simulation_source="realtime",
|
||||
simulation_scheme_name=None,
|
||||
simulation_scheme_type=resolved_simulation_scheme_type,
|
||||
simulation_run_id=None,
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -354,14 +346,12 @@ def run_burst_location_by_network(
|
||||
}
|
||||
)
|
||||
if normalized_data_source == "simulation":
|
||||
simulation_burst_ids = _get_simulation_scheme_burst_ids(
|
||||
simulation_burst_ids = _get_simulation_run_burst_ids(
|
||||
network=network,
|
||||
scheme_name=simulation_scheme_name,
|
||||
scheme_type=resolved_simulation_scheme_type,
|
||||
run_id=simulation_run_id,
|
||||
)
|
||||
payload["simulation_scheme"] = {
|
||||
"name": simulation_scheme_name,
|
||||
"type": resolved_simulation_scheme_type,
|
||||
payload["simulation_run"] = {
|
||||
"run_id": str(simulation_run_id),
|
||||
"burst_ids": simulation_burst_ids,
|
||||
}
|
||||
if scheme_name:
|
||||
@@ -471,20 +461,15 @@ def _validate_time_window(
|
||||
return start_dt, end_dt
|
||||
|
||||
|
||||
def _get_simulation_scheme_burst_ids(
|
||||
*, network: str, scheme_name: str | None, scheme_type: str
|
||||
def _get_simulation_run_burst_ids(
|
||||
*, network: str, run_id: UUID | None
|
||||
) -> list[str]:
|
||||
if not scheme_name:
|
||||
if run_id is None:
|
||||
return []
|
||||
rows = query_scheme_list(network, scheme_type=scheme_type) or []
|
||||
for row in rows:
|
||||
if len(row) < 7:
|
||||
continue
|
||||
if row[1] != scheme_name or row[2] != scheme_type:
|
||||
continue
|
||||
detail = row[6] if isinstance(row[6], dict) else {}
|
||||
return _normalize_burst_ids(detail.get("burst_ID"))
|
||||
return []
|
||||
run = get_analysis_run(network, run_id)
|
||||
if not run:
|
||||
raise ValueError(f"未找到模拟运行: {run_id}")
|
||||
return _normalize_burst_ids(run["parameters"].get("burst_ID"))
|
||||
|
||||
|
||||
def _normalize_burst_ids(value: Any) -> list[str]:
|
||||
@@ -566,8 +551,7 @@ def _build_observed_series_from_simulation(
|
||||
data_type: str,
|
||||
series_name: str,
|
||||
simulation_source: str,
|
||||
simulation_scheme_name: str | None,
|
||||
simulation_scheme_type: str,
|
||||
simulation_run_id: UUID | None,
|
||||
) -> tuple[pd.Series, int]:
|
||||
sensor_ids = _dedupe_ids(sensor_ids)
|
||||
sensor_metadata = _build_sensor_metadata(network=network, data_type=data_type)
|
||||
@@ -586,8 +570,7 @@ def _build_observed_series_from_simulation(
|
||||
end_dt=end_dt,
|
||||
data_type=data_type,
|
||||
simulation_source=simulation_source,
|
||||
simulation_scheme_name=simulation_scheme_name,
|
||||
simulation_scheme_type=simulation_scheme_type,
|
||||
simulation_run_id=simulation_run_id,
|
||||
)
|
||||
simulation_data = _normalize_timeseries_by_id(simulation_data)
|
||||
values: dict[str, float] = {}
|
||||
@@ -625,10 +608,9 @@ def _query_simulation_data_by_sensor_ids(
|
||||
end_dt: datetime,
|
||||
data_type: str,
|
||||
simulation_source: str,
|
||||
simulation_scheme_name: str | None,
|
||||
simulation_scheme_type: str,
|
||||
simulation_run_id: UUID | None,
|
||||
) -> dict[str, list[dict[str, Any]]]:
|
||||
if simulation_source not in {"scheme", "realtime"}:
|
||||
if simulation_source not in {"analysis", "realtime"}:
|
||||
raise ValueError(f"Unsupported simulation_source: {simulation_source}")
|
||||
|
||||
sensor_ids = _dedupe_ids(sensor_ids)
|
||||
@@ -645,8 +627,7 @@ def _query_simulation_data_by_sensor_ids(
|
||||
start_dt=start_dt,
|
||||
end_dt=end_dt,
|
||||
simulation_source=simulation_source,
|
||||
simulation_scheme_name=simulation_scheme_name,
|
||||
simulation_scheme_type=simulation_scheme_type,
|
||||
simulation_run_id=simulation_run_id,
|
||||
)
|
||||
)
|
||||
return result
|
||||
@@ -679,8 +660,7 @@ def _query_simulation_data_by_sensor_ids(
|
||||
start_dt=start_dt,
|
||||
end_dt=end_dt,
|
||||
simulation_source=simulation_source,
|
||||
simulation_scheme_name=simulation_scheme_name,
|
||||
simulation_scheme_type=simulation_scheme_type,
|
||||
simulation_run_id=simulation_run_id,
|
||||
)
|
||||
)
|
||||
if demand_ids:
|
||||
@@ -693,8 +673,7 @@ def _query_simulation_data_by_sensor_ids(
|
||||
start_dt=start_dt,
|
||||
end_dt=end_dt,
|
||||
simulation_source=simulation_source,
|
||||
simulation_scheme_name=simulation_scheme_name,
|
||||
simulation_scheme_type=simulation_scheme_type,
|
||||
simulation_run_id=simulation_run_id,
|
||||
)
|
||||
)
|
||||
return result
|
||||
@@ -709,19 +688,17 @@ def _query_simulation_values(
|
||||
start_dt: datetime,
|
||||
end_dt: datetime,
|
||||
simulation_source: str,
|
||||
simulation_scheme_name: str | None,
|
||||
simulation_scheme_type: str,
|
||||
simulation_run_id: UUID | None,
|
||||
) -> dict[str, list[dict[str, Any]]]:
|
||||
element_ids = _dedupe_ids(element_ids)
|
||||
if not element_ids:
|
||||
return {}
|
||||
if simulation_source == "scheme":
|
||||
if not simulation_scheme_name:
|
||||
raise ValueError("读取方案模拟数据时必须提供 simulation_scheme_name。")
|
||||
return InternalQueries.query_scheme_simulation_by_ids_timerange(
|
||||
if simulation_source == "analysis":
|
||||
if not simulation_run_id:
|
||||
raise ValueError("读取分析模拟数据时必须提供 simulation_run_id。")
|
||||
return InternalQueries.query_analysis_simulation_by_ids_timerange(
|
||||
db_name=network,
|
||||
scheme_type=simulation_scheme_type,
|
||||
scheme_name=simulation_scheme_name,
|
||||
run_id=simulation_run_id,
|
||||
element_ids=element_ids,
|
||||
start_time=start_dt.isoformat(),
|
||||
end_time=end_dt.isoformat(),
|
||||
@@ -743,7 +720,7 @@ def _query_simulation_values(
|
||||
def _build_sensor_metadata(network: str, data_type: str) -> dict[str, dict[str, str]]:
|
||||
metadata: dict[str, dict[str, str]] = {}
|
||||
for item in get_all_scada_info(network):
|
||||
scada_type = str(item.get("type", "")).lower()
|
||||
scada_type = str(item.get("device_type", "")).lower()
|
||||
if data_type == "pressure":
|
||||
if scada_type != "pressure":
|
||||
continue
|
||||
@@ -752,7 +729,7 @@ def _build_sensor_metadata(network: str, data_type: str) -> dict[str, dict[str,
|
||||
continue
|
||||
else:
|
||||
raise ValueError(f"Unsupported data_type: {data_type}")
|
||||
element_id = _normalize_identifier(item.get("associated_element_id"))
|
||||
element_id = _normalize_identifier(item.get("node_id") or item.get("link_id"))
|
||||
query_id = _normalize_identifier(item.get("api_query_id"))
|
||||
if element_id and query_id:
|
||||
metadata[element_id] = {"query_id": query_id, "scada_type": scada_type}
|
||||
@@ -765,11 +742,11 @@ def _build_scada_mapping(network: str, data_type: str) -> dict[str, str]:
|
||||
|
||||
|
||||
def _normalize_data_source(
|
||||
data_source: str | None, simulation_scheme_name: str | None = None
|
||||
data_source: str | None, simulation_run_id: UUID | None = None
|
||||
) -> str:
|
||||
normalized = str(data_source or "").strip().lower()
|
||||
if not normalized:
|
||||
return "simulation" if simulation_scheme_name else "monitoring"
|
||||
return "simulation" if simulation_run_id else "monitoring"
|
||||
if normalized not in SIMULATION_DATA_SOURCES:
|
||||
allowed_sources = ", ".join(sorted(SIMULATION_DATA_SOURCES))
|
||||
raise ValueError(
|
||||
@@ -783,7 +760,7 @@ def _get_sensor_nodes(network: str, data_type: str) -> list[str]:
|
||||
sensor_ids = sorted(mapping.keys())
|
||||
if not sensor_ids:
|
||||
type_name = "压力" if data_type == "pressure" else "流量"
|
||||
raise ValueError(f"未找到{type_name}传感器对应节点(scada_info.type)。")
|
||||
raise ValueError(f"未找到{type_name}传感器对应节点(asset.scada_devices.device_type)。")
|
||||
return sensor_ids
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user