from __future__ import annotations from pathlib import Path from typing import Any from uuid import uuid4 import pandas as pd from app.algorithms.burst_location import run_burst_location from app.services.tjnetwork import dump_inp SeriesInput = pd.Series | dict[str, Any] | list[dict[str, Any]] def _normalize_series(data: SeriesInput, field_name: str) -> pd.Series: if isinstance(data, pd.Series): series = data.copy() elif isinstance(data, dict): series = pd.Series(data, dtype=float) elif isinstance(data, list): if len(data) == 0: return pd.Series(dtype=float) frame = pd.DataFrame(data) if not {"id", "value"}.issubset(frame.columns): raise ValueError(f"{field_name} list item must include 'id' and 'value'.") series = pd.Series( frame["value"].values, index=frame["id"].astype(str).values, dtype=float ) else: raise ValueError(f"Unsupported data format for {field_name}.") series.index = series.index.map(str) return pd.to_numeric(series, errors="raise") def run_burst_location_by_network( *, network: str, pressure_scada_ids: list[str], burst_pressure: SeriesInput, normal_pressure: SeriesInput, burst_leakage: float, flow_scada_ids: list[str] | None = None, burst_flow: SeriesInput | None = None, normal_flow: SeriesInput | None = None, min_dpressure: float = 2.0, basic_pressure: float = 10.0, ) -> dict[str, Any]: if not network: raise ValueError("network is required.") tmp_filename = f"burst_location_{network}_{uuid4().hex}.inp" inp_path = Path.cwd() / tmp_filename try: dump_inp(network, tmp_filename) burst_pressure_series = _normalize_series(burst_pressure, "burst_pressure") normal_pressure_series = _normalize_series(normal_pressure, "normal_pressure") burst_flow_series = ( _normalize_series(burst_flow, "burst_flow") if burst_flow is not None else None ) normal_flow_series = ( _normalize_series(normal_flow, "normal_flow") if normal_flow is not None else None ) return run_burst_location( wn_inp_path=str(inp_path), pressure_scada_ids=pressure_scada_ids, burst_pressure=burst_pressure_series, normal_pressure=normal_pressure_series, burst_leakage=burst_leakage, flow_scada_ids=flow_scada_ids, burst_flow=burst_flow_series, normal_flow=normal_flow_series, min_dpressure=min_dpressure, basic_pressure=basic_pressure, ) finally: if inp_path.exists(): inp_path.unlink()