移除存在无效数据的 cli 命令
This commit is contained in:
@@ -851,6 +851,66 @@ def test_main_invalid_scada_field_is_rejected_before_request(capsys):
|
||||
assert "cleaned_value" in stdout
|
||||
|
||||
|
||||
def test_data_scada_get_rejects_removed_kind_before_request(capsys):
|
||||
exit_code = main(["data", "scada", "get", "--kind", "device", "--id", "D1"])
|
||||
stdout = capsys.readouterr().out
|
||||
|
||||
assert exit_code == 2
|
||||
assert '"code": "INVALID_PARAMETER"' in stdout
|
||||
assert "device" in stdout
|
||||
assert "info" in stdout
|
||||
|
||||
|
||||
def test_data_scada_list_help_only_shows_info_kind():
|
||||
result = runner.invoke(app, ["data", "scada", "list", "--help"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "info" in result.stdout
|
||||
assert "device" not in result.stdout
|
||||
assert "element" not in result.stdout
|
||||
|
||||
|
||||
def test_data_scada_help_no_longer_lists_schema():
|
||||
result = runner.invoke(app, ["data", "scada", "help"])
|
||||
payload = json.loads(result.stdout)
|
||||
|
||||
assert result.exit_code == 0
|
||||
commands = {command["command"] for command in payload["commands"]}
|
||||
assert "data scada get" in commands
|
||||
assert "data scada list" in commands
|
||||
assert "data scada schema" not in commands
|
||||
|
||||
|
||||
def test_data_scada_schema_command_is_removed():
|
||||
result = runner.invoke(app, ["data", "scada", "schema", "--kind", "info"])
|
||||
|
||||
assert result.exit_code == 2
|
||||
assert "No such command 'schema'" in result.output
|
||||
|
||||
|
||||
def test_data_help_no_longer_lists_extension_or_misc():
|
||||
result = runner.invoke(app, ["data", "help"])
|
||||
payload = json.loads(result.stdout)
|
||||
|
||||
assert result.exit_code == 0
|
||||
commands = {command["command"] for command in payload["commands"]}
|
||||
assert "data timeseries" in commands
|
||||
assert "data scada" in commands
|
||||
assert "data scheme" in commands
|
||||
assert "data extension" not in commands
|
||||
assert "data misc" not in commands
|
||||
|
||||
|
||||
def test_removed_data_extension_and_misc_commands_fail():
|
||||
extension_result = runner.invoke(app, ["data", "extension", "list"])
|
||||
misc_result = runner.invoke(app, ["data", "misc", "sensor-placements"])
|
||||
|
||||
assert extension_result.exit_code == 2
|
||||
assert "No such command 'extension'" in extension_result.output
|
||||
assert misc_result.exit_code == 2
|
||||
assert "No such command 'misc'" in misc_result.output
|
||||
|
||||
|
||||
def test_main_bare_analysis_returns_typer_help_without_json_error(capsys):
|
||||
exit_code = main(["analysis"])
|
||||
stdout = capsys.readouterr().out
|
||||
|
||||
@@ -26,8 +26,6 @@ data_timeseries_scada_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
|
||||
data_timeseries_composite_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
|
||||
data_scada_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
|
||||
data_scheme_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
|
||||
data_extension_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
|
||||
data_misc_app = typer.Typer(no_args_is_help=True, cls=TJWaterGroup)
|
||||
|
||||
app.add_typer(network_app, name="network")
|
||||
app.add_typer(component_app, name="component")
|
||||
@@ -50,8 +48,6 @@ data_timeseries_app.add_typer(data_timeseries_scada_app, name="scada")
|
||||
data_timeseries_app.add_typer(data_timeseries_composite_app, name="composite")
|
||||
data_app.add_typer(data_scada_app, name="scada")
|
||||
data_app.add_typer(data_scheme_app, name="scheme")
|
||||
data_app.add_typer(data_extension_app, name="extension")
|
||||
data_app.add_typer(data_misc_app, name="misc")
|
||||
|
||||
GROUP_HELP_APPS: list[tuple[typer.Typer, tuple[str, ...]]] = [
|
||||
(network_app, ("network",)),
|
||||
@@ -75,8 +71,6 @@ GROUP_HELP_APPS: list[tuple[typer.Typer, tuple[str, ...]]] = [
|
||||
(data_timeseries_composite_app, ("data", "timeseries", "composite")),
|
||||
(data_scada_app, ("data", "scada")),
|
||||
(data_scheme_app, ("data", "scheme")),
|
||||
(data_extension_app, ("data", "extension")),
|
||||
(data_misc_app, ("data", "misc")),
|
||||
]
|
||||
|
||||
TOP_LEVEL_COMMANDS = {"help", "network", "component", "simulation", "analysis", "data"}
|
||||
|
||||
@@ -5,8 +5,6 @@ from typing import Annotated
|
||||
import typer
|
||||
|
||||
from .apps import (
|
||||
data_extension_app,
|
||||
data_misc_app,
|
||||
data_scada_app,
|
||||
data_scheme_app,
|
||||
data_timeseries_composite_app,
|
||||
@@ -22,7 +20,6 @@ from .option_types import (
|
||||
JUNCTION_TIMESERIES_FIELDS,
|
||||
SCADA_TIMESERIES_FIELDS,
|
||||
ScadaListKind,
|
||||
ScadaSchemaKind,
|
||||
SimulationQuery,
|
||||
timeseries_fields_for_element_type,
|
||||
)
|
||||
@@ -414,15 +411,6 @@ def data_composite_pipeline_health(
|
||||
|
||||
def _scada_mapping(kind: str, action: str) -> tuple[str, dict[str, str]]:
|
||||
mapping = {
|
||||
("device", "schema"): ("/getscadadeviceschema/", {}),
|
||||
("device", "get"): ("/getscadadevice/", {"id_param": "id"}),
|
||||
("device", "list"): ("/getallscadadevices/", {}),
|
||||
("device-data", "schema"): ("/getscadadevicedataschema/", {}),
|
||||
("device-data", "get"): ("/getscadadevicedata/", {"id_param": "device_id"}),
|
||||
("element", "schema"): ("/getscadaelementschema/", {}),
|
||||
("element", "get"): ("/getscadaelement/", {"id_param": "id"}),
|
||||
("element", "list"): ("/getscadaelements/", {}),
|
||||
("info", "schema"): ("/getscadainfoschema/", {}),
|
||||
("info", "get"): ("/getscadainfo/", {"id_param": "id"}),
|
||||
("info", "list"): ("/getallscadainfo/", {}),
|
||||
}
|
||||
@@ -437,28 +425,10 @@ def _scada_mapping(kind: str, action: str) -> tuple[str, dict[str, str]]:
|
||||
return result
|
||||
|
||||
|
||||
@data_scada_app.command("schema")
|
||||
def data_scada_schema(
|
||||
ctx: typer.Context,
|
||||
kind: Annotated[ScadaSchemaKind, typer.Option("--kind", help="SCADA 类型,仅支持 device|device-data|element|info")],
|
||||
) -> None:
|
||||
runtime = runtime_context(ctx)
|
||||
path, _ = _scada_mapping(kind.value, "schema")
|
||||
emit_api(
|
||||
ctx,
|
||||
summary="读取 SCADA schema 成功",
|
||||
method="GET",
|
||||
path=path,
|
||||
params={"network": require_network(runtime)},
|
||||
require_auth=True,
|
||||
require_network_ctx=True,
|
||||
)
|
||||
|
||||
|
||||
@data_scada_app.command("get")
|
||||
def data_scada_get(
|
||||
ctx: typer.Context,
|
||||
kind: Annotated[ScadaSchemaKind, typer.Option("--kind", help="SCADA 类型,仅支持 device|device-data|element|info")],
|
||||
kind: Annotated[ScadaListKind, typer.Option("--kind", help="SCADA 类型,仅支持 info")],
|
||||
id: Annotated[str, typer.Option("--id", help="记录 ID")],
|
||||
) -> None:
|
||||
runtime = runtime_context(ctx)
|
||||
@@ -478,7 +448,7 @@ def data_scada_get(
|
||||
@data_scada_app.command("list")
|
||||
def data_scada_list(
|
||||
ctx: typer.Context,
|
||||
kind: Annotated[ScadaListKind, typer.Option("--kind", help="SCADA 类型,仅支持 device|element|info;device-data 无 list 接口")],
|
||||
kind: Annotated[ScadaListKind, typer.Option("--kind", help="SCADA 类型,仅支持 info")],
|
||||
) -> None:
|
||||
runtime = runtime_context(ctx)
|
||||
path, _ = _scada_mapping(kind.value, "list")
|
||||
@@ -536,76 +506,3 @@ def data_scheme_list(ctx: typer.Context) -> None:
|
||||
require_auth=True,
|
||||
require_network_ctx=True,
|
||||
)
|
||||
|
||||
|
||||
@data_extension_app.command("keys")
|
||||
def data_extension_keys(ctx: typer.Context) -> None:
|
||||
runtime = runtime_context(ctx)
|
||||
emit_api(
|
||||
ctx,
|
||||
summary="读取扩展数据键成功",
|
||||
method="GET",
|
||||
path="/getallextensiondatakeys/",
|
||||
params={"network": require_network(runtime)},
|
||||
require_auth=True,
|
||||
require_network_ctx=True,
|
||||
)
|
||||
|
||||
|
||||
@data_extension_app.command("get")
|
||||
def data_extension_get(
|
||||
ctx: typer.Context,
|
||||
key: Annotated[str, typer.Option("--key", help="扩展键")],
|
||||
) -> None:
|
||||
runtime = runtime_context(ctx)
|
||||
emit_api(
|
||||
ctx,
|
||||
summary="读取扩展数据成功",
|
||||
method="GET",
|
||||
path="/getextensiondata/",
|
||||
params={"network": require_network(runtime), "key": key},
|
||||
require_auth=True,
|
||||
require_network_ctx=True,
|
||||
)
|
||||
|
||||
|
||||
@data_extension_app.command("list")
|
||||
def data_extension_list(ctx: typer.Context) -> None:
|
||||
runtime = runtime_context(ctx)
|
||||
emit_api(
|
||||
ctx,
|
||||
summary="读取扩展数据列表成功",
|
||||
method="GET",
|
||||
path="/getallextensiondata/",
|
||||
params={"network": require_network(runtime)},
|
||||
require_auth=True,
|
||||
require_network_ctx=True,
|
||||
)
|
||||
|
||||
|
||||
@data_misc_app.command("sensor-placements")
|
||||
def data_misc_sensor_placements(ctx: typer.Context) -> None:
|
||||
runtime = runtime_context(ctx)
|
||||
emit_api(
|
||||
ctx,
|
||||
summary="读取传感器位置成功",
|
||||
method="GET",
|
||||
path="/getallsensorplacements/",
|
||||
params={"network": require_network(runtime)},
|
||||
require_auth=True,
|
||||
require_network_ctx=True,
|
||||
)
|
||||
|
||||
|
||||
@data_misc_app.command("burst-location-results")
|
||||
def data_misc_burst_location_results(ctx: typer.Context) -> None:
|
||||
runtime = runtime_context(ctx)
|
||||
emit_api(
|
||||
ctx,
|
||||
summary="读取爆管定位结果成功",
|
||||
method="GET",
|
||||
path="/getallburstlocateresults/",
|
||||
params={"network": require_network(runtime)},
|
||||
require_auth=True,
|
||||
require_network_ctx=True,
|
||||
)
|
||||
|
||||
@@ -100,9 +100,8 @@ def _sample_option_value(path: tuple[str, ...], option_name: str) -> str:
|
||||
(("component", "option", "schema"), "kind"): "time",
|
||||
(("component", "option", "get"), "kind"): "time",
|
||||
(("data", "timeseries", "composite"), "kind"): "scada-simulation",
|
||||
(("data", "scada", "schema"), "kind"): "device",
|
||||
(("data", "scada", "get"), "kind"): "device",
|
||||
(("data", "scada", "list"), "kind"): "device",
|
||||
(("data", "scada", "get"), "kind"): "info",
|
||||
(("data", "scada", "list"), "kind"): "info",
|
||||
}
|
||||
if (path, option_name) in path_specific_samples:
|
||||
return path_specific_samples[(path, option_name)]
|
||||
|
||||
@@ -36,16 +36,7 @@ class DataSource(str, Enum):
|
||||
SIMULATION = "simulation"
|
||||
|
||||
|
||||
class ScadaSchemaKind(str, Enum):
|
||||
DEVICE = "device"
|
||||
DEVICE_DATA = "device-data"
|
||||
ELEMENT = "element"
|
||||
INFO = "info"
|
||||
|
||||
|
||||
class ScadaListKind(str, Enum):
|
||||
DEVICE = "device"
|
||||
ELEMENT = "element"
|
||||
INFO = "info"
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ GROUP_SUMMARIES: dict[tuple[str, ...], str] = {
|
||||
("analysis", "burst-location", "schemes"): "爆管定位方案查询命令。",
|
||||
("analysis", "risk"): "风险分析相关命令。",
|
||||
("analysis", "sensor-placement"): "传感器选址相关命令。",
|
||||
("data",): "时序、SCADA、方案和扩展数据查询命令。",
|
||||
("data",): "时序、SCADA 和方案数据查询命令。",
|
||||
("data", "timeseries"): "时序数据查询命令。",
|
||||
("data", "timeseries", "realtime"): "实时模拟时序查询命令。",
|
||||
("data", "timeseries", "scheme"): "方案时序查询命令。",
|
||||
@@ -24,8 +24,6 @@ GROUP_SUMMARIES: dict[tuple[str, ...], str] = {
|
||||
("data", "timeseries", "composite"): "复合时序查询命令。",
|
||||
("data", "scada"): "SCADA 元数据查询命令。",
|
||||
("data", "scheme"): "方案数据查询命令。",
|
||||
("data", "extension"): "扩展数据查询命令。",
|
||||
("data", "misc"): "其他结果数据查询命令。",
|
||||
}
|
||||
|
||||
HIDDEN_PATH_PREFIXES: tuple[tuple[str, ...], ...] = (
|
||||
@@ -468,41 +466,22 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
||||
),
|
||||
examples=("tjwater-cli data timeseries composite pipeline-health --pipe P1 --start-time 2025-01-02T03:00:00+08:00 --end-time 2025-01-02T04:00:00+08:00",),
|
||||
),
|
||||
("data", "scada", "schema"): CommandDoc(
|
||||
path=("data", "scada", "schema"),
|
||||
summary="读取 SCADA schema",
|
||||
description="kind 支持 device、device-data、element、info。",
|
||||
options=(CommandOptionDoc("kind", "SCADA 数据类型", required=True),),
|
||||
examples=(
|
||||
"tjwater-cli data scada schema --kind device",
|
||||
"tjwater-cli data scada schema --kind device-data",
|
||||
"tjwater-cli data scada schema --kind element",
|
||||
"tjwater-cli data scada schema --kind info",
|
||||
),
|
||||
),
|
||||
("data", "scada", "get"): CommandDoc(
|
||||
path=("data", "scada", "get"),
|
||||
summary="读取单条 SCADA 元数据",
|
||||
description="kind 支持 device、device-data、element、info。",
|
||||
description="kind 仅支持 info。",
|
||||
options=(
|
||||
CommandOptionDoc("kind", "SCADA 数据类型", required=True),
|
||||
CommandOptionDoc("id", "记录 ID", required=True),
|
||||
),
|
||||
examples=(
|
||||
"tjwater-cli data scada get --kind device --id D1",
|
||||
"tjwater-cli data scada get --kind element --id E1",
|
||||
),
|
||||
examples=("tjwater-cli data scada get --kind info --id SCADA-001",),
|
||||
),
|
||||
("data", "scada", "list"): CommandDoc(
|
||||
path=("data", "scada", "list"),
|
||||
summary="列出 SCADA 元数据",
|
||||
description="kind 支持 device、element、info;device-data 当前后端无 list 接口。",
|
||||
description="kind 仅支持 info。",
|
||||
options=(CommandOptionDoc("kind", "SCADA 数据类型", required=True),),
|
||||
examples=(
|
||||
"tjwater-cli data scada list --kind device",
|
||||
"tjwater-cli data scada list --kind element",
|
||||
"tjwater-cli data scada list --kind info",
|
||||
),
|
||||
examples=("tjwater-cli data scada list --kind info",),
|
||||
),
|
||||
("data", "scheme", "schema"): CommandDoc(
|
||||
path=("data", "scheme", "schema"),
|
||||
@@ -523,37 +502,6 @@ COMMAND_DOCS: dict[tuple[str, ...], CommandDoc] = {
|
||||
description="调用 /getallschemes/。",
|
||||
examples=("tjwater-cli data scheme list",),
|
||||
),
|
||||
("data", "extension", "keys"): CommandDoc(
|
||||
path=("data", "extension", "keys"),
|
||||
summary="列出扩展数据键",
|
||||
description="调用 /getallextensiondatakeys/。",
|
||||
examples=("tjwater-cli data extension keys",),
|
||||
),
|
||||
("data", "extension", "get"): CommandDoc(
|
||||
path=("data", "extension", "get"),
|
||||
summary="读取扩展数据",
|
||||
description="调用 /getextensiondata/。",
|
||||
options=(CommandOptionDoc("key", "扩展键", required=True),),
|
||||
examples=("tjwater-cli data extension get --key my_key",),
|
||||
),
|
||||
("data", "extension", "list"): CommandDoc(
|
||||
path=("data", "extension", "list"),
|
||||
summary="列出扩展数据",
|
||||
description="调用 /getallextensiondata/。",
|
||||
examples=("tjwater-cli data extension list",),
|
||||
),
|
||||
("data", "misc", "sensor-placements"): CommandDoc(
|
||||
path=("data", "misc", "sensor-placements"),
|
||||
summary="列出传感器布置结果",
|
||||
description="调用 /getallsensorplacements/。",
|
||||
examples=("tjwater-cli data misc sensor-placements",),
|
||||
),
|
||||
("data", "misc", "burst-location-results"): CommandDoc(
|
||||
path=("data", "misc", "burst-location-results"),
|
||||
summary="列出爆管定位结果",
|
||||
description="调用 /getallburstlocateresults/。",
|
||||
examples=("tjwater-cli data misc burst-location-results",),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -259,12 +259,8 @@ app/api/v1/endpoints/project_data.py
|
||||
| `tjwater-cli data timeseries scada query --device-id ID --start-time TIME --end-time TIME [--device-id ID ...] [--field FIELD]` | `GET /scada/by-ids-time-range`、`GET /scada/by-ids-field-time-range` | SCADA 时序;CLI 把重复 `--device-id` 转换为后端逗号分隔参数 |
|
||||
| `tjwater-cli data timeseries composite --kind scada-simulation\|element-simulation\|element-scada --feature FEATURE --start-time TIME --end-time TIME` | `GET /composite/*` | 复合查询,`--feature` 可重复 |
|
||||
| `tjwater-cli data timeseries composite pipeline-health --pipe PIPE --start-time TIME --end-time TIME` | `GET /composite/pipeline-health-prediction` | 管道健康预测 |
|
||||
| `tjwater-cli data scada schema --kind device\|device-data\|element\|info` | `GET /getscada*schema/` | `SCADA` 元数据 `schema` |
|
||||
| `tjwater-cli data scada get\|list --kind device\|device-data\|element\|info` | `scada.py` 下 `GET` 查询接口 | `SCADA` 元数据 |
|
||||
| `tjwater-cli data scada get\|list --kind info` | `GET /getscadainfo/`、`GET /getallscadainfo/` | `SCADA info` 元数据 |
|
||||
| `tjwater-cli data scheme schema\|get\|list` | `schemes.py` 下 `GET` 接口 | 当前 project 方案查询 |
|
||||
| `tjwater-cli data extension keys\|get\|list` | `extension.py` 下 `GET` 查询接口 | 当前 project 扩展数据查询 |
|
||||
| `tjwater-cli data misc sensor-placements` | `GET /getallsensorplacements/` | 当前 project 传感器位置 |
|
||||
| `tjwater-cli data misc burst-location-results` | `GET /getallburstlocateresults/` | 当前 project 爆管定位结果 |
|
||||
|
||||
- `realtime` 是首批 simulation 结果的主读取域;CLI 可以按任务语义组合 `links`、`nodes`、`simulation-by-id-time`、`simulation-by-time-property`,但底层数据源仍以 `realtime.py` 为准。
|
||||
- `realtime`、`scheme`、`composite` 等时间查询命令面向用户时仍按 **UTC+8** 输入;CLI/服务端负责转换为后端使用的 **UTC0** 条件进行检索。若返回结果直接包含时间戳,必须显式带时区,避免把存储时间和展示时间混淆。
|
||||
|
||||
Reference in New Issue
Block a user