hap-cli 0.5.0__py3-none-any.whl
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.
- hap_cli/README.md +194 -0
- hap_cli/README_CN.md +601 -0
- hap_cli/__init__.py +3 -0
- hap_cli/commands/__init__.py +1 -0
- hap_cli/commands/ai_cmd.py +224 -0
- hap_cli/commands/app_cmd.py +308 -0
- hap_cli/commands/calendar_cmd.py +138 -0
- hap_cli/commands/chat_cmd.py +101 -0
- hap_cli/commands/config_cmd.py +169 -0
- hap_cli/commands/contact_cmd.py +125 -0
- hap_cli/commands/department_cmd.py +168 -0
- hap_cli/commands/group_cmd.py +128 -0
- hap_cli/commands/instance_cmd.py +310 -0
- hap_cli/commands/node_cmd.py +538 -0
- hap_cli/commands/optionset_cmd.py +99 -0
- hap_cli/commands/page_cmd.py +102 -0
- hap_cli/commands/plugin_cmd.py +133 -0
- hap_cli/commands/post_cmd.py +155 -0
- hap_cli/commands/record_cmd.py +228 -0
- hap_cli/commands/role_cmd.py +221 -0
- hap_cli/commands/workflow_cmd.py +284 -0
- hap_cli/commands/worksheet_cmd.py +342 -0
- hap_cli/context.py +43 -0
- hap_cli/core/__init__.py +1 -0
- hap_cli/core/ai.py +133 -0
- hap_cli/core/app.py +307 -0
- hap_cli/core/auth.py +219 -0
- hap_cli/core/calendar_mod.py +114 -0
- hap_cli/core/chat.py +73 -0
- hap_cli/core/contact.py +85 -0
- hap_cli/core/department.py +131 -0
- hap_cli/core/flow_node.py +1001 -0
- hap_cli/core/group.py +99 -0
- hap_cli/core/instance.py +572 -0
- hap_cli/core/optionset.py +112 -0
- hap_cli/core/page.py +138 -0
- hap_cli/core/plugin.py +87 -0
- hap_cli/core/post.py +118 -0
- hap_cli/core/record.py +268 -0
- hap_cli/core/role.py +227 -0
- hap_cli/core/session.py +348 -0
- hap_cli/core/workflow.py +556 -0
- hap_cli/core/worksheet.py +403 -0
- hap_cli/hap_cli.py +105 -0
- hap_cli/skills/SKILL.md +383 -0
- hap_cli/skills/__init__.py +0 -0
- hap_cli/tests/__init__.py +1 -0
- hap_cli/tests/test_core.py +1824 -0
- hap_cli/tests/test_full_e2e.py +136 -0
- hap_cli/tests/test_integration.py +805 -0
- hap_cli/utils/__init__.py +1 -0
- hap_cli/utils/formatting.py +111 -0
- hap_cli/utils/options.py +10 -0
- hap_cli-0.5.0.dist-info/METADATA +223 -0
- hap_cli-0.5.0.dist-info/RECORD +58 -0
- hap_cli-0.5.0.dist-info/WHEEL +5 -0
- hap_cli-0.5.0.dist-info/entry_points.txt +2 -0
- hap_cli-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""CLI commands for custom page management."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from hap_cli.context import pass_context
|
|
6
|
+
from hap_cli.core import page as page_mod
|
|
7
|
+
from hap_cli.utils.formatting import output_json, output_table
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.group()
|
|
11
|
+
def page():
|
|
12
|
+
"""Custom page management."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@page.command("copy")
|
|
17
|
+
@click.argument("app_id")
|
|
18
|
+
@click.argument("page_id")
|
|
19
|
+
@click.option("--name", "-n", required=True, help="Name for the copied page")
|
|
20
|
+
@pass_context
|
|
21
|
+
def page_copy(ctx, app_id, page_id, name):
|
|
22
|
+
"""Copy a custom page."""
|
|
23
|
+
try:
|
|
24
|
+
session = ctx.get_session()
|
|
25
|
+
data = page_mod.copy_page(session, app_id, page_id, name)
|
|
26
|
+
ctx.output(data, lambda d: click.echo(f"Page copied: {d}"))
|
|
27
|
+
except Exception as e:
|
|
28
|
+
ctx.handle_error(e)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@page.command("authorize")
|
|
32
|
+
@click.argument("app_id")
|
|
33
|
+
@click.argument("page_id")
|
|
34
|
+
@click.option("--role-id", "-r", required=True, help="Role ID to authorize")
|
|
35
|
+
@pass_context
|
|
36
|
+
def page_authorize(ctx, app_id, page_id, role_id):
|
|
37
|
+
"""Add an authorization to a custom page."""
|
|
38
|
+
try:
|
|
39
|
+
session = ctx.get_session()
|
|
40
|
+
data = page_mod.add_authorize(session, app_id, page_id, role_id)
|
|
41
|
+
ctx.output(data, lambda d: click.echo(f"Authorization added: {d}"))
|
|
42
|
+
except Exception as e:
|
|
43
|
+
ctx.handle_error(e)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@page.command("permissions")
|
|
47
|
+
@click.argument("app_id")
|
|
48
|
+
@click.argument("page_id")
|
|
49
|
+
@pass_context
|
|
50
|
+
def page_permissions(ctx, app_id, page_id):
|
|
51
|
+
"""Get authorizations for a custom page."""
|
|
52
|
+
try:
|
|
53
|
+
session = ctx.get_session()
|
|
54
|
+
data = page_mod.get_authorizes(session, app_id, page_id)
|
|
55
|
+
ctx.output(data, lambda d: output_json(d))
|
|
56
|
+
except Exception as e:
|
|
57
|
+
ctx.handle_error(e)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@page.command("edit-permission")
|
|
61
|
+
@click.argument("app_id")
|
|
62
|
+
@click.argument("page_id")
|
|
63
|
+
@click.argument("authorize_id")
|
|
64
|
+
@click.option("--status", "-s", required=True, type=int, help="Status value")
|
|
65
|
+
@pass_context
|
|
66
|
+
def page_edit_permission(ctx, app_id, page_id, authorize_id, status):
|
|
67
|
+
"""Edit the status of a page authorization."""
|
|
68
|
+
try:
|
|
69
|
+
session = ctx.get_session()
|
|
70
|
+
data = page_mod.edit_authorize_status(session, app_id, page_id, authorize_id, status)
|
|
71
|
+
ctx.output(data, lambda d: click.echo("Permission updated."))
|
|
72
|
+
except Exception as e:
|
|
73
|
+
ctx.handle_error(e)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@page.command("delete-permission")
|
|
77
|
+
@click.argument("app_id")
|
|
78
|
+
@click.argument("page_id")
|
|
79
|
+
@click.argument("authorize_id")
|
|
80
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
81
|
+
@pass_context
|
|
82
|
+
def page_delete_permission(ctx, app_id, page_id, authorize_id, yes):
|
|
83
|
+
"""Delete a page authorization."""
|
|
84
|
+
try:
|
|
85
|
+
if not yes:
|
|
86
|
+
click.confirm(f"Delete authorization {authorize_id}?", abort=True)
|
|
87
|
+
session = ctx.get_session()
|
|
88
|
+
data = page_mod.delete_authorize(session, app_id, page_id, authorize_id)
|
|
89
|
+
ctx.output(data, lambda d: click.echo("Permission deleted."))
|
|
90
|
+
except Exception as e:
|
|
91
|
+
ctx.handle_error(e)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@page.command("component-types")
|
|
95
|
+
@pass_context
|
|
96
|
+
def page_component_types(ctx):
|
|
97
|
+
"""Show available custom page component types."""
|
|
98
|
+
rows = [{"type": k, "description": v} for k, v in page_mod.PAGE_COMPONENT_TYPES.items()]
|
|
99
|
+
ctx.output(
|
|
100
|
+
rows,
|
|
101
|
+
lambda d: output_table(d, ["type", "description"], ["Component Type", "Description"]),
|
|
102
|
+
)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""CLI commands for plugin management."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from hap_cli.context import pass_context
|
|
6
|
+
from hap_cli.core import plugin as plugin_mod
|
|
7
|
+
from hap_cli.utils.formatting import output_json, output_kv
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.group()
|
|
11
|
+
def plugin():
|
|
12
|
+
"""Plugin management."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@plugin.command("list")
|
|
17
|
+
@click.option("--project-id", "-p", default="", help="Project/org ID")
|
|
18
|
+
@click.option("--page-size", "-n", default=20, help="Items per page")
|
|
19
|
+
@click.option("--page", default=1, help="Page number")
|
|
20
|
+
@pass_context
|
|
21
|
+
def plugin_list(ctx, project_id, page_size, page):
|
|
22
|
+
"""List plugins."""
|
|
23
|
+
try:
|
|
24
|
+
session = ctx.get_session()
|
|
25
|
+
pid = project_id or session.default_project_id
|
|
26
|
+
if not pid:
|
|
27
|
+
raise click.UsageError("Project ID required.")
|
|
28
|
+
data = plugin_mod.get_plugins(session, pid, page_index=page, page_size=page_size)
|
|
29
|
+
ctx.output(data, lambda d: output_json(d))
|
|
30
|
+
except Exception as e:
|
|
31
|
+
ctx.handle_error(e)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@plugin.command("get")
|
|
35
|
+
@click.argument("plugin_id")
|
|
36
|
+
@pass_context
|
|
37
|
+
def plugin_get(ctx, plugin_id):
|
|
38
|
+
"""Get plugin detail."""
|
|
39
|
+
try:
|
|
40
|
+
session = ctx.get_session()
|
|
41
|
+
data = plugin_mod.get_plugin_detail(session, plugin_id)
|
|
42
|
+
ctx.output(data, lambda d: output_kv(d))
|
|
43
|
+
except Exception as e:
|
|
44
|
+
ctx.handle_error(e)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@plugin.command("create")
|
|
48
|
+
@click.option("--project-id", "-p", default="", help="Project/org ID")
|
|
49
|
+
@click.option("--name", "-n", required=True, help="Plugin name")
|
|
50
|
+
@click.option("--desc", "-d", default="", help="Description")
|
|
51
|
+
@pass_context
|
|
52
|
+
def plugin_create(ctx, project_id, name, desc):
|
|
53
|
+
"""Create a new plugin."""
|
|
54
|
+
try:
|
|
55
|
+
session = ctx.get_session()
|
|
56
|
+
pid = project_id or session.default_project_id
|
|
57
|
+
if not pid:
|
|
58
|
+
raise click.UsageError("Project ID required.")
|
|
59
|
+
data = plugin_mod.create_plugin(session, pid, name, description=desc)
|
|
60
|
+
ctx.output(data, lambda d: click.echo(f"Plugin created: {d}"))
|
|
61
|
+
except Exception as e:
|
|
62
|
+
ctx.handle_error(e)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@plugin.command("edit")
|
|
66
|
+
@click.argument("plugin_id")
|
|
67
|
+
@click.option("--name", "-n", default="", help="New name")
|
|
68
|
+
@click.option("--desc", "-d", default="", help="New description")
|
|
69
|
+
@pass_context
|
|
70
|
+
def plugin_edit(ctx, plugin_id, name, desc):
|
|
71
|
+
"""Edit plugin name and/or description."""
|
|
72
|
+
try:
|
|
73
|
+
session = ctx.get_session()
|
|
74
|
+
data = plugin_mod.edit_plugin(session, plugin_id, name=name, description=desc)
|
|
75
|
+
ctx.output(data, lambda d: click.echo("Plugin updated."))
|
|
76
|
+
except Exception as e:
|
|
77
|
+
ctx.handle_error(e)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@plugin.command("delete")
|
|
81
|
+
@click.argument("plugin_id")
|
|
82
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
83
|
+
@pass_context
|
|
84
|
+
def plugin_delete(ctx, plugin_id, yes):
|
|
85
|
+
"""Delete a plugin."""
|
|
86
|
+
try:
|
|
87
|
+
if not yes:
|
|
88
|
+
click.confirm(f"Delete plugin {plugin_id}?", abort=True)
|
|
89
|
+
session = ctx.get_session()
|
|
90
|
+
data = plugin_mod.remove_plugin(session, plugin_id)
|
|
91
|
+
ctx.output(data, lambda d: click.echo("Plugin deleted."))
|
|
92
|
+
except Exception as e:
|
|
93
|
+
ctx.handle_error(e)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@plugin.command("release")
|
|
97
|
+
@click.argument("plugin_id")
|
|
98
|
+
@pass_context
|
|
99
|
+
def plugin_release(ctx, plugin_id):
|
|
100
|
+
"""Release a new version of a plugin."""
|
|
101
|
+
try:
|
|
102
|
+
session = ctx.get_session()
|
|
103
|
+
data = plugin_mod.release_plugin(session, plugin_id)
|
|
104
|
+
ctx.output(data, lambda d: click.echo(f"Plugin released: {d}"))
|
|
105
|
+
except Exception as e:
|
|
106
|
+
ctx.handle_error(e)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@plugin.command("rollback")
|
|
110
|
+
@click.argument("plugin_id")
|
|
111
|
+
@click.argument("version_id")
|
|
112
|
+
@pass_context
|
|
113
|
+
def plugin_rollback(ctx, plugin_id, version_id):
|
|
114
|
+
"""Rollback a plugin to a specific version."""
|
|
115
|
+
try:
|
|
116
|
+
session = ctx.get_session()
|
|
117
|
+
data = plugin_mod.rollback_plugin(session, plugin_id, version_id)
|
|
118
|
+
ctx.output(data, lambda d: click.echo(f"Plugin rolled back to {version_id}."))
|
|
119
|
+
except Exception as e:
|
|
120
|
+
ctx.handle_error(e)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@plugin.command("history")
|
|
124
|
+
@click.argument("plugin_id")
|
|
125
|
+
@pass_context
|
|
126
|
+
def plugin_history(ctx, plugin_id):
|
|
127
|
+
"""Get plugin release history."""
|
|
128
|
+
try:
|
|
129
|
+
session = ctx.get_session()
|
|
130
|
+
data = plugin_mod.get_release_history(session, plugin_id)
|
|
131
|
+
ctx.output(data, lambda d: output_json(d))
|
|
132
|
+
except Exception as e:
|
|
133
|
+
ctx.handle_error(e)
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""CLI commands for feed/post management."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from hap_cli.context import pass_context
|
|
6
|
+
from hap_cli.core import post as post_mod
|
|
7
|
+
from hap_cli.utils.formatting import output_json, output_kv
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.group()
|
|
11
|
+
def post():
|
|
12
|
+
"""Feed and post management."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@post.command("list")
|
|
17
|
+
@click.option("--project-id", "-p", default="", help="Project/org ID")
|
|
18
|
+
@click.option("--type", "post_type", default=0, type=int, help="Post type")
|
|
19
|
+
@click.option("--page-size", "-n", default=20, help="Items per page")
|
|
20
|
+
@click.option("--page", default=1, help="Page number")
|
|
21
|
+
@pass_context
|
|
22
|
+
def post_list(ctx, project_id, post_type, page_size, page):
|
|
23
|
+
"""List feed posts."""
|
|
24
|
+
try:
|
|
25
|
+
session = ctx.get_session()
|
|
26
|
+
pid = project_id or session.default_project_id
|
|
27
|
+
if not pid:
|
|
28
|
+
raise click.UsageError("Project ID required.")
|
|
29
|
+
data = post_mod.get_post_list(session, pid, post_type=post_type, page_index=page, page_size=page_size)
|
|
30
|
+
ctx.output(data, lambda d: output_json(d))
|
|
31
|
+
except Exception as e:
|
|
32
|
+
ctx.handle_error(e)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@post.command("get")
|
|
36
|
+
@click.argument("post_id")
|
|
37
|
+
@pass_context
|
|
38
|
+
def post_get(ctx, post_id):
|
|
39
|
+
"""Get post detail."""
|
|
40
|
+
try:
|
|
41
|
+
session = ctx.get_session()
|
|
42
|
+
data = post_mod.get_post_detail(session, post_id)
|
|
43
|
+
ctx.output(data, lambda d: output_kv(d))
|
|
44
|
+
except Exception as e:
|
|
45
|
+
ctx.handle_error(e)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@post.command("create")
|
|
49
|
+
@click.option("--project-id", "-p", default="", help="Project/org ID")
|
|
50
|
+
@click.option("--type", "post_type", default=0, type=int, help="Post type")
|
|
51
|
+
@click.option("--message", "-m", required=True, help="Post content")
|
|
52
|
+
@click.option("--scope", default=0, type=int, help="Visibility scope")
|
|
53
|
+
@pass_context
|
|
54
|
+
def post_create(ctx, project_id, post_type, message, scope):
|
|
55
|
+
"""Create a new post."""
|
|
56
|
+
try:
|
|
57
|
+
session = ctx.get_session()
|
|
58
|
+
pid = project_id or session.default_project_id
|
|
59
|
+
if not pid:
|
|
60
|
+
raise click.UsageError("Project ID required.")
|
|
61
|
+
data = post_mod.add_post(session, pid, post_type, message, scope=scope)
|
|
62
|
+
ctx.output(data, lambda d: click.echo(f"Post created: {d}"))
|
|
63
|
+
except Exception as e:
|
|
64
|
+
ctx.handle_error(e)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@post.command("update")
|
|
68
|
+
@click.argument("post_id")
|
|
69
|
+
@click.option("--message", "-m", required=True, help="New content")
|
|
70
|
+
@pass_context
|
|
71
|
+
def post_update(ctx, post_id, message):
|
|
72
|
+
"""Update a post."""
|
|
73
|
+
try:
|
|
74
|
+
session = ctx.get_session()
|
|
75
|
+
data = post_mod.edit_post(session, post_id, message)
|
|
76
|
+
ctx.output(data, lambda d: click.echo("Post updated."))
|
|
77
|
+
except Exception as e:
|
|
78
|
+
ctx.handle_error(e)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@post.command("delete")
|
|
82
|
+
@click.argument("post_id")
|
|
83
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
84
|
+
@pass_context
|
|
85
|
+
def post_delete(ctx, post_id, yes):
|
|
86
|
+
"""Delete a post."""
|
|
87
|
+
try:
|
|
88
|
+
if not yes:
|
|
89
|
+
click.confirm(f"Delete post {post_id}?", abort=True)
|
|
90
|
+
session = ctx.get_session()
|
|
91
|
+
data = post_mod.remove_post(session, post_id)
|
|
92
|
+
ctx.output(data, lambda d: click.echo("Post deleted."))
|
|
93
|
+
except Exception as e:
|
|
94
|
+
ctx.handle_error(e)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@post.command("comment")
|
|
98
|
+
@click.argument("post_id")
|
|
99
|
+
@click.option("--message", "-m", required=True, help="Comment content")
|
|
100
|
+
@click.option("--reply-id", default="", help="Reply to comment ID")
|
|
101
|
+
@pass_context
|
|
102
|
+
def post_comment(ctx, post_id, message, reply_id):
|
|
103
|
+
"""Add a comment to a post."""
|
|
104
|
+
try:
|
|
105
|
+
session = ctx.get_session()
|
|
106
|
+
data = post_mod.add_comment(session, post_id, message, reply_id=reply_id)
|
|
107
|
+
ctx.output(data, lambda d: click.echo("Comment added."))
|
|
108
|
+
except Exception as e:
|
|
109
|
+
ctx.handle_error(e)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@post.command("comments")
|
|
113
|
+
@click.argument("post_id")
|
|
114
|
+
@click.option("--page-size", "-n", default=20, help="Items per page")
|
|
115
|
+
@click.option("--page", default=1, help="Page number")
|
|
116
|
+
@pass_context
|
|
117
|
+
def post_comments(ctx, post_id, page_size, page):
|
|
118
|
+
"""List comments for a post."""
|
|
119
|
+
try:
|
|
120
|
+
session = ctx.get_session()
|
|
121
|
+
data = post_mod.get_comments(session, post_id, page_index=page, page_size=page_size)
|
|
122
|
+
ctx.output(data, lambda d: output_json(d))
|
|
123
|
+
except Exception as e:
|
|
124
|
+
ctx.handle_error(e)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@post.command("like")
|
|
128
|
+
@click.argument("post_id")
|
|
129
|
+
@pass_context
|
|
130
|
+
def post_like(ctx, post_id):
|
|
131
|
+
"""Like or unlike a post."""
|
|
132
|
+
try:
|
|
133
|
+
session = ctx.get_session()
|
|
134
|
+
data = post_mod.like_post(session, post_id)
|
|
135
|
+
ctx.output(data, lambda d: click.echo("Like toggled."))
|
|
136
|
+
except Exception as e:
|
|
137
|
+
ctx.handle_error(e)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@post.command("top")
|
|
141
|
+
@click.argument("post_id")
|
|
142
|
+
@click.option("--remove", is_flag=True, help="Remove from top")
|
|
143
|
+
@pass_context
|
|
144
|
+
def post_top(ctx, post_id, remove):
|
|
145
|
+
"""Pin or unpin a post to top."""
|
|
146
|
+
try:
|
|
147
|
+
session = ctx.get_session()
|
|
148
|
+
if remove:
|
|
149
|
+
data = post_mod.remove_top_post(session, post_id)
|
|
150
|
+
ctx.output(data, lambda d: click.echo("Post unpinned."))
|
|
151
|
+
else:
|
|
152
|
+
data = post_mod.add_top_post(session, post_id)
|
|
153
|
+
ctx.output(data, lambda d: click.echo("Post pinned."))
|
|
154
|
+
except Exception as e:
|
|
155
|
+
ctx.handle_error(e)
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""CLI commands for record CRUD operations."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from hap_cli.context import pass_context
|
|
6
|
+
from hap_cli.core import record as rec_mod
|
|
7
|
+
from hap_cli.utils.formatting import output_json, output_kv, format_record_row
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.group()
|
|
11
|
+
def record():
|
|
12
|
+
"""Record CRUD operations."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@record.command("list")
|
|
17
|
+
@click.argument("worksheet_id")
|
|
18
|
+
@click.option("--view-id", "-v", default="", help="View ID")
|
|
19
|
+
@click.option("--page-size", "-n", default=20, help="Records per page")
|
|
20
|
+
@click.option("--page", "-p", default=1, help="Page number")
|
|
21
|
+
@click.option("--keywords", "-k", default="", help="Search keywords")
|
|
22
|
+
@click.option("--sort", "-s", default="", help="Sort by field ID")
|
|
23
|
+
@click.option("--asc", is_flag=True, help="Sort ascending")
|
|
24
|
+
@pass_context
|
|
25
|
+
def record_list(ctx, worksheet_id, view_id, page_size, page, keywords, sort, asc):
|
|
26
|
+
"""Query records with filtering and pagination."""
|
|
27
|
+
try:
|
|
28
|
+
session = ctx.get_session()
|
|
29
|
+
result = rec_mod.get_records(
|
|
30
|
+
session, worksheet_id, view_id,
|
|
31
|
+
page_size=page_size, page_index=page,
|
|
32
|
+
keywords=keywords, sort_id=sort, is_asc=asc,
|
|
33
|
+
)
|
|
34
|
+
ctx.output(
|
|
35
|
+
result,
|
|
36
|
+
lambda d: _print_records(d),
|
|
37
|
+
)
|
|
38
|
+
except Exception as e:
|
|
39
|
+
ctx.handle_error(e)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _print_records(result):
|
|
43
|
+
"""Print records in human-readable format."""
|
|
44
|
+
records = result.get("data", [])
|
|
45
|
+
count = result.get("count", 0)
|
|
46
|
+
click.echo(f"Total: {count} records\n")
|
|
47
|
+
if not records:
|
|
48
|
+
click.echo("(no records)")
|
|
49
|
+
return
|
|
50
|
+
for i, rec in enumerate(records):
|
|
51
|
+
formatted = format_record_row(rec)
|
|
52
|
+
rid = formatted.pop("rowId", "?")
|
|
53
|
+
click.echo(f"── Record {i + 1} [{rid}] ──")
|
|
54
|
+
for k, v in formatted.items():
|
|
55
|
+
if v:
|
|
56
|
+
click.echo(f" {k}: {v}")
|
|
57
|
+
click.echo()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@record.command("get")
|
|
61
|
+
@click.argument("worksheet_id")
|
|
62
|
+
@click.argument("row_id")
|
|
63
|
+
@click.option("--view-id", "-v", default="", help="View ID")
|
|
64
|
+
@pass_context
|
|
65
|
+
def record_get(ctx, worksheet_id, row_id, view_id):
|
|
66
|
+
"""Get a single record by ID."""
|
|
67
|
+
try:
|
|
68
|
+
session = ctx.get_session()
|
|
69
|
+
data = rec_mod.get_record(session, worksheet_id, row_id, view_id)
|
|
70
|
+
ctx.output(data, lambda d: output_kv(format_record_row(d)))
|
|
71
|
+
except Exception as e:
|
|
72
|
+
ctx.handle_error(e)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@record.command("create")
|
|
76
|
+
@click.argument("worksheet_id")
|
|
77
|
+
@click.option("--field", "-f", multiple=True, help="Field value: CONTROL_ID=VALUE")
|
|
78
|
+
@click.option("--no-workflow", is_flag=True, help="Don't trigger workflows")
|
|
79
|
+
@pass_context
|
|
80
|
+
def record_create(ctx, worksheet_id, field, no_workflow):
|
|
81
|
+
"""Create a new record. Use -f CONTROL_ID=VALUE for each field."""
|
|
82
|
+
try:
|
|
83
|
+
session = ctx.get_session()
|
|
84
|
+
controls = _parse_fields(field)
|
|
85
|
+
data = rec_mod.create_record(
|
|
86
|
+
session, worksheet_id, controls,
|
|
87
|
+
trigger_workflow=not no_workflow,
|
|
88
|
+
)
|
|
89
|
+
ctx.output(data, lambda d: click.echo(f"Created: {d}"))
|
|
90
|
+
except Exception as e:
|
|
91
|
+
ctx.handle_error(e)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@record.command("update")
|
|
95
|
+
@click.argument("worksheet_id")
|
|
96
|
+
@click.argument("row_id")
|
|
97
|
+
@click.option("--field", "-f", multiple=True, help="Field value: CONTROL_ID=VALUE")
|
|
98
|
+
@click.option("--no-workflow", is_flag=True, help="Don't trigger workflows")
|
|
99
|
+
@pass_context
|
|
100
|
+
def record_update(ctx, worksheet_id, row_id, field, no_workflow):
|
|
101
|
+
"""Update a record. Use -f CONTROL_ID=VALUE for each field."""
|
|
102
|
+
try:
|
|
103
|
+
session = ctx.get_session()
|
|
104
|
+
controls = _parse_fields(field)
|
|
105
|
+
data = rec_mod.update_record(
|
|
106
|
+
session, worksheet_id, row_id, controls,
|
|
107
|
+
trigger_workflow=not no_workflow,
|
|
108
|
+
)
|
|
109
|
+
ctx.output(data, lambda d: click.echo(f"Updated: {d}"))
|
|
110
|
+
except Exception as e:
|
|
111
|
+
ctx.handle_error(e)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@record.command("delete")
|
|
115
|
+
@click.argument("worksheet_id")
|
|
116
|
+
@click.argument("row_ids", nargs=-1, required=True)
|
|
117
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
118
|
+
@pass_context
|
|
119
|
+
def record_delete(ctx, worksheet_id, row_ids, yes):
|
|
120
|
+
"""Delete records by IDs."""
|
|
121
|
+
try:
|
|
122
|
+
if not yes:
|
|
123
|
+
click.confirm(f"Delete {len(row_ids)} record(s)?", abort=True)
|
|
124
|
+
session = ctx.get_session()
|
|
125
|
+
data = rec_mod.delete_records(session, worksheet_id, list(row_ids))
|
|
126
|
+
ctx.output(data, lambda d: click.echo(f"Deleted {len(row_ids)} record(s)."))
|
|
127
|
+
except Exception as e:
|
|
128
|
+
ctx.handle_error(e)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@record.command("discussions")
|
|
132
|
+
@click.argument("worksheet_id")
|
|
133
|
+
@click.argument("row_id")
|
|
134
|
+
@click.option("--page-size", "-n", default=20, help="Discussions per page")
|
|
135
|
+
@click.option("--page", "-p", default=1, help="Page number")
|
|
136
|
+
@pass_context
|
|
137
|
+
def record_discussions(ctx, worksheet_id, row_id, page_size, page):
|
|
138
|
+
"""Get discussions for a record."""
|
|
139
|
+
try:
|
|
140
|
+
session = ctx.get_session()
|
|
141
|
+
data = rec_mod.get_discussions(
|
|
142
|
+
session, row_id, source_type=7,
|
|
143
|
+
page_index=page, page_size=page_size,
|
|
144
|
+
)
|
|
145
|
+
ctx.output(data, lambda d: output_json(d))
|
|
146
|
+
except Exception as e:
|
|
147
|
+
ctx.handle_error(e)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@record.command("add-discussion")
|
|
151
|
+
@click.argument("worksheet_id")
|
|
152
|
+
@click.argument("row_id")
|
|
153
|
+
@click.option("--message", "-m", required=True, help="Discussion message")
|
|
154
|
+
@click.option("--app-id", default="", help="Application ID")
|
|
155
|
+
@pass_context
|
|
156
|
+
def record_add_discussion(ctx, worksheet_id, row_id, message, app_id):
|
|
157
|
+
"""Add a discussion/comment to a record."""
|
|
158
|
+
try:
|
|
159
|
+
session = ctx.get_session()
|
|
160
|
+
data = rec_mod.add_discussion(
|
|
161
|
+
session, row_id, message, source_type=7, app_id=app_id,
|
|
162
|
+
)
|
|
163
|
+
ctx.output(data, lambda d: click.echo(f"Discussion added."))
|
|
164
|
+
except Exception as e:
|
|
165
|
+
ctx.handle_error(e)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@record.command("delete-discussion")
|
|
169
|
+
@click.argument("discussion_id")
|
|
170
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
|
|
171
|
+
@pass_context
|
|
172
|
+
def record_delete_discussion(ctx, discussion_id, yes):
|
|
173
|
+
"""Delete a discussion/comment."""
|
|
174
|
+
try:
|
|
175
|
+
if not yes:
|
|
176
|
+
click.confirm(f"Delete discussion {discussion_id}?", abort=True)
|
|
177
|
+
session = ctx.get_session()
|
|
178
|
+
data = rec_mod.remove_discussion(session, discussion_id, source_type=7)
|
|
179
|
+
ctx.output(data, lambda d: click.echo(f"Discussion deleted."))
|
|
180
|
+
except Exception as e:
|
|
181
|
+
ctx.handle_error(e)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@record.command("pivot")
|
|
185
|
+
@click.argument("worksheet_id")
|
|
186
|
+
@click.option("--view-id", "-v", default="", help="View ID")
|
|
187
|
+
@click.option("--app-id", default="", help="Application ID")
|
|
188
|
+
@pass_context
|
|
189
|
+
def record_pivot(ctx, worksheet_id, view_id, app_id):
|
|
190
|
+
"""Get pivot table / report data for a worksheet."""
|
|
191
|
+
try:
|
|
192
|
+
session = ctx.get_session()
|
|
193
|
+
data = rec_mod.get_pivot_data(
|
|
194
|
+
session, worksheet_id, view_id=view_id, app_id=app_id,
|
|
195
|
+
)
|
|
196
|
+
ctx.output(data, lambda d: output_json(d))
|
|
197
|
+
except Exception as e:
|
|
198
|
+
ctx.handle_error(e)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@record.command("logs")
|
|
202
|
+
@click.argument("worksheet_id")
|
|
203
|
+
@click.argument("row_id")
|
|
204
|
+
@click.option("--page-size", "-n", default=50, help="Logs per page")
|
|
205
|
+
@click.option("--page", "-p", default=1, help="Page number")
|
|
206
|
+
@pass_context
|
|
207
|
+
def record_logs(ctx, worksheet_id, row_id, page_size, page):
|
|
208
|
+
"""Get operation logs for a record."""
|
|
209
|
+
try:
|
|
210
|
+
session = ctx.get_session()
|
|
211
|
+
data = rec_mod.get_record_logs(
|
|
212
|
+
session, worksheet_id, row_id,
|
|
213
|
+
page_index=page, page_size=page_size,
|
|
214
|
+
)
|
|
215
|
+
ctx.output(data, lambda d: output_json(d))
|
|
216
|
+
except Exception as e:
|
|
217
|
+
ctx.handle_error(e)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _parse_fields(fields: tuple) -> list[dict]:
|
|
221
|
+
"""Parse -f CONTROL_ID=VALUE pairs into controls list."""
|
|
222
|
+
controls = []
|
|
223
|
+
for f in fields:
|
|
224
|
+
if "=" not in f:
|
|
225
|
+
raise click.UsageError(f"Invalid field format: {f}. Use CONTROL_ID=VALUE")
|
|
226
|
+
cid, value = f.split("=", 1)
|
|
227
|
+
controls.append({"controlId": cid.strip(), "value": value.strip()})
|
|
228
|
+
return controls
|