data-ops-cli 0.2.1__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/PKG-INFO +1 -1
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/data_ops_cli.egg-info/PKG-INFO +1 -1
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/data_ops_cli.egg-info/SOURCES.txt +6 -0
- data_ops_cli-0.2.3/src/dataops_cli/__init__.py +1 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/auth.py +24 -8
- data_ops_cli-0.2.3/src/dataops_cli/cli/data_table.py +260 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/dataset.py +3 -5
- data_ops_cli-0.2.3/src/dataops_cli/cli/datasource.py +196 -0
- data_ops_cli-0.2.3/src/dataops_cli/cli/dict_.py +96 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/factory.py +2 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/table.py +90 -6
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/widget.py +1 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/workflow.py +1 -1
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/exceptions.py +12 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/__init__.py +1 -1
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/utils/dbConn_sync.py +2 -2
- data_ops_cli-0.2.3/src/dataops_cli/schems/__init__.py +27 -0
- data_ops_cli-0.2.3/src/dataops_cli/schems/data_table.py +436 -0
- data_ops_cli-0.2.3/src/dataops_cli/schems/datasource.py +25 -0
- data_ops_cli-0.2.3/src/dataops_cli/schems/dict_.py +13 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/schems/enums.py +39 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/schems/meta.py +52 -3
- data_ops_cli-0.2.3/src/dataops_cli/schems/table.py +65 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/schems/widget.py +26 -9
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/schems/workflow.py +2 -2
- data_ops_cli-0.2.3/src/dataops_cli/sdk/__init__.py +35 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/sdk/auth.py +9 -16
- data_ops_cli-0.2.3/src/dataops_cli/sdk/data_table.py +231 -0
- data_ops_cli-0.2.3/src/dataops_cli/sdk/dataset.py +54 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/sdk/datasource.py +24 -3
- data_ops_cli-0.2.3/src/dataops_cli/sdk/dict_.py +69 -0
- data_ops_cli-0.2.3/src/dataops_cli/sdk/meta.py +510 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/sdk/report.py +3 -4
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/sdk/table.py +13 -12
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/sdk/workflow.py +3 -4
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/session.py +40 -1
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/utils.py +13 -0
- data_ops_cli-0.2.1/src/dataops_cli/__init__.py +0 -1
- data_ops_cli-0.2.1/src/dataops_cli/cli/data_table.py +0 -43
- data_ops_cli-0.2.1/src/dataops_cli/cli/datasource.py +0 -102
- data_ops_cli-0.2.1/src/dataops_cli/schems/__init__.py +0 -9
- data_ops_cli-0.2.1/src/dataops_cli/schems/datasource.py +0 -59
- data_ops_cli-0.2.1/src/dataops_cli/sdk/__init__.py +0 -10
- data_ops_cli-0.2.1/src/dataops_cli/sdk/data_table.py +0 -59
- data_ops_cli-0.2.1/src/dataops_cli/sdk/meta.py +0 -254
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/README.md +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/pyproject.toml +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/setup.cfg +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/data_ops_cli.egg-info/dependency_links.txt +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/data_ops_cli.egg-info/entry_points.txt +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/data_ops_cli.egg-info/requires.txt +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/data_ops_cli.egg-info/top_level.txt +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/__init__.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/metrics.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/ref.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/report.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/cli/utils.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/constants.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/__init__.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/config.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/constants.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/instance.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/nacos_exception.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/naming_param.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/naming_request.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/naming_response.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/preserved_metadata_key.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/schemas/nacos/service.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/utils/__init__.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/utils/common.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/utils/dbConn_base.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/utils/duckdb_manager.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/utils/log.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/utils/nacos.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/runner/utils/neo4j_sync.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/schems/group.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/schems/query.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/schems/report.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/schems/user.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/sdk/ref.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/tiptap/__init__.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/tiptap/builder.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/tiptap/constants.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/tiptap/models.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/tiptap/parse_utils.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/tiptap/render_utils.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/tiptap/renderers.py +0 -0
- {data_ops_cli-0.2.1 → data_ops_cli-0.2.3}/src/dataops_cli/tiptap/tool.py +0 -0
|
@@ -16,6 +16,7 @@ src/dataops_cli/cli/auth.py
|
|
|
16
16
|
src/dataops_cli/cli/data_table.py
|
|
17
17
|
src/dataops_cli/cli/dataset.py
|
|
18
18
|
src/dataops_cli/cli/datasource.py
|
|
19
|
+
src/dataops_cli/cli/dict_.py
|
|
19
20
|
src/dataops_cli/cli/factory.py
|
|
20
21
|
src/dataops_cli/cli/metrics.py
|
|
21
22
|
src/dataops_cli/cli/ref.py
|
|
@@ -44,19 +45,24 @@ src/dataops_cli/runner/utils/log.py
|
|
|
44
45
|
src/dataops_cli/runner/utils/nacos.py
|
|
45
46
|
src/dataops_cli/runner/utils/neo4j_sync.py
|
|
46
47
|
src/dataops_cli/schems/__init__.py
|
|
48
|
+
src/dataops_cli/schems/data_table.py
|
|
47
49
|
src/dataops_cli/schems/datasource.py
|
|
50
|
+
src/dataops_cli/schems/dict_.py
|
|
48
51
|
src/dataops_cli/schems/enums.py
|
|
49
52
|
src/dataops_cli/schems/group.py
|
|
50
53
|
src/dataops_cli/schems/meta.py
|
|
51
54
|
src/dataops_cli/schems/query.py
|
|
52
55
|
src/dataops_cli/schems/report.py
|
|
56
|
+
src/dataops_cli/schems/table.py
|
|
53
57
|
src/dataops_cli/schems/user.py
|
|
54
58
|
src/dataops_cli/schems/widget.py
|
|
55
59
|
src/dataops_cli/schems/workflow.py
|
|
56
60
|
src/dataops_cli/sdk/__init__.py
|
|
57
61
|
src/dataops_cli/sdk/auth.py
|
|
58
62
|
src/dataops_cli/sdk/data_table.py
|
|
63
|
+
src/dataops_cli/sdk/dataset.py
|
|
59
64
|
src/dataops_cli/sdk/datasource.py
|
|
65
|
+
src/dataops_cli/sdk/dict_.py
|
|
60
66
|
src/dataops_cli/sdk/meta.py
|
|
61
67
|
src/dataops_cli/sdk/ref.py
|
|
62
68
|
src/dataops_cli/sdk/report.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.3"
|
|
@@ -12,7 +12,6 @@ import typer
|
|
|
12
12
|
from rich.console import Console
|
|
13
13
|
from rich.table import Table
|
|
14
14
|
|
|
15
|
-
from dataops_cli.cli.datasource import get_datasource_config
|
|
16
15
|
from dataops_cli.constants import USER_INFO_FILE
|
|
17
16
|
from dataops_cli.exceptions import AuthenticationError
|
|
18
17
|
from dataops_cli.schems import UserInfo
|
|
@@ -23,6 +22,7 @@ from dataops_cli.sdk.auth import (
|
|
|
23
22
|
service_logout,
|
|
24
23
|
service_set_application,
|
|
25
24
|
)
|
|
25
|
+
from dataops_cli.sdk.datasource import get_application_datasource_config
|
|
26
26
|
from dataops_cli.utils import get_work_dir
|
|
27
27
|
|
|
28
28
|
auth_typer = typer.Typer(name="auth", help="认证相关命令", no_args_is_help=True)
|
|
@@ -43,14 +43,30 @@ def auth_status() -> None:
|
|
|
43
43
|
print("未登录")
|
|
44
44
|
else:
|
|
45
45
|
user_info = UserInfo.model_validate_json(target_path.read_bytes())
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
try:
|
|
47
|
+
# application
|
|
48
|
+
application = service_get_current_application()
|
|
49
|
+
datasource_config = get_application_datasource_config()
|
|
50
|
+
except AuthenticationError:
|
|
51
|
+
print(f"{user_info.true_name}({user_info.user_name})登录过期,请重新登录")
|
|
51
52
|
else:
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
if application is None:
|
|
54
|
+
print(f"{user_info.true_name}({user_info.user_name})已登录, 应用未设置")
|
|
55
|
+
else:
|
|
56
|
+
print(
|
|
57
|
+
f"{user_info.true_name}({user_info.user_name})已登录, 应用设置为{application.LABEL_CN}({application.CODE})"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
table = Table(
|
|
61
|
+
"datasource",
|
|
62
|
+
"功能",
|
|
63
|
+
title=f"应用数据源配置\n{application.LABEL_CN}({application.CODE})",
|
|
64
|
+
)
|
|
65
|
+
table.add_row(datasource_config.main.name, "主数据源")
|
|
66
|
+
table.add_row(datasource_config.hazard_read.name, "隐患读数据源")
|
|
67
|
+
table.add_row(datasource_config.hazard_write.name, "隐患写数据源")
|
|
68
|
+
console = Console(no_color=True)
|
|
69
|
+
console.print(table)
|
|
54
70
|
|
|
55
71
|
|
|
56
72
|
@auth_typer.command("login", no_args_is_help=True)
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""多维表格 (DataTable) CLI 命令模块.
|
|
2
|
+
|
|
3
|
+
提供多维表格的列表查询和详情功能。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Annotated, Optional
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
from dataops_cli.cli.utils import render_table
|
|
15
|
+
from dataops_cli.exceptions import DataOpsError
|
|
16
|
+
from dataops_cli.schems import MetaType, PageParam, SqlQueryParam
|
|
17
|
+
from dataops_cli.sdk import (
|
|
18
|
+
service_get_data_table_by_code,
|
|
19
|
+
service_get_data_table_by_name,
|
|
20
|
+
service_get_data_table_data_by_code,
|
|
21
|
+
service_get_data_table_data_by_name,
|
|
22
|
+
)
|
|
23
|
+
from dataops_cli.sdk.meta import release_list_list
|
|
24
|
+
from dataops_cli.utils import filter_nodes_by_pattern
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# CLI 命令
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
data_table_typer = typer.Typer(
|
|
31
|
+
name="data_table",
|
|
32
|
+
help="多维表格相关",
|
|
33
|
+
no_args_is_help=True,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@data_table_typer.command("list")
|
|
38
|
+
def list_datatable(
|
|
39
|
+
datasource: Annotated[str | None, typer.Argument(help="数据源")] = None,
|
|
40
|
+
query: Annotated[str | None, typer.Option("-q", "--query", help="正则表达式")] = None,
|
|
41
|
+
) -> None:
|
|
42
|
+
"""列出所有多维表格.
|
|
43
|
+
|
|
44
|
+
从元数据服务获取多维表格列表,支持通过正则表达式按名称、标题或备注进行过滤。
|
|
45
|
+
|
|
46
|
+
示例:
|
|
47
|
+
列出所有多维表格
|
|
48
|
+
>>> data-ops-cli data_table list
|
|
49
|
+
|
|
50
|
+
按数据源过滤
|
|
51
|
+
>>> data-ops-cli data_table list my_datasource
|
|
52
|
+
|
|
53
|
+
按名称/标题/备注过滤
|
|
54
|
+
>>> data-ops-cli data_table list --query ".*报表.*"
|
|
55
|
+
>>> data-ops-cli data_table list -q "日报"
|
|
56
|
+
"""
|
|
57
|
+
try:
|
|
58
|
+
group_nodes = release_list_list(MetaType.DATA_TABLE, datasource=datasource)
|
|
59
|
+
except DataOpsError as e:
|
|
60
|
+
typer.echo(f"错误: {e.message}")
|
|
61
|
+
raise typer.Exit(code=1) from None
|
|
62
|
+
|
|
63
|
+
if query:
|
|
64
|
+
group_nodes = filter_nodes_by_pattern(group_nodes, query, include_digest=True)
|
|
65
|
+
|
|
66
|
+
table = Table("code", "title", "remark")
|
|
67
|
+
console = Console(no_color=True)
|
|
68
|
+
for column in group_nodes:
|
|
69
|
+
table.add_row(column.CODE, column.TITLE, column.REMARK or "")
|
|
70
|
+
console.print(table)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@data_table_typer.command("detail", no_args_is_help=True)
|
|
74
|
+
def get_datatable_detail(
|
|
75
|
+
arg1: Annotated[str, typer.Argument(help="编码或数据源")],
|
|
76
|
+
name: Annotated[Optional[str], typer.Argument(help="表名")] = None,
|
|
77
|
+
) -> None:
|
|
78
|
+
"""获取多维表格的完整详情.
|
|
79
|
+
|
|
80
|
+
根据多维表格编码获取详细信息,包括字段定义、模型配置、查询配置等。
|
|
81
|
+
支持通过 --field 选项输出指定字段。
|
|
82
|
+
|
|
83
|
+
示例:
|
|
84
|
+
获取多维表格完整信息
|
|
85
|
+
>>> data-ops-cli data_table detail dt_code_001
|
|
86
|
+
|
|
87
|
+
只输出字段列表 (fields)
|
|
88
|
+
>>> data-ops-cli data_table detail dt_code_001 --field data.fields
|
|
89
|
+
|
|
90
|
+
输出指定字段的 JSON
|
|
91
|
+
>>> data-ops-cli data_table detail dt_code_001 -f data
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
meta = (
|
|
95
|
+
service_get_data_table_by_name(arg1, name)
|
|
96
|
+
if name
|
|
97
|
+
else service_get_data_table_by_code(arg1)
|
|
98
|
+
)
|
|
99
|
+
except DataOpsError as e:
|
|
100
|
+
typer.echo(e.message)
|
|
101
|
+
raise typer.Exit(code=1) from None
|
|
102
|
+
|
|
103
|
+
table = Table("field", "value", show_header=False)
|
|
104
|
+
table.add_row("datasource", meta.data.model.datasource)
|
|
105
|
+
table.add_row("引用类型", meta.data.model.ref_type)
|
|
106
|
+
table.add_row("引用编码", meta.data.model.ref_code)
|
|
107
|
+
table.add_row("引用表名", meta.data.model.table_name)
|
|
108
|
+
|
|
109
|
+
console = Console(no_color=True)
|
|
110
|
+
console.print(table)
|
|
111
|
+
|
|
112
|
+
table = Table("column(visable)", "type", "title", "visible", title="字段")
|
|
113
|
+
for field in meta.data.fields:
|
|
114
|
+
table.add_row(field.name + f"({'√' if field.visible else 'x'})", field.type, field.title)
|
|
115
|
+
console.print(table)
|
|
116
|
+
|
|
117
|
+
# TODO 完善输出
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# @data_table_typer.command("create", no_args_is_help=True)
|
|
121
|
+
# def create_datatable(
|
|
122
|
+
# code: Annotated[str, typer.Argument(help="多维表格编码")],
|
|
123
|
+
# title: Annotated[str, typer.Argument(help="中文标题")],
|
|
124
|
+
# data_file: Annotated[
|
|
125
|
+
# str,
|
|
126
|
+
# typer.Argument(
|
|
127
|
+
# help="meta.data JSON 文件路径或 JSON 字符串 (以 { 开头视为 JSON, 否则视为文件路径)"
|
|
128
|
+
# ),
|
|
129
|
+
# ],
|
|
130
|
+
# remark: Annotated[str | None, typer.Option("-r", "--remark", help="备注")] = None,
|
|
131
|
+
# ) -> None:
|
|
132
|
+
# """创建多维表格.
|
|
133
|
+
|
|
134
|
+
# 从本地 JSON 文件读取 meta.data 配置并创建多维表格。
|
|
135
|
+
|
|
136
|
+
# 示例:
|
|
137
|
+
# 从文件创建
|
|
138
|
+
# >>> data-ops-cli data_table create dt_code_001 "测试表格" ./data_table.json
|
|
139
|
+
|
|
140
|
+
# 直接传入 JSON 字符串
|
|
141
|
+
# >>> data-ops-cli data_table create dt_code_001 "测试表格" '{"model": {...}, "fields": [...]}'
|
|
142
|
+
# """
|
|
143
|
+
# # 解析 data: 可能是文件路径或 JSON 字符串
|
|
144
|
+
# data: dict
|
|
145
|
+
# if data_file.strip().startswith("{"):
|
|
146
|
+
# try:
|
|
147
|
+
# data = json.loads(data_file)
|
|
148
|
+
# except json.JSONDecodeError as e:
|
|
149
|
+
# typer.echo(f"错误: JSON 解析失败: {e}")
|
|
150
|
+
# raise typer.Exit(code=1) from None
|
|
151
|
+
# else:
|
|
152
|
+
# path = Path(data_file)
|
|
153
|
+
# if not path.exists():
|
|
154
|
+
# typer.echo(f"错误: 文件不存在: {data_file}")
|
|
155
|
+
# raise typer.Exit(code=1) from None
|
|
156
|
+
# try:
|
|
157
|
+
# data = json.loads(path.read_text(encoding="utf-8"))
|
|
158
|
+
# except json.JSONDecodeError as e:
|
|
159
|
+
# typer.echo(f"错误: JSON 文件解析失败: {e}")
|
|
160
|
+
# raise typer.Exit(code=1) from None
|
|
161
|
+
|
|
162
|
+
# try:
|
|
163
|
+
# result = service_create_data_table(code=code, title=title, data=data)
|
|
164
|
+
# cuid = result.get("cuid", "") if isinstance(result, dict) else ""
|
|
165
|
+
# typer.echo(f"成功创建多维表格: code={code}, cuid={cuid}")
|
|
166
|
+
# except DataOpsError as e:
|
|
167
|
+
# typer.echo(f"错误: {e.message}")
|
|
168
|
+
# raise typer.Exit(code=1) from None
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# @data_table_typer.command("update", no_args_is_help=True)
|
|
172
|
+
# def update_datatable(
|
|
173
|
+
# code: Annotated[str, typer.Argument(help="多维表格编码")],
|
|
174
|
+
# title: Annotated[str, typer.Argument(help="中文标题")],
|
|
175
|
+
# data_file: Annotated[
|
|
176
|
+
# str,
|
|
177
|
+
# typer.Argument(help="meta.data JSON 文件路径或 JSON 字符串"),
|
|
178
|
+
# ],
|
|
179
|
+
# cuid: Annotated[
|
|
180
|
+
# str | None, typer.Option("-c", "--cuid", help="元数据 CUID (更新时必传)")
|
|
181
|
+
# ] = None,
|
|
182
|
+
# ) -> None:
|
|
183
|
+
# """更新多维表格.
|
|
184
|
+
|
|
185
|
+
# 从本地 JSON 文件读取 meta.data 配置并更新多维表格。
|
|
186
|
+
# 需要通过 --cuid 指定要更新的元数据 CUID。
|
|
187
|
+
|
|
188
|
+
# 示例:
|
|
189
|
+
# 从文件更新
|
|
190
|
+
# >>> data-ops-cli data_table update dt_code_001 "测试表格" ./data_table.json --cuid abc123
|
|
191
|
+
|
|
192
|
+
# 直接传入 JSON 字符串
|
|
193
|
+
# >>> data-ops-cli data_table update dt_code_001 "测试表格" '{"model": {...}, "fields": [...]}' -c abc123
|
|
194
|
+
# """
|
|
195
|
+
# if not cuid:
|
|
196
|
+
# typer.echo("错误: 更新操作需要 --cuid 参数,请先通过 detail 命令获取 CUID")
|
|
197
|
+
# raise typer.Exit(code=1) from None
|
|
198
|
+
|
|
199
|
+
# # 解析 data: 可能是文件路径或 JSON 字符串
|
|
200
|
+
# data: dict
|
|
201
|
+
# if data_file.strip().startswith("{"):
|
|
202
|
+
# try:
|
|
203
|
+
# data = json.loads(data_file)
|
|
204
|
+
# except json.JSONDecodeError as e:
|
|
205
|
+
# typer.echo(f"错误: JSON 解析失败: {e}")
|
|
206
|
+
# raise typer.Exit(code=1) from None
|
|
207
|
+
# else:
|
|
208
|
+
# path = Path(data_file)
|
|
209
|
+
# if not path.exists():
|
|
210
|
+
# typer.echo(f"错误: 文件不存在: {data_file}")
|
|
211
|
+
# raise typer.Exit(code=1) from None
|
|
212
|
+
# try:
|
|
213
|
+
# data = json.loads(path.read_text(encoding="utf-8"))
|
|
214
|
+
# except json.JSONDecodeError as e:
|
|
215
|
+
# typer.echo(f"错误: JSON 文件解析失败: {e}")
|
|
216
|
+
# raise typer.Exit(code=1) from None
|
|
217
|
+
|
|
218
|
+
# try:
|
|
219
|
+
# service_update_data_table(code=code, title=title, data=data, cuid=cuid)
|
|
220
|
+
# typer.echo(f"成功更新多维表格: code={code}, cuid={cuid}")
|
|
221
|
+
# except DataOpsError as e:
|
|
222
|
+
# typer.echo(f"错误: {e.message}")
|
|
223
|
+
# raise typer.Exit(code=1) from None
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@data_table_typer.command("data", no_args_is_help=True)
|
|
227
|
+
def get_datatable_data(
|
|
228
|
+
arg1: Annotated[str, typer.Argument(help="编码或数据源")],
|
|
229
|
+
name: Annotated[Optional[str], typer.Argument(help="表名")] = None,
|
|
230
|
+
page: Annotated[int, typer.Option("--page", "-p", min=0, help="耶稣")] = 0,
|
|
231
|
+
size: Annotated[int, typer.Option("--size", "-l", min=0, help="单页数")] = 20,
|
|
232
|
+
query_param: Annotated[
|
|
233
|
+
str | None, typer.Option("--query", "-q", help="查询参数(SqlQueryParam)")
|
|
234
|
+
] = None,
|
|
235
|
+
) -> None:
|
|
236
|
+
"""获取多维表格数据.
|
|
237
|
+
|
|
238
|
+
示例:
|
|
239
|
+
>>> data-ops-cli data_table data dt_code_001
|
|
240
|
+
"""
|
|
241
|
+
pp = PageParam(start=page, limit=size)
|
|
242
|
+
qp = SqlQueryParam.model_validate_json(query_param) if query_param else SqlQueryParam()
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
data = (
|
|
246
|
+
service_get_data_table_data_by_name(arg1, name, pp, qp)
|
|
247
|
+
if name
|
|
248
|
+
else service_get_data_table_data_by_code(arg1, pp, qp)
|
|
249
|
+
)
|
|
250
|
+
except DataOpsError as e:
|
|
251
|
+
typer.echo(f"错误: {e.message}")
|
|
252
|
+
raise typer.Exit(code=1) from None
|
|
253
|
+
if not data:
|
|
254
|
+
print("未查询到数据")
|
|
255
|
+
return
|
|
256
|
+
print(render_table(data))
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
if __name__ == "__main__":
|
|
260
|
+
list_datatable()
|
|
@@ -5,9 +5,7 @@ from rich.console import Console
|
|
|
5
5
|
from rich.table import Table
|
|
6
6
|
|
|
7
7
|
from dataops_cli.exceptions import DataOpsError
|
|
8
|
-
from dataops_cli.
|
|
9
|
-
from dataops_cli.schems.enums import MetaType
|
|
10
|
-
from dataops_cli.sdk.meta import release_list_get_by_code, release_list_list
|
|
8
|
+
from dataops_cli.sdk import service_get_dataset_detail_by_code, service_list_datasets
|
|
11
9
|
from dataops_cli.utils import filter_nodes_by_pattern
|
|
12
10
|
|
|
13
11
|
dataset_typer = typer.Typer(name="dataset", help="Manage dataset.", no_args_is_help=True)
|
|
@@ -29,7 +27,7 @@ def list_dataset(
|
|
|
29
27
|
>>> data-ops-cli dataset list "HHI.*"
|
|
30
28
|
"""
|
|
31
29
|
try:
|
|
32
|
-
group_nodes =
|
|
30
|
+
group_nodes = service_list_datasets()
|
|
33
31
|
except DataOpsError as e:
|
|
34
32
|
typer.echo(f"错误: {e.message}")
|
|
35
33
|
raise typer.Exit(code=1) from None
|
|
@@ -62,7 +60,7 @@ def get_dataset_detail_by_code(
|
|
|
62
60
|
>>> data-ops-cli dataset detail my_dataset_code --full
|
|
63
61
|
"""
|
|
64
62
|
try:
|
|
65
|
-
meta =
|
|
63
|
+
meta = service_get_dataset_detail_by_code(code)
|
|
66
64
|
except DataOpsError as e:
|
|
67
65
|
typer.echo(f"错误: {e.message}")
|
|
68
66
|
raise typer.Exit(code=1) from None
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""数据源管理模块.
|
|
2
|
+
|
|
3
|
+
提供数据源、表、数据集的查询和管理功能。
|
|
4
|
+
|
|
5
|
+
- 服务层函数 (service_*):纯 Python 接口,返回原始数据,可供 agent 自由组合
|
|
6
|
+
- CLI 命令:基于服务层的薄包装,负责格式化输出
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Annotated
|
|
12
|
+
|
|
13
|
+
import typer
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
from rich.table import Table
|
|
16
|
+
|
|
17
|
+
from dataops_cli.exceptions import DataOpsError
|
|
18
|
+
from dataops_cli.schems import (
|
|
19
|
+
DataDataSource,
|
|
20
|
+
InnerMetaType,
|
|
21
|
+
MetaCreate,
|
|
22
|
+
MetaUpdate,
|
|
23
|
+
)
|
|
24
|
+
from dataops_cli.schems.enums import DataSourceType
|
|
25
|
+
from dataops_cli.sdk.datasource import get_application_datasource_config, get_datasource
|
|
26
|
+
from dataops_cli.sdk.meta import (
|
|
27
|
+
release_list_create,
|
|
28
|
+
release_list_get_by_name,
|
|
29
|
+
release_list_list,
|
|
30
|
+
release_list_update,
|
|
31
|
+
)
|
|
32
|
+
from dataops_cli.sdk.table import (
|
|
33
|
+
service_get_table_data_by_name,
|
|
34
|
+
)
|
|
35
|
+
from dataops_cli.session import get_application
|
|
36
|
+
from dataops_cli.utils import filter_nodes_by_pattern
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# CLI 命令(薄包装,负责输出渲染)
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
datasource_typer = typer.Typer(name="datasource", help="Manage data sources.", no_args_is_help=True)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@datasource_typer.command("config")
|
|
46
|
+
def get_datasource_config() -> None:
|
|
47
|
+
"""显示当前应用的数据库源配置信息.
|
|
48
|
+
|
|
49
|
+
展示应用关联的三个核心数据源:
|
|
50
|
+
- 主数据源: 默认读写数据源
|
|
51
|
+
- 隐患读数据源: 隐患业务只读数据源
|
|
52
|
+
- 隐患写数据源: 隐患业务只写数据源
|
|
53
|
+
|
|
54
|
+
示例:
|
|
55
|
+
# 显示当前应用的数据源配置
|
|
56
|
+
>>> data-ops-cli datasource config
|
|
57
|
+
"""
|
|
58
|
+
application = get_application()
|
|
59
|
+
datasource_config = get_application_datasource_config(application.CUID)
|
|
60
|
+
table = Table(
|
|
61
|
+
"name", "功能", title=f"应用数据源配置\n{application.LABEL_CN}({application.CODE})"
|
|
62
|
+
)
|
|
63
|
+
table.add_row(datasource_config.main.name, "主数据源")
|
|
64
|
+
table.add_row(datasource_config.hazard_read.name, "隐患读数据源")
|
|
65
|
+
table.add_row(datasource_config.hazard_write.name, "隐患写数据源")
|
|
66
|
+
console = Console(no_color=True)
|
|
67
|
+
console.print(table)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@datasource_typer.command("list")
|
|
71
|
+
def list_datasource(
|
|
72
|
+
query: Annotated[str | None, typer.Option("-q", "--query", help="正则表达式查询")] = None,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""列出所有已注册的数据源.
|
|
75
|
+
|
|
76
|
+
从元数据服务获取所有数据源节点,支持通过正则表达式过滤数据源名称。
|
|
77
|
+
|
|
78
|
+
参数:
|
|
79
|
+
query: 可选的正则表达式,用于过滤返回的数据源列表
|
|
80
|
+
|
|
81
|
+
示例:
|
|
82
|
+
# 列出所有数据源
|
|
83
|
+
>>> data-ops-cli datasource list
|
|
84
|
+
|
|
85
|
+
# 通过正则过滤数据源
|
|
86
|
+
>>> data-ops-cli datasource list -q "doris.*"
|
|
87
|
+
|
|
88
|
+
# 完整参数名形式
|
|
89
|
+
>>> data-ops-cli datasource list --query zhwg
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
group_nodes = release_list_list(InnerMetaType.DATASOURCE.value)
|
|
93
|
+
except DataOpsError as e:
|
|
94
|
+
typer.echo(f"错误: {e.message}")
|
|
95
|
+
raise typer.Exit(code=1) from None
|
|
96
|
+
|
|
97
|
+
if query:
|
|
98
|
+
group_nodes = filter_nodes_by_pattern(group_nodes, query, include_digest=True)
|
|
99
|
+
|
|
100
|
+
table = Table("name", "alias", "remark")
|
|
101
|
+
console = Console(no_color=True)
|
|
102
|
+
for column in group_nodes:
|
|
103
|
+
table.add_row(column.NAME, column.TITLE, column.REMARK or "")
|
|
104
|
+
console.print(table)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@datasource_typer.command("detail")
|
|
108
|
+
def get_datasource_detail(
|
|
109
|
+
name: Annotated[str, typer.Argument(help="数据源名称")],
|
|
110
|
+
) -> None:
|
|
111
|
+
"""获取指定数据源的详细信息.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
name: 数据源名称
|
|
115
|
+
"""
|
|
116
|
+
meta = release_list_get_by_name(InnerMetaType.DATASOURCE.value, name, _class=DataDataSource)
|
|
117
|
+
table = Table("field", "value", show_header=False)
|
|
118
|
+
table.add_row("name", meta.name)
|
|
119
|
+
table.add_row("title", meta.title)
|
|
120
|
+
table.add_row("分组", meta.groupCode)
|
|
121
|
+
table.add_row("数据库类型", meta.data.type.value)
|
|
122
|
+
table.add_row("地址", meta.data.ip)
|
|
123
|
+
table.add_row("端口", str(meta.data.port))
|
|
124
|
+
table.add_row("数据库账户", meta.data.username)
|
|
125
|
+
table.add_row("说明", meta.remark or "")
|
|
126
|
+
console = Console(no_color=True)
|
|
127
|
+
console.print(table)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@datasource_typer.command("create", no_args_is_help=True)
|
|
131
|
+
def create_datasource(
|
|
132
|
+
name: Annotated[str, typer.Option(help="数据源名称")],
|
|
133
|
+
title: Annotated[str, typer.Option(help="数据源中文名称")],
|
|
134
|
+
type: Annotated[DataSourceType, typer.Option("-t", "--type", help="数据源类型")],
|
|
135
|
+
ip: Annotated[str, typer.Option(help="数据源 IP 地址")],
|
|
136
|
+
port: Annotated[int, typer.Option("-p", "--port", help="数据源端口号")],
|
|
137
|
+
username: Annotated[str, typer.Option("-u", "--username", help="数据源用户名")],
|
|
138
|
+
password: Annotated[str, typer.Option(help="数据源密码", hide_input=True)],
|
|
139
|
+
database: Annotated[str, typer.Option("-db", "--database", help="数据库名称")],
|
|
140
|
+
remark: Annotated[str | None, typer.Option(help="数据源备注")] = None,
|
|
141
|
+
) -> None:
|
|
142
|
+
"""创建新的数据源."""
|
|
143
|
+
meta_create = MetaCreate[DataDataSource](
|
|
144
|
+
type="datasource",
|
|
145
|
+
subType=f"database__{type.value}",
|
|
146
|
+
name=name,
|
|
147
|
+
title=title,
|
|
148
|
+
remark=remark,
|
|
149
|
+
groupCode="inner",
|
|
150
|
+
data=DataDataSource(
|
|
151
|
+
type=type, ip=ip, port=port, username=username, password=password, database=database
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
result = release_list_create(meta_create, _class=DataDataSource)
|
|
155
|
+
typer.echo(f"数据源 {result.name} 创建成功")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@datasource_typer.command("update", no_args_is_help=True)
|
|
159
|
+
def update_datasource(
|
|
160
|
+
name: Annotated[str, typer.Argument(help="数据源名称")],
|
|
161
|
+
title: Annotated[str | None, typer.Option(help="数据源中文名称")] = None,
|
|
162
|
+
type: Annotated[DataSourceType | None, typer.Option("-t", "--type", help="数据源类型")] = None,
|
|
163
|
+
ip: Annotated[str | None, typer.Option(help="数据源 IP 地址")] = None,
|
|
164
|
+
port: Annotated[int | None, typer.Option("-p", "--port", help="数据源端口号")] = None,
|
|
165
|
+
username: Annotated[str | None, typer.Option("-u", "--username", help="数据源用户名")] = None,
|
|
166
|
+
password: Annotated[str | None, typer.Option(help="数据源密码", hide_input=True)] = None,
|
|
167
|
+
database: Annotated[str | None, typer.Option("-db", "--database", help="数据库名称")] = None,
|
|
168
|
+
remark: Annotated[str | None, typer.Option(help="数据源备注")] = None,
|
|
169
|
+
) -> None:
|
|
170
|
+
"""更新已有的数据源."""
|
|
171
|
+
meta = release_list_get_by_name(InnerMetaType.DATASOURCE.value, name, _class=DataDataSource)
|
|
172
|
+
if type:
|
|
173
|
+
meta.data.type = type
|
|
174
|
+
meta.subType = f"database__{type.value}"
|
|
175
|
+
if title:
|
|
176
|
+
meta.title = title
|
|
177
|
+
if ip:
|
|
178
|
+
meta.data.ip = ip
|
|
179
|
+
if port:
|
|
180
|
+
meta.data.port = port
|
|
181
|
+
if username:
|
|
182
|
+
meta.data.username = username
|
|
183
|
+
if password:
|
|
184
|
+
meta.data.password = password
|
|
185
|
+
if database:
|
|
186
|
+
meta.data.database = database
|
|
187
|
+
if remark:
|
|
188
|
+
meta.remark = remark
|
|
189
|
+
|
|
190
|
+
result = release_list_update(meta, _class=DataDataSource)
|
|
191
|
+
typer.echo(f"数据源 {result.name} 更新成功")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
print(DataDataSource.model_json_schema())
|
|
196
|
+
# list_datasource("doris")
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from typing import Annotated, Optional
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
|
|
7
|
+
from dataops_cli.exceptions import DataOpsError
|
|
8
|
+
from dataops_cli.sdk.dict_ import (
|
|
9
|
+
service_get_dict_detail_by_code,
|
|
10
|
+
service_get_dict_detail_by_name,
|
|
11
|
+
service_list_dicts,
|
|
12
|
+
)
|
|
13
|
+
from dataops_cli.utils import filter_nodes_by_pattern
|
|
14
|
+
|
|
15
|
+
dict_typer = typer.Typer(name="dict", help="Manage dict.", no_args_is_help=True)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dict_typer.command("list")
|
|
19
|
+
def list_dict(
|
|
20
|
+
datasource: Annotated[str, typer.Argument(help="数据源")],
|
|
21
|
+
query: Annotated[str | None, typer.Argument(help="字典名称关键字")] = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
"""列出数据源中的字典列表.
|
|
24
|
+
|
|
25
|
+
支持通过关键字过滤字典名称,并支持正则表达式匹配。
|
|
26
|
+
|
|
27
|
+
示例:
|
|
28
|
+
# 列出指定数据源的所有字典
|
|
29
|
+
>>> data-ops-cli dict list <datasource>
|
|
30
|
+
|
|
31
|
+
# 列出数据源中名称包含 "user" 的字典
|
|
32
|
+
>>> data-ops-cli dict list <datasource> user
|
|
33
|
+
|
|
34
|
+
# 使用正则过滤字典名
|
|
35
|
+
>>> data-ops-cli dict list <datasource> "^user_.*"
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
group_nodes = service_list_dicts(datasource)
|
|
39
|
+
except DataOpsError as e:
|
|
40
|
+
typer.echo(f"错误: {e.message}")
|
|
41
|
+
raise typer.Exit(code=1) from None
|
|
42
|
+
|
|
43
|
+
if not group_nodes:
|
|
44
|
+
print("未查询到匹配字典")
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
if query:
|
|
48
|
+
group_nodes = filter_nodes_by_pattern(group_nodes, query)
|
|
49
|
+
|
|
50
|
+
table = Table("code", "name", "alias", "remark", title="dict list")
|
|
51
|
+
console = Console(no_color=True)
|
|
52
|
+
for column in group_nodes:
|
|
53
|
+
table.add_row(column.CODE, column.NAME, column.TITLE)
|
|
54
|
+
console.print(table)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dict_typer.command("detail", no_args_is_help=True)
|
|
58
|
+
def get_dict_detail(
|
|
59
|
+
arg1: Annotated[str, typer.Argument(help="编码或数据源")],
|
|
60
|
+
name: Annotated[Optional[str], typer.Argument(help="字典名称")] = None,
|
|
61
|
+
full: Annotated[bool, typer.Option("--full", help="是否显示完整信息")] = False,
|
|
62
|
+
) -> None:
|
|
63
|
+
"""获取字典详情信息.
|
|
64
|
+
|
|
65
|
+
支持两种查询方式:
|
|
66
|
+
1. 通过字典编码(CODE)直接查询
|
|
67
|
+
2. 通过数据源名称和字典名称组合查询
|
|
68
|
+
|
|
69
|
+
默认只显示字典选项的基本信息(label/value)。
|
|
70
|
+
使用 --full 选项可显示完整信息。
|
|
71
|
+
|
|
72
|
+
示例:
|
|
73
|
+
# 通过字典编码查询字典结构
|
|
74
|
+
>>> data-ops-cli dict detail dict_code_123
|
|
75
|
+
|
|
76
|
+
# 通过数据源和字典名查询
|
|
77
|
+
>>> data-ops-cli dict detail <datasource> my_dict
|
|
78
|
+
|
|
79
|
+
# 显示完整的字典信息
|
|
80
|
+
>>> data-ops-cli dict detail <datasource> my_dict --full
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
meta = (
|
|
84
|
+
service_get_dict_detail_by_name(arg1, name)
|
|
85
|
+
if name
|
|
86
|
+
else service_get_dict_detail_by_code(arg1)
|
|
87
|
+
)
|
|
88
|
+
except DataOpsError as e:
|
|
89
|
+
typer.echo(f"错误: {e.message}")
|
|
90
|
+
raise typer.Exit(code=1) from None
|
|
91
|
+
|
|
92
|
+
console = Console(no_color=True)
|
|
93
|
+
table = Table("value", "label", title=f"{arg1} {meta.name} dict")
|
|
94
|
+
for option in meta.data.options:
|
|
95
|
+
table.add_row(str(option.value), option.label)
|
|
96
|
+
console.print(table)
|