Bind every scheme request to ProjectContext, keep viewer access read-only, reject concurrent optimization jobs without blocking worker threads, and cap export/update payload sizes. Run optimization and workbook work off the event loop.
89 lines
2.5 KiB
Python
89 lines
2.5 KiB
Python
from datetime import datetime
|
|
from typing import Literal
|
|
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
|
|
AdjustmentStatus = Literal["current", "original", "added", "replaced"]
|
|
|
|
|
|
def _normalize_location_ids(value: list[str]) -> list[str]:
|
|
normalized = [str(item).strip() for item in value]
|
|
if any(not item for item in normalized):
|
|
raise ValueError("sensor locations cannot contain blank node IDs")
|
|
if len(set(normalized)) != len(normalized):
|
|
raise ValueError("sensor locations cannot contain duplicate node IDs")
|
|
return normalized
|
|
|
|
|
|
class SensorPlacementOptimizeRequest(BaseModel):
|
|
network: str = Field(
|
|
...,
|
|
min_length=1,
|
|
max_length=63,
|
|
pattern=r"^[^/\\\x00]+$",
|
|
)
|
|
scheme_name: str = Field(..., min_length=1, max_length=32)
|
|
sensor_type: Literal["pressure"]
|
|
method: Literal["sensitivity", "kmeans"]
|
|
sensor_count: int = Field(..., gt=0, le=200)
|
|
min_diameter: int = Field(default=0, ge=0)
|
|
|
|
@field_validator("network")
|
|
@classmethod
|
|
def validate_network(cls, value: str) -> str:
|
|
normalized = value.strip()
|
|
if normalized in {".", ".."}:
|
|
raise ValueError("network must be a project identifier")
|
|
return normalized
|
|
|
|
|
|
class SensorPlacementUpdateRequest(BaseModel):
|
|
expected_sensor_location: list[str] = Field(
|
|
...,
|
|
min_length=1,
|
|
max_length=200,
|
|
)
|
|
sensor_location: list[str] = Field(..., min_length=1, max_length=200)
|
|
|
|
@field_validator("expected_sensor_location", "sensor_location")
|
|
@classmethod
|
|
def validate_locations(cls, value: list[str]) -> list[str]:
|
|
return _normalize_location_ids(value)
|
|
|
|
|
|
class SensorPlacementExportRequest(BaseModel):
|
|
sensor_location: list[str] = Field(..., min_length=1, max_length=200)
|
|
adjustment_status: dict[str, AdjustmentStatus] = Field(
|
|
default_factory=dict,
|
|
max_length=200,
|
|
)
|
|
|
|
@field_validator("sensor_location")
|
|
@classmethod
|
|
def validate_locations(cls, value: list[str]) -> list[str]:
|
|
return _normalize_location_ids(value)
|
|
|
|
|
|
class SensorPointResponse(BaseModel):
|
|
node_id: str
|
|
project_x: float
|
|
project_y: float
|
|
map_x: float
|
|
map_y: float
|
|
longitude: float
|
|
latitude: float
|
|
elevation: float
|
|
|
|
|
|
class SensorPlacementSchemeResponse(BaseModel):
|
|
id: int
|
|
scheme_name: str
|
|
sensor_number: int
|
|
min_diameter: int
|
|
username: str
|
|
create_time: datetime
|
|
sensor_location: list[str]
|
|
sensor_points: list[SensorPointResponse]
|
|
can_edit: bool = False
|