tamar-file-hub-client 0.0.1__py3-none-any.whl → 0.0.3__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.
- file_hub_client/__init__.py +39 -0
- file_hub_client/client.py +43 -6
- file_hub_client/rpc/async_client.py +91 -11
- file_hub_client/rpc/gen/taple_service_pb2.py +225 -0
- file_hub_client/rpc/gen/taple_service_pb2_grpc.py +1626 -0
- file_hub_client/rpc/generate_grpc.py +2 -2
- file_hub_client/rpc/interceptors.py +550 -0
- file_hub_client/rpc/protos/taple_service.proto +874 -0
- file_hub_client/rpc/sync_client.py +91 -9
- file_hub_client/schemas/__init__.py +60 -0
- file_hub_client/schemas/taple.py +413 -0
- file_hub_client/services/__init__.py +5 -0
- file_hub_client/services/file/async_blob_service.py +558 -482
- file_hub_client/services/file/async_file_service.py +18 -9
- file_hub_client/services/file/base_file_service.py +19 -6
- file_hub_client/services/file/sync_blob_service.py +556 -478
- file_hub_client/services/file/sync_file_service.py +18 -9
- file_hub_client/services/folder/async_folder_service.py +20 -11
- file_hub_client/services/folder/sync_folder_service.py +20 -11
- file_hub_client/services/taple/__init__.py +10 -0
- file_hub_client/services/taple/async_taple_service.py +2281 -0
- file_hub_client/services/taple/base_taple_service.py +353 -0
- file_hub_client/services/taple/idempotent_taple_mixin.py +142 -0
- file_hub_client/services/taple/sync_taple_service.py +2256 -0
- file_hub_client/utils/__init__.py +43 -1
- file_hub_client/utils/file_utils.py +59 -11
- file_hub_client/utils/idempotency.py +196 -0
- file_hub_client/utils/logging.py +315 -0
- file_hub_client/utils/retry.py +241 -2
- file_hub_client/utils/smart_retry.py +403 -0
- tamar_file_hub_client-0.0.3.dist-info/METADATA +2050 -0
- tamar_file_hub_client-0.0.3.dist-info/RECORD +57 -0
- tamar_file_hub_client-0.0.1.dist-info/METADATA +0 -874
- tamar_file_hub_client-0.0.1.dist-info/RECORD +0 -44
- {tamar_file_hub_client-0.0.1.dist-info → tamar_file_hub_client-0.0.3.dist-info}/WHEEL +0 -0
- {tamar_file_hub_client-0.0.1.dist-info → tamar_file_hub_client-0.0.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,2256 @@
|
|
1
|
+
"""
|
2
|
+
同步 Taple 服务
|
3
|
+
"""
|
4
|
+
import grpc
|
5
|
+
import tempfile
|
6
|
+
import os
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import Optional, List, Dict, Any, Union, Callable
|
9
|
+
import uuid
|
10
|
+
|
11
|
+
from .base_taple_service import BaseTapleService
|
12
|
+
from ...rpc.sync_client import SyncGrpcClient
|
13
|
+
from ...schemas.taple import (
|
14
|
+
TableResponse, SheetResponse, ColumnResponse, RowResponse, CellResponse,
|
15
|
+
ListSheetsResponse, ConflictInfo, CloneTableDataResponse, ExportTableDataResponse
|
16
|
+
)
|
17
|
+
from ...enums.export_format import ExportFormat
|
18
|
+
from ...errors import ValidationError
|
19
|
+
from ...utils.retry import retry_with_backoff, retry_on_lock_conflict
|
20
|
+
from ...utils.converter import timestamp_to_datetime
|
21
|
+
|
22
|
+
|
23
|
+
class SyncTapleService(BaseTapleService):
|
24
|
+
"""同步 Taple 服务"""
|
25
|
+
|
26
|
+
def __init__(self, client: SyncGrpcClient):
|
27
|
+
"""
|
28
|
+
初始化 Taple 服务
|
29
|
+
|
30
|
+
Args:
|
31
|
+
client: 同步 gRPC 客户端
|
32
|
+
"""
|
33
|
+
self.client = client
|
34
|
+
|
35
|
+
# Table operations
|
36
|
+
@retry_with_backoff(max_retries=3)
|
37
|
+
def create_table(
|
38
|
+
self,
|
39
|
+
name: str,
|
40
|
+
*,
|
41
|
+
folder_id: Optional[str] = None,
|
42
|
+
description: Optional[str] = None,
|
43
|
+
idempotency_key: Optional[str] = None,
|
44
|
+
request_id: Optional[str] = None,
|
45
|
+
**metadata
|
46
|
+
) -> TableResponse:
|
47
|
+
"""
|
48
|
+
创建表格
|
49
|
+
|
50
|
+
Args:
|
51
|
+
name: 表格名称
|
52
|
+
folder_id: 父文件夹ID(可选,默认为"我的文件夹")
|
53
|
+
description: 表格描述
|
54
|
+
idempotency_key: 幂等性键(可选)
|
55
|
+
**metadata: 额外的元数据
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
创建的表格信息
|
59
|
+
"""
|
60
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
61
|
+
|
62
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
63
|
+
|
64
|
+
request = taple_service_pb2.CreateTableRequest(name=name)
|
65
|
+
|
66
|
+
if folder_id:
|
67
|
+
request.folder_id = folder_id
|
68
|
+
if description:
|
69
|
+
request.description = description
|
70
|
+
if idempotency_key is not None:
|
71
|
+
request.idempotency_key = idempotency_key
|
72
|
+
|
73
|
+
response = stub.CreateTable(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
74
|
+
return TableResponse(table=self._convert_table(response.table))
|
75
|
+
|
76
|
+
@retry_with_backoff(max_retries=3)
|
77
|
+
def get_table(
|
78
|
+
self,
|
79
|
+
*,
|
80
|
+
table_id: Optional[str] = None,
|
81
|
+
file_id: Optional[str] = None,
|
82
|
+
request_id: Optional[str] = None,
|
83
|
+
**metadata
|
84
|
+
) -> TableResponse:
|
85
|
+
"""
|
86
|
+
获取表格信息
|
87
|
+
|
88
|
+
Args:
|
89
|
+
table_id: 表格ID
|
90
|
+
file_id: 文件ID
|
91
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
92
|
+
**metadata: 额外的元数据
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
表格信息
|
96
|
+
"""
|
97
|
+
if not table_id and not file_id:
|
98
|
+
raise ValidationError("table_id 或 file_id 必须提供一个")
|
99
|
+
|
100
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
101
|
+
|
102
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
103
|
+
|
104
|
+
request = taple_service_pb2.GetTableRequest()
|
105
|
+
if table_id:
|
106
|
+
request.table_id = table_id
|
107
|
+
if file_id:
|
108
|
+
request.file_id = file_id
|
109
|
+
|
110
|
+
response = stub.GetTable(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
111
|
+
return TableResponse(table=self._convert_table(response.table))
|
112
|
+
|
113
|
+
@retry_with_backoff(max_retries=3)
|
114
|
+
def update_table(
|
115
|
+
self,
|
116
|
+
table_id: str,
|
117
|
+
*,
|
118
|
+
name: Optional[str] = None,
|
119
|
+
description: Optional[str] = None,
|
120
|
+
idempotency_key: Optional[str] = None,
|
121
|
+
request_id: Optional[str] = None,
|
122
|
+
**metadata
|
123
|
+
) -> TableResponse:
|
124
|
+
"""
|
125
|
+
更新表格信息
|
126
|
+
|
127
|
+
Args:
|
128
|
+
table_id: 表格ID
|
129
|
+
name: 表格名称
|
130
|
+
description: 表格描述
|
131
|
+
idempotency_key: 幂等性键(可选)
|
132
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
133
|
+
**metadata: 额外的元数据
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
更新后的表格信息
|
137
|
+
"""
|
138
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
139
|
+
|
140
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
141
|
+
|
142
|
+
request = taple_service_pb2.UpdateTableRequest(table_id=table_id)
|
143
|
+
|
144
|
+
if name is not None:
|
145
|
+
request.name = name
|
146
|
+
if description is not None:
|
147
|
+
request.description = description
|
148
|
+
if idempotency_key is not None:
|
149
|
+
request.idempotency_key = idempotency_key
|
150
|
+
|
151
|
+
response = stub.UpdateTable(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
152
|
+
return TableResponse(table=self._convert_table(response.table))
|
153
|
+
|
154
|
+
@retry_with_backoff(max_retries=3)
|
155
|
+
def delete_table(self, table_id: str, *, idempotency_key: Optional[str] = None, request_id: Optional[str] = None,
|
156
|
+
**metadata) -> None:
|
157
|
+
"""
|
158
|
+
删除表格
|
159
|
+
|
160
|
+
Args:
|
161
|
+
table_id: 表格ID
|
162
|
+
idempotency_key: 幂等性键(可选)
|
163
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
164
|
+
**metadata: 额外的元数据
|
165
|
+
"""
|
166
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
167
|
+
|
168
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
169
|
+
|
170
|
+
request = taple_service_pb2.DeleteTableRequest(table_id=table_id)
|
171
|
+
|
172
|
+
if idempotency_key is not None:
|
173
|
+
request.idempotency_key = idempotency_key
|
174
|
+
|
175
|
+
stub.DeleteTable(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
176
|
+
|
177
|
+
# Sheet operations
|
178
|
+
@retry_with_backoff(max_retries=3)
|
179
|
+
def create_sheet(
|
180
|
+
self,
|
181
|
+
table_id: str,
|
182
|
+
name: str,
|
183
|
+
*,
|
184
|
+
description: Optional[str] = None,
|
185
|
+
position: Optional[int] = None,
|
186
|
+
idempotency_key: Optional[str] = None,
|
187
|
+
request_id: Optional[str] = None,
|
188
|
+
**metadata
|
189
|
+
) -> SheetResponse:
|
190
|
+
"""
|
191
|
+
创建工作表
|
192
|
+
|
193
|
+
Args:
|
194
|
+
table_id: 表格ID
|
195
|
+
name: 工作表名称
|
196
|
+
description: 工作表描述
|
197
|
+
position: 工作表位置
|
198
|
+
idempotency_key: 幂等性键(可选)
|
199
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
200
|
+
**metadata: 额外的元数据
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
创建的工作表信息
|
204
|
+
"""
|
205
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
206
|
+
|
207
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
208
|
+
|
209
|
+
request = taple_service_pb2.CreateSheetRequest(
|
210
|
+
table_id=table_id,
|
211
|
+
name=name
|
212
|
+
)
|
213
|
+
|
214
|
+
if description:
|
215
|
+
request.description = description
|
216
|
+
if position is not None:
|
217
|
+
request.position = position
|
218
|
+
if idempotency_key is not None:
|
219
|
+
request.idempotency_key = idempotency_key
|
220
|
+
|
221
|
+
response = stub.CreateSheet(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
222
|
+
return SheetResponse(sheet=self._convert_sheet(response.sheet))
|
223
|
+
|
224
|
+
@retry_with_backoff(max_retries=3)
|
225
|
+
def get_sheet(self, sheet_id: str, request_id: Optional[str] = None,
|
226
|
+
**metadata) -> SheetResponse:
|
227
|
+
"""
|
228
|
+
获取工作表信息
|
229
|
+
|
230
|
+
Args:
|
231
|
+
sheet_id: 工作表ID
|
232
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
233
|
+
**metadata: 额外的元数据
|
234
|
+
|
235
|
+
Returns:
|
236
|
+
工作表信息
|
237
|
+
"""
|
238
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
239
|
+
|
240
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
241
|
+
|
242
|
+
request = taple_service_pb2.GetSheetRequest(sheet_id=sheet_id)
|
243
|
+
response = stub.GetSheet(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
244
|
+
return SheetResponse(sheet=self._convert_sheet(response.sheet))
|
245
|
+
|
246
|
+
@retry_with_backoff(max_retries=3)
|
247
|
+
def list_sheets(self, table_id: str, request_id: Optional[str] = None,
|
248
|
+
**metadata) -> ListSheetsResponse:
|
249
|
+
"""
|
250
|
+
列出工作表
|
251
|
+
|
252
|
+
Args:
|
253
|
+
table_id: 表格ID
|
254
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
255
|
+
**metadata: 额外的元数据
|
256
|
+
|
257
|
+
Returns:
|
258
|
+
工作表列表
|
259
|
+
"""
|
260
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
261
|
+
|
262
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
263
|
+
|
264
|
+
request = taple_service_pb2.ListSheetsRequest(table_id=table_id)
|
265
|
+
response = stub.ListSheets(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
266
|
+
|
267
|
+
sheets = [self._convert_sheet(sheet) for sheet in response.sheets]
|
268
|
+
return ListSheetsResponse(sheets=sheets)
|
269
|
+
|
270
|
+
@retry_with_backoff(max_retries=3)
|
271
|
+
def update_sheet(
|
272
|
+
self,
|
273
|
+
sheet_id: str,
|
274
|
+
*,
|
275
|
+
name: Optional[str] = None,
|
276
|
+
description: Optional[str] = None,
|
277
|
+
position: Optional[int] = None,
|
278
|
+
idempotency_key: Optional[str] = None,
|
279
|
+
request_id: Optional[str] = None,
|
280
|
+
**metadata
|
281
|
+
) -> SheetResponse:
|
282
|
+
"""
|
283
|
+
更新工作表
|
284
|
+
|
285
|
+
Args:
|
286
|
+
sheet_id: 工作表ID
|
287
|
+
name: 工作表名称
|
288
|
+
description: 工作表描述
|
289
|
+
position: 工作表位置
|
290
|
+
idempotency_key: 幂等性键(可选)
|
291
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
292
|
+
**metadata: 额外的元数据
|
293
|
+
|
294
|
+
Returns:
|
295
|
+
更新后的工作表信息
|
296
|
+
"""
|
297
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
298
|
+
|
299
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
300
|
+
|
301
|
+
request = taple_service_pb2.UpdateSheetRequest(sheet_id=sheet_id)
|
302
|
+
|
303
|
+
if name is not None:
|
304
|
+
request.name = name
|
305
|
+
if description is not None:
|
306
|
+
request.description = description
|
307
|
+
if position is not None:
|
308
|
+
request.position = position
|
309
|
+
if idempotency_key is not None:
|
310
|
+
request.idempotency_key = idempotency_key
|
311
|
+
|
312
|
+
response = stub.UpdateSheet(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
313
|
+
return SheetResponse(sheet=self._convert_sheet(response.sheet))
|
314
|
+
|
315
|
+
@retry_with_backoff(max_retries=3)
|
316
|
+
def delete_sheet(self, sheet_id: str, *, idempotency_key: Optional[str] = None, request_id: Optional[str] = None,
|
317
|
+
**metadata) -> None:
|
318
|
+
"""
|
319
|
+
删除工作表
|
320
|
+
|
321
|
+
Args:
|
322
|
+
sheet_id: 工作表ID
|
323
|
+
idempotency_key: 幂等性键(可选)
|
324
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
325
|
+
**metadata: 额外的元数据
|
326
|
+
"""
|
327
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
328
|
+
|
329
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
330
|
+
|
331
|
+
request = taple_service_pb2.DeleteSheetRequest(sheet_id=sheet_id)
|
332
|
+
|
333
|
+
if idempotency_key is not None:
|
334
|
+
request.idempotency_key = idempotency_key
|
335
|
+
|
336
|
+
stub.DeleteSheet(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
337
|
+
|
338
|
+
# Column operations
|
339
|
+
@retry_with_backoff(max_retries=3)
|
340
|
+
@retry_on_lock_conflict()
|
341
|
+
def create_column(
|
342
|
+
self,
|
343
|
+
sheet_id: str,
|
344
|
+
name: str,
|
345
|
+
column_type: str = "text",
|
346
|
+
*,
|
347
|
+
sheet_version: Optional[int] = None,
|
348
|
+
client_id: Optional[str] = None,
|
349
|
+
position: Optional[int] = None,
|
350
|
+
width: Optional[int] = None,
|
351
|
+
description: Optional[str] = None,
|
352
|
+
properties: Optional[Dict[str, Any]] = None,
|
353
|
+
idempotency_key: Optional[str] = None,
|
354
|
+
request_id: Optional[str] = None,
|
355
|
+
**metadata
|
356
|
+
) -> ColumnResponse:
|
357
|
+
"""
|
358
|
+
创建列
|
359
|
+
|
360
|
+
Args:
|
361
|
+
sheet_id: 工作表ID
|
362
|
+
name: 列名称
|
363
|
+
column_type: 列类型
|
364
|
+
sheet_version: 版本号(可选,不传则自动获取)
|
365
|
+
client_id: 客户端ID(可选,不传则自动生成)
|
366
|
+
position: 列位置
|
367
|
+
width: 列宽度
|
368
|
+
description: 列描述信息
|
369
|
+
properties: 列属性
|
370
|
+
idempotency_key: 幂等性键(可选)
|
371
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
372
|
+
**metadata: 额外的元数据
|
373
|
+
|
374
|
+
Returns:
|
375
|
+
创建的列信息
|
376
|
+
"""
|
377
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
378
|
+
import uuid
|
379
|
+
|
380
|
+
# 如果没有提供sheet_version,自动获取
|
381
|
+
if sheet_version is None:
|
382
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
383
|
+
sheet_version = version_result.version
|
384
|
+
|
385
|
+
# 如果没有提供client_id,自动生成
|
386
|
+
if client_id is None:
|
387
|
+
client_id = str(uuid.uuid4())
|
388
|
+
|
389
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
390
|
+
|
391
|
+
request = taple_service_pb2.CreateColumnRequest(
|
392
|
+
sheet_id=sheet_id,
|
393
|
+
sheet_version=sheet_version,
|
394
|
+
client_id=client_id,
|
395
|
+
name=name,
|
396
|
+
column_type=column_type
|
397
|
+
)
|
398
|
+
if position is not None:
|
399
|
+
request.position = position
|
400
|
+
if width is not None:
|
401
|
+
request.width = width
|
402
|
+
if description is not None:
|
403
|
+
request.description = description
|
404
|
+
if properties:
|
405
|
+
request.properties.CopyFrom(self._convert_dict_to_struct(properties))
|
406
|
+
if idempotency_key is not None:
|
407
|
+
request.idempotency_key = idempotency_key
|
408
|
+
|
409
|
+
response = stub.CreateColumn(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
410
|
+
|
411
|
+
from ...schemas.taple import ColumnResponse
|
412
|
+
return ColumnResponse(
|
413
|
+
column=self._convert_column(response.column) if response.column else None,
|
414
|
+
current_version=response.current_version if hasattr(response, 'current_version') else None,
|
415
|
+
applied_immediately=response.applied_immediately if hasattr(response, 'applied_immediately') else None
|
416
|
+
)
|
417
|
+
|
418
|
+
|
419
|
+
@retry_with_backoff(max_retries=3)
|
420
|
+
@retry_on_lock_conflict()
|
421
|
+
def update_column(
|
422
|
+
self,
|
423
|
+
sheet_id: str,
|
424
|
+
column_key: str,
|
425
|
+
*,
|
426
|
+
sheet_version: Optional[int] = None,
|
427
|
+
client_id: Optional[str] = None,
|
428
|
+
name: Optional[str] = None,
|
429
|
+
column_type: Optional[str] = None,
|
430
|
+
width: Optional[int] = None,
|
431
|
+
hidden: Optional[bool] = None,
|
432
|
+
description: Optional[str] = None,
|
433
|
+
properties: Optional[Dict[str, Any]] = None,
|
434
|
+
idempotency_key: Optional[str] = None,
|
435
|
+
request_id: Optional[str] = None,
|
436
|
+
**metadata
|
437
|
+
) -> ColumnResponse:
|
438
|
+
"""
|
439
|
+
更新列
|
440
|
+
|
441
|
+
Args:
|
442
|
+
sheet_id: 工作表ID
|
443
|
+
column_key: 列key
|
444
|
+
sheet_version: 版本号(可选,不传则自动获取)
|
445
|
+
client_id: 客户端ID(可选,不传则自动生成)
|
446
|
+
name: 列名称
|
447
|
+
column_type: 列类型
|
448
|
+
width: 列宽度
|
449
|
+
hidden: 是否隐藏
|
450
|
+
description: 列描述信息
|
451
|
+
properties: 列属性
|
452
|
+
idempotency_key: 幂等性键(可选)
|
453
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
454
|
+
**metadata: 额外的元数据
|
455
|
+
|
456
|
+
Returns:
|
457
|
+
更新后的列信息
|
458
|
+
"""
|
459
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
460
|
+
import uuid
|
461
|
+
|
462
|
+
# 如果没有提供sheet_version,自动获取
|
463
|
+
if sheet_version is None:
|
464
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
465
|
+
sheet_version = version_result.version
|
466
|
+
|
467
|
+
# 如果没有提供client_id,自动生成
|
468
|
+
if client_id is None:
|
469
|
+
client_id = str(uuid.uuid4())
|
470
|
+
|
471
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
472
|
+
|
473
|
+
request = taple_service_pb2.UpdateColumnRequest(
|
474
|
+
sheet_id=sheet_id,
|
475
|
+
sheet_version=sheet_version,
|
476
|
+
client_id=client_id,
|
477
|
+
column_key=column_key
|
478
|
+
)
|
479
|
+
|
480
|
+
if name is not None:
|
481
|
+
request.name = name
|
482
|
+
if column_type is not None:
|
483
|
+
request.column_type = column_type
|
484
|
+
if width is not None:
|
485
|
+
request.width = width
|
486
|
+
if hidden is not None:
|
487
|
+
request.hidden = hidden
|
488
|
+
if description is not None:
|
489
|
+
request.description = description
|
490
|
+
if properties:
|
491
|
+
request.properties.CopyFrom(self._convert_dict_to_struct(properties))
|
492
|
+
if idempotency_key is not None:
|
493
|
+
request.idempotency_key = idempotency_key
|
494
|
+
|
495
|
+
response = stub.UpdateColumn(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
496
|
+
|
497
|
+
from ...schemas.taple import ColumnResponse
|
498
|
+
return ColumnResponse(
|
499
|
+
column=self._convert_column(response.column) if response.column else None,
|
500
|
+
current_version=response.current_version if hasattr(response, 'current_version') else None,
|
501
|
+
applied_immediately=response.applied_immediately if hasattr(response, 'applied_immediately') else None
|
502
|
+
)
|
503
|
+
|
504
|
+
@retry_with_backoff(max_retries=3)
|
505
|
+
@retry_on_lock_conflict()
|
506
|
+
def delete_column(
|
507
|
+
self,
|
508
|
+
sheet_id: str,
|
509
|
+
column_key: str,
|
510
|
+
*,
|
511
|
+
sheet_version: Optional[int] = None,
|
512
|
+
client_id: Optional[str] = None,
|
513
|
+
idempotency_key: Optional[str] = None,
|
514
|
+
request_id: Optional[str] = None,
|
515
|
+
**metadata
|
516
|
+
) -> None:
|
517
|
+
"""
|
518
|
+
删除列
|
519
|
+
|
520
|
+
Args:
|
521
|
+
sheet_id: 工作表ID
|
522
|
+
column_key: 列key
|
523
|
+
sheet_version: 版本号(可选,不传则自动获取)
|
524
|
+
client_id: 客户端ID(可选,不传则自动生成)
|
525
|
+
idempotency_key: 幂等性键(可选)
|
526
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
527
|
+
**metadata: 额外的元数据
|
528
|
+
"""
|
529
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
530
|
+
import uuid
|
531
|
+
|
532
|
+
# 如果没有提供sheet_version,自动获取
|
533
|
+
if sheet_version is None:
|
534
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
535
|
+
sheet_version = version_result.version
|
536
|
+
|
537
|
+
# 如果没有提供client_id,自动生成
|
538
|
+
if client_id is None:
|
539
|
+
client_id = str(uuid.uuid4())
|
540
|
+
|
541
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
542
|
+
|
543
|
+
request = taple_service_pb2.DeleteColumnRequest(
|
544
|
+
sheet_id=sheet_id,
|
545
|
+
sheet_version=sheet_version,
|
546
|
+
client_id=client_id,
|
547
|
+
column_key=column_key
|
548
|
+
)
|
549
|
+
if idempotency_key is not None:
|
550
|
+
request.idempotency_key = idempotency_key
|
551
|
+
stub.DeleteColumn(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
552
|
+
|
553
|
+
|
554
|
+
# Row operations
|
555
|
+
@retry_with_backoff(max_retries=3)
|
556
|
+
@retry_on_lock_conflict()
|
557
|
+
def create_row(
|
558
|
+
self,
|
559
|
+
sheet_id: str,
|
560
|
+
*,
|
561
|
+
sheet_version: Optional[int] = None,
|
562
|
+
client_id: Optional[str] = None,
|
563
|
+
position: Optional[int] = None,
|
564
|
+
height: Optional[int] = None,
|
565
|
+
hidden: Optional[bool] = None,
|
566
|
+
idempotency_key: Optional[str] = None,
|
567
|
+
request_id: Optional[str] = None,
|
568
|
+
**metadata
|
569
|
+
) -> RowResponse:
|
570
|
+
"""
|
571
|
+
创建行
|
572
|
+
|
573
|
+
Args:
|
574
|
+
sheet_id: 工作表ID
|
575
|
+
sheet_version: 版本号(可选,不传则自动获取)
|
576
|
+
client_id: 客户端ID(可选,不传则自动生成)
|
577
|
+
position: 行位置
|
578
|
+
height: 行高度
|
579
|
+
hidden: 是否隐藏
|
580
|
+
idempotency_key: 幂等性键(可选)
|
581
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
582
|
+
**metadata: 额外的元数据
|
583
|
+
|
584
|
+
Returns:
|
585
|
+
创建的行信息
|
586
|
+
"""
|
587
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
588
|
+
import uuid
|
589
|
+
|
590
|
+
# 如果没有提供sheet_version,自动获取
|
591
|
+
if sheet_version is None:
|
592
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
593
|
+
sheet_version = version_result.version
|
594
|
+
|
595
|
+
# 如果没有提供client_id,自动生成
|
596
|
+
if client_id is None:
|
597
|
+
client_id = str(uuid.uuid4())
|
598
|
+
|
599
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
600
|
+
|
601
|
+
request = taple_service_pb2.CreateRowRequest(
|
602
|
+
sheet_id=sheet_id,
|
603
|
+
sheet_version=sheet_version,
|
604
|
+
client_id=client_id
|
605
|
+
)
|
606
|
+
|
607
|
+
if position is not None:
|
608
|
+
request.position = position
|
609
|
+
if height is not None:
|
610
|
+
request.height = height
|
611
|
+
if hidden is not None:
|
612
|
+
request.hidden = hidden
|
613
|
+
if idempotency_key is not None:
|
614
|
+
request.idempotency_key = idempotency_key
|
615
|
+
|
616
|
+
response = stub.CreateRow(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
617
|
+
|
618
|
+
conflict_info = None
|
619
|
+
if hasattr(response, 'conflict_info') and response.conflict_info:
|
620
|
+
conflict_info = ConflictInfo(
|
621
|
+
has_conflict=response.conflict_info.has_conflict,
|
622
|
+
server_version=response.conflict_info.server_version,
|
623
|
+
conflict_type=response.conflict_info.conflict_type,
|
624
|
+
conflicted_columns=list(response.conflict_info.conflicted_columns),
|
625
|
+
resolution_suggestion=response.conflict_info.resolution_suggestion
|
626
|
+
)
|
627
|
+
|
628
|
+
return RowResponse(
|
629
|
+
row=self._convert_row(response.row) if response.row else None,
|
630
|
+
current_version=response.current_version if hasattr(response, 'current_version') else None,
|
631
|
+
applied_immediately=response.applied_immediately if hasattr(response, 'applied_immediately') else None,
|
632
|
+
success=response.success if hasattr(response, 'success') else None,
|
633
|
+
error_message=response.error_message if hasattr(response, 'error_message') else None,
|
634
|
+
conflict_info=conflict_info
|
635
|
+
)
|
636
|
+
|
637
|
+
|
638
|
+
@retry_with_backoff(max_retries=3)
|
639
|
+
@retry_on_lock_conflict()
|
640
|
+
def update_row(
|
641
|
+
self,
|
642
|
+
sheet_id: str,
|
643
|
+
row_key: str,
|
644
|
+
*,
|
645
|
+
sheet_version: Optional[int] = None,
|
646
|
+
client_id: Optional[str] = None,
|
647
|
+
position: Optional[int] = None,
|
648
|
+
height: Optional[int] = None,
|
649
|
+
hidden: Optional[bool] = None,
|
650
|
+
idempotency_key: Optional[str] = None,
|
651
|
+
request_id: Optional[str] = None,
|
652
|
+
**metadata
|
653
|
+
) -> RowResponse:
|
654
|
+
"""
|
655
|
+
更新行
|
656
|
+
|
657
|
+
Args:
|
658
|
+
sheet_id: 工作表ID
|
659
|
+
row_key: 行key
|
660
|
+
sheet_version: 版本号(可选,不传则自动获取)
|
661
|
+
client_id: 客户端ID(可选,不传则自动生成)
|
662
|
+
position: 行位置
|
663
|
+
height: 行高度
|
664
|
+
hidden: 是否隐藏
|
665
|
+
idempotency_key: 幂等性键(可选)
|
666
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
667
|
+
**metadata: 额外的元数据
|
668
|
+
|
669
|
+
Returns:
|
670
|
+
更新后的行信息
|
671
|
+
"""
|
672
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
673
|
+
import uuid
|
674
|
+
|
675
|
+
# 如果没有提供sheet_version,自动获取
|
676
|
+
if sheet_version is None:
|
677
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
678
|
+
sheet_version = version_result.version
|
679
|
+
|
680
|
+
# 如果没有提供client_id,自动生成
|
681
|
+
if client_id is None:
|
682
|
+
client_id = str(uuid.uuid4())
|
683
|
+
|
684
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
685
|
+
|
686
|
+
request = taple_service_pb2.UpdateRowRequest(
|
687
|
+
sheet_id=sheet_id,
|
688
|
+
sheet_version=sheet_version,
|
689
|
+
client_id=client_id,
|
690
|
+
row_key=row_key
|
691
|
+
)
|
692
|
+
|
693
|
+
if position is not None:
|
694
|
+
request.position = position
|
695
|
+
if height is not None:
|
696
|
+
request.height = height
|
697
|
+
if hidden is not None:
|
698
|
+
request.hidden = hidden
|
699
|
+
if idempotency_key is not None:
|
700
|
+
request.idempotency_key = idempotency_key
|
701
|
+
|
702
|
+
response = stub.UpdateRow(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
703
|
+
|
704
|
+
conflict_info = None
|
705
|
+
if hasattr(response, 'conflict_info') and response.conflict_info:
|
706
|
+
conflict_info = ConflictInfo(
|
707
|
+
has_conflict=response.conflict_info.has_conflict,
|
708
|
+
server_version=response.conflict_info.server_version,
|
709
|
+
conflict_type=response.conflict_info.conflict_type,
|
710
|
+
conflicted_columns=list(response.conflict_info.conflicted_columns),
|
711
|
+
resolution_suggestion=response.conflict_info.resolution_suggestion
|
712
|
+
)
|
713
|
+
|
714
|
+
return RowResponse(
|
715
|
+
row=self._convert_row(response.row) if response.row else None,
|
716
|
+
current_version=response.current_version if hasattr(response, 'current_version') else None,
|
717
|
+
applied_immediately=response.applied_immediately if hasattr(response, 'applied_immediately') else None,
|
718
|
+
success=response.success if hasattr(response, 'success') else None,
|
719
|
+
error_message=response.error_message if hasattr(response, 'error_message') else None,
|
720
|
+
conflict_info=conflict_info
|
721
|
+
)
|
722
|
+
|
723
|
+
@retry_with_backoff(max_retries=3)
|
724
|
+
@retry_on_lock_conflict()
|
725
|
+
def delete_row(
|
726
|
+
self,
|
727
|
+
sheet_id: str,
|
728
|
+
row_key: str,
|
729
|
+
*,
|
730
|
+
sheet_version: Optional[int] = None,
|
731
|
+
client_id: Optional[str] = None,
|
732
|
+
idempotency_key: Optional[str] = None,
|
733
|
+
request_id: Optional[str] = None,
|
734
|
+
**metadata
|
735
|
+
) -> None:
|
736
|
+
"""
|
737
|
+
删除行
|
738
|
+
|
739
|
+
Args:
|
740
|
+
sheet_id: 工作表ID
|
741
|
+
row_key: 行key
|
742
|
+
sheet_version: 版本号(可选,不传则自动获取)
|
743
|
+
client_id: 客户端ID(可选,不传则自动生成)
|
744
|
+
idempotency_key: 幂等性键(可选)
|
745
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
746
|
+
**metadata: 额外的元数据
|
747
|
+
"""
|
748
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
749
|
+
import uuid
|
750
|
+
|
751
|
+
# 如果没有提供sheet_version,自动获取
|
752
|
+
if sheet_version is None:
|
753
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
754
|
+
sheet_version = version_result.version
|
755
|
+
|
756
|
+
# 如果没有提供client_id,自动生成
|
757
|
+
if client_id is None:
|
758
|
+
client_id = str(uuid.uuid4())
|
759
|
+
|
760
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
761
|
+
|
762
|
+
request = taple_service_pb2.DeleteRowRequest(
|
763
|
+
sheet_id=sheet_id,
|
764
|
+
sheet_version=sheet_version,
|
765
|
+
client_id=client_id,
|
766
|
+
row_key=row_key
|
767
|
+
)
|
768
|
+
|
769
|
+
if idempotency_key is not None:
|
770
|
+
request.idempotency_key = idempotency_key
|
771
|
+
|
772
|
+
stub.DeleteRow(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
773
|
+
|
774
|
+
# 以下是在 proto 中定义的方法
|
775
|
+
@retry_with_backoff(max_retries=3)
|
776
|
+
@retry_on_lock_conflict()
|
777
|
+
def edit_cell(
|
778
|
+
self,
|
779
|
+
sheet_id: str,
|
780
|
+
column_key: str,
|
781
|
+
row_key: str,
|
782
|
+
*,
|
783
|
+
sheet_version: Optional[int] = None,
|
784
|
+
client_id: Optional[str] = None,
|
785
|
+
raw_value: Optional[str] = None,
|
786
|
+
formatted_value: Optional[str] = None,
|
787
|
+
formula: Optional[str] = None,
|
788
|
+
data_type: Optional[str] = None,
|
789
|
+
styles: Optional[Dict[str, Any]] = None,
|
790
|
+
idempotency_key: Optional[str] = None,
|
791
|
+
request_id: Optional[str] = None,
|
792
|
+
**metadata
|
793
|
+
) -> CellResponse:
|
794
|
+
"""
|
795
|
+
Edit a cell with version control (create or update).
|
796
|
+
|
797
|
+
Args:
|
798
|
+
sheet_id: Sheet ID
|
799
|
+
column_key: Column key
|
800
|
+
row_key: Row key
|
801
|
+
sheet_version: Version for optimistic locking (optional)
|
802
|
+
client_id: Client ID (optional)
|
803
|
+
raw_value: Cell value
|
804
|
+
formatted_value: Formatted value
|
805
|
+
formula: Cell formula
|
806
|
+
data_type: Data type
|
807
|
+
styles: Cell styles
|
808
|
+
idempotency_key: 幂等性键(可选)
|
809
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
810
|
+
**metadata: Extra metadata
|
811
|
+
|
812
|
+
Returns:
|
813
|
+
Cell response with updated info
|
814
|
+
"""
|
815
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
816
|
+
import uuid
|
817
|
+
|
818
|
+
# 如果没有提供sheet_version,自动获取
|
819
|
+
if sheet_version is None:
|
820
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
821
|
+
sheet_version = version_result.version
|
822
|
+
|
823
|
+
# 如果没有提供client_id,自动生成
|
824
|
+
if client_id is None:
|
825
|
+
client_id = str(uuid.uuid4())
|
826
|
+
|
827
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
828
|
+
|
829
|
+
request = taple_service_pb2.EditCellRequest(
|
830
|
+
sheet_id=sheet_id,
|
831
|
+
column_key=column_key,
|
832
|
+
row_key=row_key,
|
833
|
+
sheet_version=sheet_version,
|
834
|
+
client_id=client_id
|
835
|
+
)
|
836
|
+
|
837
|
+
if raw_value is not None:
|
838
|
+
request.raw_value = raw_value
|
839
|
+
if formatted_value is not None:
|
840
|
+
request.formatted_value = formatted_value
|
841
|
+
if formula is not None:
|
842
|
+
request.formula = formula
|
843
|
+
if data_type is not None:
|
844
|
+
request.data_type = data_type
|
845
|
+
if styles:
|
846
|
+
request.styles.CopyFrom(self._convert_dict_to_struct(styles))
|
847
|
+
if idempotency_key is not None:
|
848
|
+
request.idempotency_key = idempotency_key
|
849
|
+
|
850
|
+
response = stub.EditCell(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
851
|
+
|
852
|
+
from ...schemas.taple import CellResponse, ConflictInfo
|
853
|
+
|
854
|
+
conflict_info = None
|
855
|
+
if hasattr(response, 'conflict_info') and response.conflict_info:
|
856
|
+
conflict_info = ConflictInfo(
|
857
|
+
has_conflict=response.conflict_info.has_conflict,
|
858
|
+
server_version=response.conflict_info.server_version,
|
859
|
+
conflict_type=response.conflict_info.conflict_type,
|
860
|
+
conflicted_columns=list(response.conflict_info.conflicted_columns),
|
861
|
+
resolution_suggestion=response.conflict_info.resolution_suggestion
|
862
|
+
)
|
863
|
+
|
864
|
+
return CellResponse(
|
865
|
+
cell=self._convert_cell(response.cell) if response.cell else None,
|
866
|
+
current_version=response.current_version if hasattr(response, 'current_version') else None,
|
867
|
+
applied_immediately=response.applied_immediately if hasattr(response, 'applied_immediately') else None,
|
868
|
+
success=response.success if hasattr(response, 'success') else None,
|
869
|
+
error_message=response.error_message if hasattr(response, 'error_message') else None,
|
870
|
+
conflict_info=conflict_info
|
871
|
+
)
|
872
|
+
|
873
|
+
@retry_with_backoff(max_retries=3)
|
874
|
+
@retry_on_lock_conflict()
|
875
|
+
def delete_cell(
|
876
|
+
self,
|
877
|
+
sheet_id: str,
|
878
|
+
column_key: str,
|
879
|
+
row_key: str,
|
880
|
+
*,
|
881
|
+
sheet_version: Optional[int] = None,
|
882
|
+
client_id: Optional[str] = None,
|
883
|
+
idempotency_key: Optional[str] = None,
|
884
|
+
request_id: Optional[str] = None,
|
885
|
+
**metadata
|
886
|
+
) -> None:
|
887
|
+
"""
|
888
|
+
Delete a cell with version control.
|
889
|
+
|
890
|
+
Args:
|
891
|
+
sheet_id: Sheet ID
|
892
|
+
column_key: Column key
|
893
|
+
row_key: Row key
|
894
|
+
sheet_version: Version for optimistic locking (optional)
|
895
|
+
client_id: Client ID (optional)
|
896
|
+
idempotency_key: 幂等性键(可选)
|
897
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
898
|
+
**metadata: Extra metadata
|
899
|
+
"""
|
900
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
901
|
+
import uuid
|
902
|
+
|
903
|
+
# 如果没有提供sheet_version,自动获取
|
904
|
+
if sheet_version is None:
|
905
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
906
|
+
sheet_version = version_result.version
|
907
|
+
|
908
|
+
# 如果没有提供client_id,自动生成
|
909
|
+
if client_id is None:
|
910
|
+
client_id = str(uuid.uuid4())
|
911
|
+
|
912
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
913
|
+
|
914
|
+
request = taple_service_pb2.DeleteCellRequest(
|
915
|
+
sheet_id=sheet_id,
|
916
|
+
column_key=column_key,
|
917
|
+
row_key=row_key,
|
918
|
+
sheet_version=sheet_version,
|
919
|
+
client_id=client_id
|
920
|
+
)
|
921
|
+
if idempotency_key is not None:
|
922
|
+
request.idempotency_key = idempotency_key
|
923
|
+
|
924
|
+
stub.DeleteCell(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
925
|
+
|
926
|
+
@retry_with_backoff(max_retries=3)
|
927
|
+
@retry_on_lock_conflict()
|
928
|
+
def batch_edit_columns(
|
929
|
+
self,
|
930
|
+
sheet_id: str,
|
931
|
+
operations: List[Dict[str, Any]],
|
932
|
+
*,
|
933
|
+
sheet_version: Optional[int] = None,
|
934
|
+
client_id: Optional[str] = None,
|
935
|
+
idempotency_key: Optional[str] = None,
|
936
|
+
request_id: Optional[str] = None,
|
937
|
+
**metadata
|
938
|
+
) -> Any:
|
939
|
+
"""
|
940
|
+
Execute batch column operations.
|
941
|
+
|
942
|
+
Args:
|
943
|
+
sheet_id: Sheet ID
|
944
|
+
operations: List of column operations
|
945
|
+
sheet_version: Version for optimistic locking (optional)
|
946
|
+
client_id: Client ID (optional)
|
947
|
+
idempotency_key: 幂等性键(可选)
|
948
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
949
|
+
**metadata: Extra metadata
|
950
|
+
|
951
|
+
Returns:
|
952
|
+
BatchEditColumnsResponse
|
953
|
+
"""
|
954
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
955
|
+
import uuid
|
956
|
+
|
957
|
+
# 如果没有提供sheet_version,自动获取
|
958
|
+
if sheet_version is None:
|
959
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
960
|
+
sheet_version = version_result.version
|
961
|
+
|
962
|
+
# 如果没有提供client_id,自动生成
|
963
|
+
if client_id is None:
|
964
|
+
client_id = str(uuid.uuid4())
|
965
|
+
|
966
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
967
|
+
|
968
|
+
# Convert operations to proto format
|
969
|
+
proto_operations = []
|
970
|
+
for op in operations:
|
971
|
+
column_op = taple_service_pb2.ColumnOperation()
|
972
|
+
|
973
|
+
if 'create' in op:
|
974
|
+
create_data = taple_service_pb2.CreateColumnData(
|
975
|
+
name=op['create']['name']
|
976
|
+
)
|
977
|
+
if 'column_type' in op['create']:
|
978
|
+
create_data.column_type = op['create']['column_type']
|
979
|
+
if 'position' in op['create']:
|
980
|
+
create_data.position = op['create']['position']
|
981
|
+
if 'width' in op['create']:
|
982
|
+
create_data.width = op['create']['width']
|
983
|
+
if 'properties' in op['create']:
|
984
|
+
create_data.properties.CopyFrom(self._convert_dict_to_struct(op['create']['properties']))
|
985
|
+
column_op.create.CopyFrom(create_data)
|
986
|
+
|
987
|
+
elif 'update' in op:
|
988
|
+
update_data = taple_service_pb2.UpdateColumnData(
|
989
|
+
column_key=op['update']['column_key']
|
990
|
+
)
|
991
|
+
if 'name' in op['update']:
|
992
|
+
update_data.name = op['update']['name']
|
993
|
+
if 'column_type' in op['update']:
|
994
|
+
update_data.column_type = op['update']['column_type']
|
995
|
+
if 'position' in op['update']:
|
996
|
+
update_data.position = op['update']['position']
|
997
|
+
if 'width' in op['update']:
|
998
|
+
update_data.width = op['update']['width']
|
999
|
+
if 'hidden' in op['update']:
|
1000
|
+
update_data.hidden = op['update']['hidden']
|
1001
|
+
if 'description' in op['update']:
|
1002
|
+
update_data.description = op['update']['description']
|
1003
|
+
if 'properties' in op['update']:
|
1004
|
+
update_data.properties.CopyFrom(self._convert_dict_to_struct(op['update']['properties']))
|
1005
|
+
column_op.update.CopyFrom(update_data)
|
1006
|
+
|
1007
|
+
elif 'delete' in op:
|
1008
|
+
delete_data = taple_service_pb2.DeleteColumnData(
|
1009
|
+
column_key=op['delete']['column_key']
|
1010
|
+
)
|
1011
|
+
column_op.delete.CopyFrom(delete_data)
|
1012
|
+
|
1013
|
+
proto_operations.append(column_op)
|
1014
|
+
|
1015
|
+
request = taple_service_pb2.BatchEditColumnsRequest(
|
1016
|
+
sheet_id=sheet_id,
|
1017
|
+
sheet_version=sheet_version,
|
1018
|
+
client_id=client_id,
|
1019
|
+
operations=proto_operations
|
1020
|
+
)
|
1021
|
+
if idempotency_key is not None:
|
1022
|
+
request.idempotency_key = idempotency_key
|
1023
|
+
|
1024
|
+
response = stub.BatchEditColumns(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1025
|
+
|
1026
|
+
from ...schemas.taple import ConflictInfo
|
1027
|
+
|
1028
|
+
conflict_info = None
|
1029
|
+
if response.conflict_info:
|
1030
|
+
conflict_info = ConflictInfo(
|
1031
|
+
has_conflict=response.conflict_info.has_conflict,
|
1032
|
+
server_version=response.conflict_info.server_version,
|
1033
|
+
conflict_type=response.conflict_info.conflict_type,
|
1034
|
+
conflicted_columns=list(response.conflict_info.conflicted_columns),
|
1035
|
+
resolution_suggestion=response.conflict_info.resolution_suggestion
|
1036
|
+
)
|
1037
|
+
|
1038
|
+
# Return raw response for now, can create proper schema later
|
1039
|
+
return {
|
1040
|
+
'success': response.success,
|
1041
|
+
'current_version': response.current_version,
|
1042
|
+
'results': [
|
1043
|
+
{
|
1044
|
+
'success': result.success,
|
1045
|
+
'column': self._convert_column(result.column) if result.column else None,
|
1046
|
+
'error_message': result.error_message,
|
1047
|
+
'operation_type': result.operation_type
|
1048
|
+
} for result in response.results
|
1049
|
+
],
|
1050
|
+
'error_message': response.error_message,
|
1051
|
+
'conflict_info': conflict_info
|
1052
|
+
}
|
1053
|
+
|
1054
|
+
@retry_with_backoff(max_retries=3)
|
1055
|
+
@retry_on_lock_conflict()
|
1056
|
+
def batch_edit_rows(
|
1057
|
+
self,
|
1058
|
+
sheet_id: str,
|
1059
|
+
operations: List[Dict[str, Any]],
|
1060
|
+
*,
|
1061
|
+
sheet_version: Optional[int] = None,
|
1062
|
+
client_id: Optional[str] = None,
|
1063
|
+
idempotency_key: Optional[str] = None,
|
1064
|
+
request_id: Optional[str] = None,
|
1065
|
+
**metadata
|
1066
|
+
) -> Any:
|
1067
|
+
"""
|
1068
|
+
Execute batch row operations.
|
1069
|
+
|
1070
|
+
Args:
|
1071
|
+
sheet_id: Sheet ID
|
1072
|
+
operations: List of row operations
|
1073
|
+
sheet_version: Version for optimistic locking (optional)
|
1074
|
+
client_id: Client ID (optional)
|
1075
|
+
idempotency_key: 幂等性键(可选)
|
1076
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1077
|
+
**metadata: Extra metadata
|
1078
|
+
|
1079
|
+
Returns:
|
1080
|
+
BatchEditRowsResponse
|
1081
|
+
"""
|
1082
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1083
|
+
import uuid
|
1084
|
+
|
1085
|
+
# 如果没有提供sheet_version,自动获取
|
1086
|
+
if sheet_version is None:
|
1087
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
1088
|
+
sheet_version = version_result.version
|
1089
|
+
|
1090
|
+
# 如果没有提供client_id,自动生成
|
1091
|
+
if client_id is None:
|
1092
|
+
client_id = str(uuid.uuid4())
|
1093
|
+
|
1094
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1095
|
+
|
1096
|
+
# Convert operations to proto format
|
1097
|
+
proto_operations = []
|
1098
|
+
for op in operations:
|
1099
|
+
row_op = taple_service_pb2.RowOperation()
|
1100
|
+
|
1101
|
+
if 'create' in op:
|
1102
|
+
# Try setting fields directly on the nested message
|
1103
|
+
if 'position' in op['create']:
|
1104
|
+
row_op.create.position = op['create']['position']
|
1105
|
+
if 'height' in op['create']:
|
1106
|
+
row_op.create.height = op['create']['height']
|
1107
|
+
|
1108
|
+
elif 'update' in op:
|
1109
|
+
# Set fields directly on the nested message
|
1110
|
+
row_op.update.row_key = op['update']['row_key']
|
1111
|
+
if 'position' in op['update']:
|
1112
|
+
row_op.update.position = op['update']['position']
|
1113
|
+
if 'height' in op['update']:
|
1114
|
+
row_op.update.height = op['update']['height']
|
1115
|
+
if 'hidden' in op['update']:
|
1116
|
+
row_op.update.hidden = op['update']['hidden']
|
1117
|
+
|
1118
|
+
elif 'delete' in op:
|
1119
|
+
# Set fields directly on the nested message
|
1120
|
+
row_op.delete.row_key = op['delete']['row_key']
|
1121
|
+
|
1122
|
+
proto_operations.append(row_op)
|
1123
|
+
|
1124
|
+
request = taple_service_pb2.BatchEditRowsRequest(
|
1125
|
+
sheet_id=sheet_id,
|
1126
|
+
sheet_version=sheet_version,
|
1127
|
+
client_id=client_id,
|
1128
|
+
operations=proto_operations
|
1129
|
+
)
|
1130
|
+
if idempotency_key is not None:
|
1131
|
+
request.idempotency_key = idempotency_key
|
1132
|
+
|
1133
|
+
try:
|
1134
|
+
response = stub.BatchEditRows(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1135
|
+
except Exception as e:
|
1136
|
+
# 如果发生异常,返回错误信息
|
1137
|
+
return {
|
1138
|
+
'success': False,
|
1139
|
+
'error_message': str(e),
|
1140
|
+
'current_version': sheet_version,
|
1141
|
+
'results': [],
|
1142
|
+
'conflict_info': None
|
1143
|
+
}
|
1144
|
+
|
1145
|
+
from ...schemas.taple import ConflictInfo
|
1146
|
+
|
1147
|
+
conflict_info = None
|
1148
|
+
if response.conflict_info:
|
1149
|
+
conflict_info = ConflictInfo(
|
1150
|
+
has_conflict=response.conflict_info.has_conflict,
|
1151
|
+
server_version=response.conflict_info.server_version,
|
1152
|
+
conflict_type=response.conflict_info.conflict_type,
|
1153
|
+
conflicted_columns=list(response.conflict_info.conflicted_columns),
|
1154
|
+
resolution_suggestion=response.conflict_info.resolution_suggestion
|
1155
|
+
)
|
1156
|
+
|
1157
|
+
# Return raw response for now, can create proper schema later
|
1158
|
+
return {
|
1159
|
+
'success': response.success if hasattr(response, 'success') else False,
|
1160
|
+
'current_version': response.current_version if hasattr(response, 'current_version') else sheet_version,
|
1161
|
+
'results': [
|
1162
|
+
{
|
1163
|
+
'success': result.success if hasattr(result, 'success') else False,
|
1164
|
+
'row': self._convert_row(result.row) if hasattr(result, 'row') and result.row else None,
|
1165
|
+
'error_message': result.error_message if hasattr(result, 'error_message') else None,
|
1166
|
+
'operation_type': result.operation_type if hasattr(result, 'operation_type') else None
|
1167
|
+
} for result in (response.results if hasattr(response, 'results') else [])
|
1168
|
+
],
|
1169
|
+
'error_message': response.error_message if hasattr(response, 'error_message') else None,
|
1170
|
+
'conflict_info': conflict_info
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
@retry_with_backoff(max_retries=3)
|
1174
|
+
@retry_on_lock_conflict()
|
1175
|
+
def batch_edit_cells(
|
1176
|
+
self,
|
1177
|
+
sheet_id: str,
|
1178
|
+
operations: List[Dict[str, Any]],
|
1179
|
+
*,
|
1180
|
+
sheet_version: Optional[int] = None,
|
1181
|
+
client_id: Optional[str] = None,
|
1182
|
+
idempotency_key: Optional[str] = None,
|
1183
|
+
request_id: Optional[str] = None,
|
1184
|
+
**metadata
|
1185
|
+
) -> Any:
|
1186
|
+
"""
|
1187
|
+
Execute batch cell operations.
|
1188
|
+
|
1189
|
+
Args:
|
1190
|
+
sheet_id: Sheet ID
|
1191
|
+
operations: List of cell operations
|
1192
|
+
sheet_version: Version for optimistic locking (optional)
|
1193
|
+
client_id: Client ID (optional)
|
1194
|
+
idempotency_key: 幂等性键(可选)
|
1195
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1196
|
+
**metadata: Extra metadata
|
1197
|
+
|
1198
|
+
Returns:
|
1199
|
+
BatchEditCellsResponse
|
1200
|
+
"""
|
1201
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1202
|
+
import uuid
|
1203
|
+
|
1204
|
+
# 如果没有提供sheet_version,自动获取
|
1205
|
+
if sheet_version is None:
|
1206
|
+
version_result = self.get_sheet_version(sheet_id=sheet_id, **metadata)
|
1207
|
+
sheet_version = version_result.version
|
1208
|
+
|
1209
|
+
# 如果没有提供client_id,自动生成
|
1210
|
+
if client_id is None:
|
1211
|
+
client_id = str(uuid.uuid4())
|
1212
|
+
|
1213
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1214
|
+
|
1215
|
+
# Convert operations to proto format
|
1216
|
+
proto_operations = []
|
1217
|
+
for op in operations:
|
1218
|
+
cell_op = taple_service_pb2.CellOperation()
|
1219
|
+
|
1220
|
+
if 'edit' in op:
|
1221
|
+
edit_data = taple_service_pb2.EditCellData(
|
1222
|
+
column_key=op['edit']['column_key'],
|
1223
|
+
row_key=op['edit']['row_key']
|
1224
|
+
)
|
1225
|
+
if 'raw_value' in op['edit']:
|
1226
|
+
edit_data.raw_value = op['edit']['raw_value']
|
1227
|
+
if 'formatted_value' in op['edit']:
|
1228
|
+
edit_data.formatted_value = op['edit']['formatted_value']
|
1229
|
+
if 'formula' in op['edit']:
|
1230
|
+
edit_data.formula = op['edit']['formula']
|
1231
|
+
if 'data_type' in op['edit']:
|
1232
|
+
edit_data.data_type = op['edit']['data_type']
|
1233
|
+
if 'styles' in op['edit']:
|
1234
|
+
edit_data.styles.CopyFrom(self._convert_dict_to_struct(op['edit']['styles']))
|
1235
|
+
cell_op.edit.CopyFrom(edit_data)
|
1236
|
+
|
1237
|
+
elif 'clear' in op:
|
1238
|
+
clear_data = taple_service_pb2.ClearCellData(
|
1239
|
+
column_key=op['clear']['column_key'],
|
1240
|
+
row_key=op['clear']['row_key']
|
1241
|
+
)
|
1242
|
+
cell_op.clear.CopyFrom(clear_data)
|
1243
|
+
|
1244
|
+
elif 'delete' in op:
|
1245
|
+
delete_data = taple_service_pb2.DeleteCellData(
|
1246
|
+
column_key=op['delete']['column_key'],
|
1247
|
+
row_key=op['delete']['row_key']
|
1248
|
+
)
|
1249
|
+
cell_op.delete.CopyFrom(delete_data)
|
1250
|
+
|
1251
|
+
proto_operations.append(cell_op)
|
1252
|
+
|
1253
|
+
request = taple_service_pb2.BatchEditCellsRequest(
|
1254
|
+
sheet_id=sheet_id,
|
1255
|
+
sheet_version=sheet_version,
|
1256
|
+
client_id=client_id,
|
1257
|
+
operations=proto_operations
|
1258
|
+
)
|
1259
|
+
if idempotency_key is not None:
|
1260
|
+
request.idempotency_key = idempotency_key
|
1261
|
+
|
1262
|
+
response = stub.BatchEditCells(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1263
|
+
|
1264
|
+
from ...schemas.taple import ConflictInfo
|
1265
|
+
|
1266
|
+
conflict_info = None
|
1267
|
+
if response.conflict_info:
|
1268
|
+
conflict_info = ConflictInfo(
|
1269
|
+
has_conflict=response.conflict_info.has_conflict,
|
1270
|
+
server_version=response.conflict_info.server_version,
|
1271
|
+
conflict_type=response.conflict_info.conflict_type,
|
1272
|
+
conflicted_columns=list(response.conflict_info.conflicted_columns),
|
1273
|
+
resolution_suggestion=response.conflict_info.resolution_suggestion
|
1274
|
+
)
|
1275
|
+
|
1276
|
+
# Return raw response for now, can create proper schema later
|
1277
|
+
return {
|
1278
|
+
'success': response.success,
|
1279
|
+
'current_version': response.current_version,
|
1280
|
+
'results': [
|
1281
|
+
{
|
1282
|
+
'success': result.success,
|
1283
|
+
'cell': self._convert_cell(result.cell) if result.cell else None,
|
1284
|
+
'error_message': result.error_message,
|
1285
|
+
'operation_type': result.operation_type
|
1286
|
+
} for result in response.results
|
1287
|
+
],
|
1288
|
+
'error_message': response.error_message,
|
1289
|
+
'conflict_info': conflict_info
|
1290
|
+
}
|
1291
|
+
|
1292
|
+
@retry_with_backoff(max_retries=3)
|
1293
|
+
def get_column_data(
|
1294
|
+
self,
|
1295
|
+
sheet_id: str,
|
1296
|
+
column_key: str,
|
1297
|
+
request_id: Optional[str] = None,
|
1298
|
+
**metadata
|
1299
|
+
) -> Any:
|
1300
|
+
"""
|
1301
|
+
Get column data including all cells in the column.
|
1302
|
+
|
1303
|
+
Args:
|
1304
|
+
sheet_id: Sheet ID
|
1305
|
+
column_key: Column key
|
1306
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1307
|
+
**metadata: Extra metadata
|
1308
|
+
|
1309
|
+
Returns:
|
1310
|
+
ColumnDataResponse with column info and cells
|
1311
|
+
"""
|
1312
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1313
|
+
|
1314
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1315
|
+
|
1316
|
+
request = taple_service_pb2.GetColumnDataRequest(
|
1317
|
+
sheet_id=sheet_id,
|
1318
|
+
column_key=column_key
|
1319
|
+
)
|
1320
|
+
|
1321
|
+
response = stub.GetColumnData(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1322
|
+
|
1323
|
+
# Return raw response for now, can create proper schema later
|
1324
|
+
return {
|
1325
|
+
'column': self._convert_column(response.column) if response.column else None,
|
1326
|
+
'cells': [self._convert_cell(cell) for cell in response.cells]
|
1327
|
+
}
|
1328
|
+
|
1329
|
+
@retry_with_backoff(max_retries=3)
|
1330
|
+
def get_row_data(
|
1331
|
+
self,
|
1332
|
+
sheet_id: str,
|
1333
|
+
row_key: str,
|
1334
|
+
request_id: Optional[str] = None,
|
1335
|
+
**metadata
|
1336
|
+
) -> Any:
|
1337
|
+
"""
|
1338
|
+
Get row data including all cells in the row.
|
1339
|
+
|
1340
|
+
Args:
|
1341
|
+
sheet_id: Sheet ID
|
1342
|
+
row_key: Row key
|
1343
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1344
|
+
**metadata: Extra metadata
|
1345
|
+
|
1346
|
+
Returns:
|
1347
|
+
RowDataResponse with row info and cells
|
1348
|
+
"""
|
1349
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1350
|
+
|
1351
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1352
|
+
|
1353
|
+
request = taple_service_pb2.GetRowDataRequest(
|
1354
|
+
sheet_id=sheet_id,
|
1355
|
+
row_key=row_key
|
1356
|
+
)
|
1357
|
+
|
1358
|
+
response = stub.GetRowData(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1359
|
+
|
1360
|
+
# Return raw response for now, can create proper schema later
|
1361
|
+
return {
|
1362
|
+
'row': self._convert_row(response.row) if response.row else None,
|
1363
|
+
'cells': [self._convert_cell(cell) for cell in response.cells]
|
1364
|
+
}
|
1365
|
+
|
1366
|
+
@retry_with_backoff(max_retries=3)
|
1367
|
+
def get_cell_data(
|
1368
|
+
self,
|
1369
|
+
sheet_id: str,
|
1370
|
+
column_key: str,
|
1371
|
+
row_key: str,
|
1372
|
+
request_id: Optional[str] = None,
|
1373
|
+
**metadata
|
1374
|
+
) -> Any:
|
1375
|
+
"""
|
1376
|
+
Get cell data.
|
1377
|
+
|
1378
|
+
Args:
|
1379
|
+
sheet_id: Sheet ID
|
1380
|
+
column_key: Column key
|
1381
|
+
row_key: Row key
|
1382
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1383
|
+
**metadata: Extra metadata
|
1384
|
+
|
1385
|
+
Returns:
|
1386
|
+
CellDataResponse with cell info
|
1387
|
+
"""
|
1388
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1389
|
+
|
1390
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1391
|
+
|
1392
|
+
request = taple_service_pb2.GetCellDataRequest(
|
1393
|
+
sheet_id=sheet_id,
|
1394
|
+
column_key=column_key,
|
1395
|
+
row_key=row_key
|
1396
|
+
)
|
1397
|
+
|
1398
|
+
response = stub.GetCellData(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1399
|
+
|
1400
|
+
# Return raw response for now, can create proper schema later
|
1401
|
+
return {
|
1402
|
+
'cell': self._convert_cell(response.cell) if response.cell else None
|
1403
|
+
}
|
1404
|
+
|
1405
|
+
@retry_with_backoff(max_retries=3)
|
1406
|
+
def get_sheet_version(
|
1407
|
+
self,
|
1408
|
+
sheet_id: str,
|
1409
|
+
request_id: Optional[str] = None,
|
1410
|
+
**metadata
|
1411
|
+
) -> Any:
|
1412
|
+
"""
|
1413
|
+
Get sheet version (lightweight).
|
1414
|
+
|
1415
|
+
Args:
|
1416
|
+
sheet_id: Sheet ID
|
1417
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1418
|
+
**metadata: Extra metadata
|
1419
|
+
|
1420
|
+
Returns:
|
1421
|
+
GetSheetVersionResponse
|
1422
|
+
"""
|
1423
|
+
if not sheet_id:
|
1424
|
+
raise ValidationError("sheet_id 不能为空")
|
1425
|
+
|
1426
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1427
|
+
|
1428
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1429
|
+
|
1430
|
+
request = taple_service_pb2.GetSheetVersionRequest(sheet_id=sheet_id)
|
1431
|
+
response = stub.GetSheetVersion(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1432
|
+
|
1433
|
+
from ...schemas.taple import GetSheetVersionResponse
|
1434
|
+
return GetSheetVersionResponse(
|
1435
|
+
sheet_id=response.sheet_id,
|
1436
|
+
version=response.version,
|
1437
|
+
metadata=self._convert_sheet(response.metadata) if response.metadata else None
|
1438
|
+
)
|
1439
|
+
|
1440
|
+
@retry_with_backoff(max_retries=3)
|
1441
|
+
def get_sheet_data(
|
1442
|
+
self,
|
1443
|
+
sheet_id: str,
|
1444
|
+
version: Optional[int] = None,
|
1445
|
+
request_id: Optional[str] = None,
|
1446
|
+
**metadata
|
1447
|
+
) -> Any:
|
1448
|
+
"""
|
1449
|
+
Get complete sheet data.
|
1450
|
+
|
1451
|
+
Args:
|
1452
|
+
sheet_id: Sheet ID
|
1453
|
+
version: Optional version to get changes since
|
1454
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1455
|
+
**metadata: Extra metadata
|
1456
|
+
|
1457
|
+
Returns:
|
1458
|
+
GetSheetDataResponse
|
1459
|
+
"""
|
1460
|
+
if not sheet_id:
|
1461
|
+
raise ValidationError("sheet_id 不能为空")
|
1462
|
+
|
1463
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1464
|
+
|
1465
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1466
|
+
|
1467
|
+
request = taple_service_pb2.GetSheetDataRequest(sheet_id=sheet_id)
|
1468
|
+
if version is not None:
|
1469
|
+
request.version = version
|
1470
|
+
|
1471
|
+
response = stub.GetSheetData(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1472
|
+
|
1473
|
+
from ...schemas.taple import GetSheetDataResponse
|
1474
|
+
return GetSheetDataResponse(
|
1475
|
+
sheet_id=response.sheet_id,
|
1476
|
+
version=response.version,
|
1477
|
+
metadata=self._convert_sheet(response.metadata) if response.metadata else None,
|
1478
|
+
columns=[self._convert_column(col) for col in response.columns] if response.columns else [],
|
1479
|
+
rows=[self._convert_row(row) for row in response.rows] if response.rows else [],
|
1480
|
+
cells=[self._convert_cell(cell) for cell in response.cells] if response.cells else [],
|
1481
|
+
last_updated=timestamp_to_datetime(response.last_updated) if response.last_updated else None
|
1482
|
+
)
|
1483
|
+
|
1484
|
+
@retry_with_backoff(max_retries=3)
|
1485
|
+
@retry_on_lock_conflict()
|
1486
|
+
def batch_edit_sheet(
|
1487
|
+
self,
|
1488
|
+
sheet_id: str,
|
1489
|
+
operations: List[Any],
|
1490
|
+
sheet_version: int,
|
1491
|
+
client_id: str,
|
1492
|
+
*,
|
1493
|
+
idempotency_key: Optional[str] = None,
|
1494
|
+
request_id: Optional[str] = None,
|
1495
|
+
**metadata
|
1496
|
+
) -> Any:
|
1497
|
+
"""
|
1498
|
+
Execute batch operations on a sheet.
|
1499
|
+
|
1500
|
+
Args:
|
1501
|
+
sheet_id: Sheet ID
|
1502
|
+
operations: List of operations
|
1503
|
+
sheet_version: Current version for optimistic locking
|
1504
|
+
client_id: Client ID
|
1505
|
+
idempotency_key: 幂等性键(可选)
|
1506
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1507
|
+
**metadata: Extra metadata
|
1508
|
+
|
1509
|
+
Returns:
|
1510
|
+
BatchEditSheetResponse
|
1511
|
+
"""
|
1512
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1513
|
+
|
1514
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1515
|
+
|
1516
|
+
request = taple_service_pb2.BatchEditSheetRequest(
|
1517
|
+
sheet_id=sheet_id,
|
1518
|
+
operations=operations,
|
1519
|
+
sheet_version=sheet_version,
|
1520
|
+
client_id=client_id
|
1521
|
+
)
|
1522
|
+
|
1523
|
+
if idempotency_key is not None:
|
1524
|
+
request.idempotency_key = idempotency_key
|
1525
|
+
|
1526
|
+
response = stub.BatchEditSheet(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1527
|
+
|
1528
|
+
from ...schemas.taple import BatchEditSheetResponse, ConflictInfo
|
1529
|
+
|
1530
|
+
conflict_info = None
|
1531
|
+
if response.conflict_info:
|
1532
|
+
conflict_info = ConflictInfo(
|
1533
|
+
has_conflict=response.conflict_info.has_conflict,
|
1534
|
+
server_version=response.conflict_info.server_version,
|
1535
|
+
conflict_type=response.conflict_info.conflict_type,
|
1536
|
+
conflicted_columns=list(response.conflict_info.conflicted_columns),
|
1537
|
+
resolution_suggestion=response.conflict_info.resolution_suggestion
|
1538
|
+
)
|
1539
|
+
|
1540
|
+
return BatchEditSheetResponse(
|
1541
|
+
success=response.success,
|
1542
|
+
batch_id=response.batch_id,
|
1543
|
+
current_version=response.current_version,
|
1544
|
+
results=list(response.results),
|
1545
|
+
error_message=response.error_message,
|
1546
|
+
conflict_info=conflict_info
|
1547
|
+
)
|
1548
|
+
|
1549
|
+
@retry_with_backoff(max_retries=3)
|
1550
|
+
def clone_table_data(
|
1551
|
+
self,
|
1552
|
+
source_table_id: str,
|
1553
|
+
target_org_id: str,
|
1554
|
+
target_user_id: str,
|
1555
|
+
*,
|
1556
|
+
target_folder_id: Optional[str] = None,
|
1557
|
+
new_table_name: Optional[str] = None,
|
1558
|
+
include_views: bool = False,
|
1559
|
+
idempotency_key: Optional[str] = None,
|
1560
|
+
request_id: Optional[str] = None,
|
1561
|
+
**metadata
|
1562
|
+
) -> 'CloneTableDataResponse':
|
1563
|
+
"""
|
1564
|
+
Clone table data to another organization.
|
1565
|
+
|
1566
|
+
Args:
|
1567
|
+
source_table_id: Source table ID
|
1568
|
+
target_org_id: Target organization ID
|
1569
|
+
target_user_id: Target user ID
|
1570
|
+
target_folder_id: Target folder ID (optional)
|
1571
|
+
new_table_name: New table name (optional, defaults to original name + Copy)
|
1572
|
+
include_views: Whether to include view data (default: False)
|
1573
|
+
idempotency_key: Idempotency key (optional)
|
1574
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1575
|
+
**metadata: Extra metadata
|
1576
|
+
|
1577
|
+
Returns:
|
1578
|
+
CloneTableDataResponse with clone operation result
|
1579
|
+
"""
|
1580
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1581
|
+
from ...schemas.taple import CloneTableDataResponse
|
1582
|
+
|
1583
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1584
|
+
|
1585
|
+
request = taple_service_pb2.CloneTableDataRequest(
|
1586
|
+
source_table_id=source_table_id,
|
1587
|
+
target_org_id=target_org_id,
|
1588
|
+
target_user_id=target_user_id
|
1589
|
+
)
|
1590
|
+
|
1591
|
+
if target_folder_id:
|
1592
|
+
request.target_folder_id = target_folder_id
|
1593
|
+
if new_table_name:
|
1594
|
+
request.new_table_name = new_table_name
|
1595
|
+
if include_views is not None:
|
1596
|
+
request.include_views = include_views
|
1597
|
+
if idempotency_key:
|
1598
|
+
request.idempotency_key = idempotency_key
|
1599
|
+
|
1600
|
+
response = stub.CloneTableData(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1601
|
+
|
1602
|
+
return CloneTableDataResponse(
|
1603
|
+
success=response.success,
|
1604
|
+
new_table_id=response.new_table_id,
|
1605
|
+
new_file_id=response.new_file_id,
|
1606
|
+
sheets_cloned=response.sheets_cloned,
|
1607
|
+
cells_cloned=response.cells_cloned,
|
1608
|
+
error_message=response.error_message if response.error_message else None,
|
1609
|
+
created_at=timestamp_to_datetime(response.created_at)
|
1610
|
+
)
|
1611
|
+
|
1612
|
+
@retry_with_backoff(max_retries=3)
|
1613
|
+
def export_table_data(
|
1614
|
+
self,
|
1615
|
+
table_id: str,
|
1616
|
+
format: 'ExportFormat',
|
1617
|
+
*,
|
1618
|
+
sheet_ids: Optional[List[str]] = None,
|
1619
|
+
options: Optional[Dict[str, Any]] = None,
|
1620
|
+
idempotency_key: Optional[str] = None,
|
1621
|
+
request_id: Optional[str] = None,
|
1622
|
+
**metadata
|
1623
|
+
) -> 'ExportTableDataResponse':
|
1624
|
+
"""
|
1625
|
+
Export table data to file.
|
1626
|
+
|
1627
|
+
Args:
|
1628
|
+
table_id: Table ID to export
|
1629
|
+
format: Export format (EXCEL, CSV, JSON)
|
1630
|
+
sheet_ids: List of sheet IDs to export (optional, empty means all)
|
1631
|
+
options: Export options dict
|
1632
|
+
idempotency_key: Idempotency key (optional)
|
1633
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1634
|
+
**metadata: Extra metadata
|
1635
|
+
|
1636
|
+
Returns:
|
1637
|
+
ExportTableDataResponse with export result
|
1638
|
+
"""
|
1639
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1640
|
+
from ...schemas.taple import ExportTableDataResponse
|
1641
|
+
from google.protobuf import struct_pb2
|
1642
|
+
|
1643
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1644
|
+
|
1645
|
+
# Convert format enum
|
1646
|
+
format_map = {
|
1647
|
+
ExportFormat.XLSX: taple_service_pb2.EXPORT_FORMAT_EXCEL,
|
1648
|
+
ExportFormat.CSV: taple_service_pb2.EXPORT_FORMAT_CSV,
|
1649
|
+
ExportFormat.JSON: taple_service_pb2.EXPORT_FORMAT_JSON,
|
1650
|
+
}
|
1651
|
+
|
1652
|
+
request = taple_service_pb2.ExportTableDataRequest(
|
1653
|
+
table_id=table_id,
|
1654
|
+
format=format_map.get(format, taple_service_pb2.EXPORT_FORMAT_UNSPECIFIED)
|
1655
|
+
)
|
1656
|
+
|
1657
|
+
if sheet_ids:
|
1658
|
+
request.sheet_ids.extend(sheet_ids)
|
1659
|
+
|
1660
|
+
if options:
|
1661
|
+
# Create ExportOptions
|
1662
|
+
export_options = taple_service_pb2.ExportOptions()
|
1663
|
+
if 'include_formulas' in options:
|
1664
|
+
export_options.include_formulas = options['include_formulas']
|
1665
|
+
if 'include_styles' in options:
|
1666
|
+
export_options.include_styles = options['include_styles']
|
1667
|
+
if 'include_hidden_sheets' in options:
|
1668
|
+
export_options.include_hidden_sheets = options['include_hidden_sheets']
|
1669
|
+
if 'include_hidden_rows_cols' in options:
|
1670
|
+
export_options.include_hidden_rows_cols = options['include_hidden_rows_cols']
|
1671
|
+
if 'date_format' in options:
|
1672
|
+
export_options.date_format = options['date_format']
|
1673
|
+
if 'csv_delimiter' in options:
|
1674
|
+
export_options.csv_delimiter = options['csv_delimiter']
|
1675
|
+
if 'csv_encoding' in options:
|
1676
|
+
export_options.csv_encoding = options['csv_encoding']
|
1677
|
+
request.options.CopyFrom(export_options)
|
1678
|
+
|
1679
|
+
if idempotency_key:
|
1680
|
+
request.idempotency_key = idempotency_key
|
1681
|
+
|
1682
|
+
response = stub.ExportTableData(request, metadata=self.client.build_metadata(request_id=request_id, **metadata))
|
1683
|
+
|
1684
|
+
return ExportTableDataResponse(
|
1685
|
+
success=response.success,
|
1686
|
+
export_id=response.export_id,
|
1687
|
+
file_url=response.file_url,
|
1688
|
+
download_url=response.download_url,
|
1689
|
+
file_size=response.file_size,
|
1690
|
+
file_name=response.file_name,
|
1691
|
+
format=format.value if hasattr(format, 'value') else str(format), # Convert enum to string
|
1692
|
+
sheets_exported=response.sheets_exported,
|
1693
|
+
error_message=response.error_message if response.error_message else None,
|
1694
|
+
created_at=timestamp_to_datetime(response.created_at),
|
1695
|
+
expires_at=timestamp_to_datetime(response.expires_at)
|
1696
|
+
)
|
1697
|
+
|
1698
|
+
@retry_with_backoff(max_retries=3)
|
1699
|
+
def import_table_data(
|
1700
|
+
self,
|
1701
|
+
file_id: str,
|
1702
|
+
*,
|
1703
|
+
target_table_id: Optional[str] = None,
|
1704
|
+
table_name: Optional[str] = None,
|
1705
|
+
folder_id: Optional[str] = None,
|
1706
|
+
import_mode: str = "APPEND",
|
1707
|
+
skip_first_row: bool = True,
|
1708
|
+
auto_detect_types: bool = True,
|
1709
|
+
clear_existing_data: bool = False,
|
1710
|
+
column_mapping: Optional[Dict[str, str]] = None,
|
1711
|
+
date_format: str = "YYYY-MM-DD",
|
1712
|
+
csv_delimiter: str = ",",
|
1713
|
+
csv_encoding: str = "UTF-8",
|
1714
|
+
max_rows: int = 0,
|
1715
|
+
idempotency_key: Optional[str] = None,
|
1716
|
+
request_id: Optional[str] = None,
|
1717
|
+
**metadata
|
1718
|
+
) -> 'ImportTableDataResponse':
|
1719
|
+
"""
|
1720
|
+
导入文件数据到表格
|
1721
|
+
|
1722
|
+
Args:
|
1723
|
+
file_id: 要导入的文件ID
|
1724
|
+
target_table_id: 目标表格ID(可选,不提供则创建新表格)
|
1725
|
+
table_name: 表格名称(仅在创建新表格时使用)
|
1726
|
+
folder_id: 文件夹ID(仅在创建新表格时使用)
|
1727
|
+
import_mode: 导入模式(APPEND/REPLACE/MERGE)
|
1728
|
+
skip_first_row: 是否跳过第一行(标题行)
|
1729
|
+
auto_detect_types: 是否自动检测列类型
|
1730
|
+
clear_existing_data: 是否清空现有数据(仅在导入到现有表格时)
|
1731
|
+
column_mapping: 列映射(源列名 -> 目标列名)
|
1732
|
+
date_format: 日期格式
|
1733
|
+
csv_delimiter: CSV分隔符
|
1734
|
+
csv_encoding: CSV编码
|
1735
|
+
max_rows: 最大导入行数限制(0表示无限制)
|
1736
|
+
idempotency_key: 幂等性键
|
1737
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1738
|
+
**metadata: 额外的元数据
|
1739
|
+
|
1740
|
+
Returns:
|
1741
|
+
ImportTableDataResponse
|
1742
|
+
"""
|
1743
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1744
|
+
from ...schemas.taple import ImportTableDataResponse
|
1745
|
+
|
1746
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1747
|
+
|
1748
|
+
# 构建导入选项
|
1749
|
+
import_options = taple_service_pb2.ImportOptions(
|
1750
|
+
import_mode=self._get_import_mode_enum(import_mode),
|
1751
|
+
skip_first_row=skip_first_row,
|
1752
|
+
auto_detect_types=auto_detect_types,
|
1753
|
+
clear_existing_data=clear_existing_data,
|
1754
|
+
date_format=date_format,
|
1755
|
+
csv_delimiter=csv_delimiter,
|
1756
|
+
csv_encoding=csv_encoding,
|
1757
|
+
max_rows=max_rows
|
1758
|
+
)
|
1759
|
+
|
1760
|
+
# 添加列映射
|
1761
|
+
if column_mapping:
|
1762
|
+
for source_col, target_col in column_mapping.items():
|
1763
|
+
import_options.column_mapping[source_col] = target_col
|
1764
|
+
|
1765
|
+
# 构建请求
|
1766
|
+
request = taple_service_pb2.ImportTableDataRequest(
|
1767
|
+
file_id=file_id,
|
1768
|
+
options=import_options
|
1769
|
+
)
|
1770
|
+
|
1771
|
+
if target_table_id:
|
1772
|
+
request.target_table_id = target_table_id
|
1773
|
+
if folder_id:
|
1774
|
+
request.folder_id = folder_id
|
1775
|
+
if table_name:
|
1776
|
+
request.table_name = table_name
|
1777
|
+
if idempotency_key:
|
1778
|
+
request.idempotency_key = idempotency_key
|
1779
|
+
|
1780
|
+
# 调用RPC
|
1781
|
+
response = stub.ImportTableData(
|
1782
|
+
request,
|
1783
|
+
metadata=self.client.build_metadata(request_id=request_id, **metadata)
|
1784
|
+
)
|
1785
|
+
|
1786
|
+
# 转换响应
|
1787
|
+
return ImportTableDataResponse(
|
1788
|
+
success=response.success,
|
1789
|
+
table_id=response.table_id,
|
1790
|
+
file_id=response.file_id if response.file_id else None,
|
1791
|
+
sheets_imported=response.sheets_imported,
|
1792
|
+
rows_imported=response.rows_imported,
|
1793
|
+
cells_imported=response.cells_imported,
|
1794
|
+
sheet_results=[
|
1795
|
+
{
|
1796
|
+
'sheet_name': result.sheet_name,
|
1797
|
+
'sheet_id': result.sheet_id,
|
1798
|
+
'rows_imported': result.rows_imported,
|
1799
|
+
'cells_imported': result.cells_imported,
|
1800
|
+
'success': result.success,
|
1801
|
+
'error_message': result.error_message if result.error_message else None
|
1802
|
+
}
|
1803
|
+
for result in response.sheet_results
|
1804
|
+
],
|
1805
|
+
error_message=response.error_message if response.error_message else None,
|
1806
|
+
warnings=[
|
1807
|
+
{
|
1808
|
+
'type': warning.type,
|
1809
|
+
'message': warning.message,
|
1810
|
+
'sheet_name': warning.sheet_name if warning.sheet_name else None,
|
1811
|
+
'row_number': warning.row_number if warning.row_number else None,
|
1812
|
+
'column_name': warning.column_name if warning.column_name else None
|
1813
|
+
}
|
1814
|
+
for warning in response.warnings
|
1815
|
+
],
|
1816
|
+
created_at=timestamp_to_datetime(response.created_at),
|
1817
|
+
processing_time_ms=response.processing_time_ms
|
1818
|
+
)
|
1819
|
+
|
1820
|
+
def _get_import_mode_enum(self, mode: str) -> int:
|
1821
|
+
"""将字符串导入模式转换为枚举值"""
|
1822
|
+
from ...rpc.gen import taple_service_pb2
|
1823
|
+
|
1824
|
+
mode_map = {
|
1825
|
+
"APPEND": taple_service_pb2.IMPORT_MODE_APPEND,
|
1826
|
+
"REPLACE": taple_service_pb2.IMPORT_MODE_REPLACE,
|
1827
|
+
"MERGE": taple_service_pb2.IMPORT_MODE_MERGE
|
1828
|
+
}
|
1829
|
+
|
1830
|
+
return mode_map.get(mode.upper(), taple_service_pb2.IMPORT_MODE_APPEND)
|
1831
|
+
|
1832
|
+
# Table view operations
|
1833
|
+
@retry_with_backoff(max_retries=3)
|
1834
|
+
def create_table_view(
|
1835
|
+
self,
|
1836
|
+
sheet_id: str,
|
1837
|
+
name: str,
|
1838
|
+
view_type: str,
|
1839
|
+
*,
|
1840
|
+
filter_criteria: Optional[Dict[str, Any]] = None,
|
1841
|
+
sort_criteria: Optional[Dict[str, Any]] = None,
|
1842
|
+
visible_columns: Optional[List[str]] = None,
|
1843
|
+
group_criteria: Optional[Dict[str, Any]] = None,
|
1844
|
+
is_hidden: bool = False,
|
1845
|
+
is_default: bool = False,
|
1846
|
+
config: Optional[Dict[str, Any]] = None,
|
1847
|
+
idempotency_key: Optional[str] = None,
|
1848
|
+
request_id: Optional[str] = None,
|
1849
|
+
**metadata
|
1850
|
+
) -> 'TableViewResponse':
|
1851
|
+
"""
|
1852
|
+
创建表格视图
|
1853
|
+
|
1854
|
+
Args:
|
1855
|
+
sheet_id: 所属工作表ID
|
1856
|
+
name: 视图名称
|
1857
|
+
view_type: 视图类型(table/gantt/calendar/kanban/gallery等)
|
1858
|
+
filter_criteria: 过滤条件(可选)
|
1859
|
+
sort_criteria: 排序条件(可选)
|
1860
|
+
visible_columns: 可见列列表(可选)
|
1861
|
+
group_criteria: 分组条件(可选)
|
1862
|
+
is_hidden: 是否隐藏(默认False)
|
1863
|
+
is_default: 是否默认视图(默认False)
|
1864
|
+
config: 扩展配置(可选)
|
1865
|
+
idempotency_key: 幂等性键(可选)
|
1866
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1867
|
+
**metadata: 额外的元数据
|
1868
|
+
|
1869
|
+
Returns:
|
1870
|
+
TableViewResponse
|
1871
|
+
"""
|
1872
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1873
|
+
from ...schemas.taple import TableViewResponse
|
1874
|
+
|
1875
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1876
|
+
|
1877
|
+
request = taple_service_pb2.CreateTableViewRequest(
|
1878
|
+
sheet_id=sheet_id,
|
1879
|
+
view_name=name,
|
1880
|
+
view_type=view_type,
|
1881
|
+
visible_columns=visible_columns or [],
|
1882
|
+
is_hidden=is_hidden,
|
1883
|
+
is_default=is_default,
|
1884
|
+
)
|
1885
|
+
|
1886
|
+
# 处理可选的 JSON 字段
|
1887
|
+
if filter_criteria:
|
1888
|
+
request.filter_criteria.CopyFrom(self._convert_dict_to_struct(filter_criteria))
|
1889
|
+
if sort_criteria:
|
1890
|
+
request.sort_criteria.CopyFrom(self._convert_dict_to_struct(sort_criteria))
|
1891
|
+
if group_criteria:
|
1892
|
+
request.group_criteria.CopyFrom(self._convert_dict_to_struct(group_criteria))
|
1893
|
+
if config:
|
1894
|
+
request.config.CopyFrom(self._convert_dict_to_struct(config))
|
1895
|
+
|
1896
|
+
response = stub.CreateTableView(
|
1897
|
+
request,
|
1898
|
+
metadata=self.client.build_metadata(request_id=request_id, **metadata)
|
1899
|
+
)
|
1900
|
+
|
1901
|
+
return TableViewResponse(view=self._convert_table_view(response.view))
|
1902
|
+
|
1903
|
+
@retry_with_backoff(max_retries=3)
|
1904
|
+
def get_table_view(
|
1905
|
+
self,
|
1906
|
+
view_id: str,
|
1907
|
+
request_id: Optional[str] = None,
|
1908
|
+
**metadata
|
1909
|
+
) -> 'TableViewResponse':
|
1910
|
+
"""
|
1911
|
+
获取表格视图
|
1912
|
+
|
1913
|
+
Args:
|
1914
|
+
view_id: 视图ID
|
1915
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1916
|
+
**metadata: 额外的元数据
|
1917
|
+
|
1918
|
+
Returns:
|
1919
|
+
TableViewResponse
|
1920
|
+
"""
|
1921
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1922
|
+
from ...schemas.taple import TableViewResponse
|
1923
|
+
|
1924
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1925
|
+
|
1926
|
+
request = taple_service_pb2.GetTableViewRequest(view_id=view_id)
|
1927
|
+
|
1928
|
+
response = stub.GetTableView(
|
1929
|
+
request,
|
1930
|
+
metadata=self.client.build_metadata(request_id=request_id, **metadata)
|
1931
|
+
)
|
1932
|
+
|
1933
|
+
return TableViewResponse(view=self._convert_table_view(response.view))
|
1934
|
+
|
1935
|
+
@retry_with_backoff(max_retries=3)
|
1936
|
+
def list_table_views(
|
1937
|
+
self,
|
1938
|
+
*,
|
1939
|
+
table_id: Optional[str] = None,
|
1940
|
+
sheet_id: Optional[str] = None,
|
1941
|
+
view_type: Optional[str] = None,
|
1942
|
+
request_id: Optional[str] = None,
|
1943
|
+
**metadata
|
1944
|
+
) -> 'ListTableViewsResponse':
|
1945
|
+
"""
|
1946
|
+
列出表格视图
|
1947
|
+
|
1948
|
+
Args:
|
1949
|
+
table_id: 按表格ID查询(可选)
|
1950
|
+
sheet_id: 按工作表ID查询(可选)
|
1951
|
+
view_type: 筛选视图类型(可选)
|
1952
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
1953
|
+
**metadata: 额外的元数据
|
1954
|
+
|
1955
|
+
Returns:
|
1956
|
+
ListTableViewsResponse
|
1957
|
+
"""
|
1958
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
1959
|
+
from ...schemas.taple import ListTableViewsResponse
|
1960
|
+
|
1961
|
+
if not table_id and not sheet_id:
|
1962
|
+
raise ValidationError("必须提供 table_id 或 sheet_id 之一")
|
1963
|
+
|
1964
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
1965
|
+
|
1966
|
+
request = taple_service_pb2.ListTableViewsRequest()
|
1967
|
+
|
1968
|
+
if table_id:
|
1969
|
+
request.table_id = table_id
|
1970
|
+
elif sheet_id:
|
1971
|
+
request.sheet_id = sheet_id
|
1972
|
+
|
1973
|
+
if view_type:
|
1974
|
+
request.view_type = view_type
|
1975
|
+
|
1976
|
+
response = stub.ListTableViews(
|
1977
|
+
request,
|
1978
|
+
metadata=self.client.build_metadata(request_id=request_id, **metadata)
|
1979
|
+
)
|
1980
|
+
|
1981
|
+
return ListTableViewsResponse(
|
1982
|
+
views=[self._convert_table_view(view) for view in response.views],
|
1983
|
+
total_count=response.total_count
|
1984
|
+
)
|
1985
|
+
|
1986
|
+
@retry_with_backoff(max_retries=3)
|
1987
|
+
def update_table_view(
|
1988
|
+
self,
|
1989
|
+
view_id: str,
|
1990
|
+
*,
|
1991
|
+
name: Optional[str] = None,
|
1992
|
+
filter_criteria: Optional[Dict[str, Any]] = None,
|
1993
|
+
sort_criteria: Optional[Dict[str, Any]] = None,
|
1994
|
+
visible_columns: Optional[List[str]] = None,
|
1995
|
+
group_criteria: Optional[Dict[str, Any]] = None,
|
1996
|
+
is_hidden: Optional[bool] = None,
|
1997
|
+
is_default: Optional[bool] = None,
|
1998
|
+
config: Optional[Dict[str, Any]] = None,
|
1999
|
+
idempotency_key: Optional[str] = None,
|
2000
|
+
request_id: Optional[str] = None,
|
2001
|
+
**metadata
|
2002
|
+
) -> 'TableViewResponse':
|
2003
|
+
"""
|
2004
|
+
更新表格视图
|
2005
|
+
|
2006
|
+
Args:
|
2007
|
+
view_id: 视图ID
|
2008
|
+
name: 新名称(可选)
|
2009
|
+
filter_criteria: 过滤条件(可选)
|
2010
|
+
sort_criteria: 排序条件(可选)
|
2011
|
+
visible_columns: 可见列列表(可选,传入空列表清空可见列设置)
|
2012
|
+
group_criteria: 分组条件(可选)
|
2013
|
+
is_hidden: 是否隐藏(可选)
|
2014
|
+
is_default: 是否默认视图(可选)
|
2015
|
+
config: 扩展配置(可选)
|
2016
|
+
idempotency_key: 幂等性键(可选)
|
2017
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
2018
|
+
**metadata: 额外的元数据
|
2019
|
+
|
2020
|
+
Returns:
|
2021
|
+
TableViewResponse
|
2022
|
+
"""
|
2023
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
2024
|
+
from ...schemas.taple import TableViewResponse
|
2025
|
+
from google.protobuf.struct_pb2 import ListValue
|
2026
|
+
|
2027
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
2028
|
+
|
2029
|
+
request = taple_service_pb2.UpdateTableViewRequest(view_id=view_id)
|
2030
|
+
|
2031
|
+
if name is not None:
|
2032
|
+
request.view_name = name
|
2033
|
+
|
2034
|
+
# 处理可选的 JSON 字段
|
2035
|
+
if filter_criteria is not None:
|
2036
|
+
request.filter_criteria.CopyFrom(self._convert_dict_to_struct(filter_criteria))
|
2037
|
+
if sort_criteria is not None:
|
2038
|
+
request.sort_criteria.CopyFrom(self._convert_dict_to_struct(sort_criteria))
|
2039
|
+
if visible_columns is not None:
|
2040
|
+
from google.protobuf.struct_pb2 import Value
|
2041
|
+
list_value = ListValue()
|
2042
|
+
for col in visible_columns:
|
2043
|
+
value = Value()
|
2044
|
+
value.string_value = str(col)
|
2045
|
+
list_value.values.append(value)
|
2046
|
+
request.visible_columns.CopyFrom(list_value)
|
2047
|
+
if group_criteria is not None:
|
2048
|
+
request.group_criteria.CopyFrom(self._convert_dict_to_struct(group_criteria))
|
2049
|
+
|
2050
|
+
# 处理布尔字段
|
2051
|
+
if is_hidden is not None:
|
2052
|
+
request.is_hidden = is_hidden
|
2053
|
+
if is_default is not None:
|
2054
|
+
request.is_default = is_default
|
2055
|
+
|
2056
|
+
if config is not None:
|
2057
|
+
request.config.CopyFrom(self._convert_dict_to_struct(config))
|
2058
|
+
|
2059
|
+
response = stub.UpdateTableView(
|
2060
|
+
request,
|
2061
|
+
metadata=self.client.build_metadata(request_id=request_id, **metadata)
|
2062
|
+
)
|
2063
|
+
|
2064
|
+
return TableViewResponse(view=self._convert_table_view(response.view))
|
2065
|
+
|
2066
|
+
@retry_with_backoff(max_retries=3)
|
2067
|
+
def delete_table_view(
|
2068
|
+
self,
|
2069
|
+
view_id: str,
|
2070
|
+
*,
|
2071
|
+
idempotency_key: Optional[str] = None,
|
2072
|
+
request_id: Optional[str] = None,
|
2073
|
+
**metadata
|
2074
|
+
) -> None:
|
2075
|
+
"""
|
2076
|
+
删除表格视图
|
2077
|
+
|
2078
|
+
Args:
|
2079
|
+
view_id: 视图ID
|
2080
|
+
idempotency_key: 幂等性键(可选)
|
2081
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
2082
|
+
**metadata: 额外的元数据
|
2083
|
+
"""
|
2084
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
2085
|
+
|
2086
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
2087
|
+
|
2088
|
+
request = taple_service_pb2.DeleteTableViewRequest(view_id=view_id)
|
2089
|
+
|
2090
|
+
stub.DeleteTableView(
|
2091
|
+
request,
|
2092
|
+
metadata=self.client.build_metadata(request_id=request_id, **metadata)
|
2093
|
+
)
|
2094
|
+
|
2095
|
+
@retry_with_backoff(max_retries=3)
|
2096
|
+
def update_table_view_config(
|
2097
|
+
self,
|
2098
|
+
view_id: str,
|
2099
|
+
config: Dict[str, Any],
|
2100
|
+
*,
|
2101
|
+
idempotency_key: Optional[str] = None,
|
2102
|
+
request_id: Optional[str] = None,
|
2103
|
+
**metadata
|
2104
|
+
) -> 'TableViewResponse':
|
2105
|
+
"""
|
2106
|
+
更新视图配置
|
2107
|
+
|
2108
|
+
Args:
|
2109
|
+
view_id: 视图ID
|
2110
|
+
config: 新配置
|
2111
|
+
idempotency_key: 幂等性键(可选)
|
2112
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
2113
|
+
**metadata: 额外的元数据
|
2114
|
+
|
2115
|
+
Returns:
|
2116
|
+
TableViewResponse
|
2117
|
+
"""
|
2118
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
2119
|
+
from ...schemas.taple import TableViewResponse
|
2120
|
+
|
2121
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
2122
|
+
|
2123
|
+
request = taple_service_pb2.UpdateTableViewConfigRequest(view_id=view_id)
|
2124
|
+
request.config.CopyFrom(self._convert_dict_to_struct(config))
|
2125
|
+
|
2126
|
+
response = stub.UpdateTableViewConfig(
|
2127
|
+
request,
|
2128
|
+
metadata=self.client.build_metadata(request_id=request_id, **metadata)
|
2129
|
+
)
|
2130
|
+
|
2131
|
+
return TableViewResponse(view=self._convert_table_view(response.view))
|
2132
|
+
|
2133
|
+
def _convert_table_view(self, proto_view) -> 'TableView':
|
2134
|
+
"""转换 proto TableView 到 Python TableView 模型"""
|
2135
|
+
from ...schemas.taple import TableView
|
2136
|
+
from google.protobuf.json_format import MessageToDict
|
2137
|
+
|
2138
|
+
# 处理 visible_columns 的转换
|
2139
|
+
visible_columns = None
|
2140
|
+
if proto_view.visible_columns:
|
2141
|
+
visible_columns = [str(col) for col in proto_view.visible_columns.values]
|
2142
|
+
|
2143
|
+
return TableView(
|
2144
|
+
id=proto_view.id,
|
2145
|
+
table_id=proto_view.table_id,
|
2146
|
+
sheet_id=proto_view.sheet_id,
|
2147
|
+
org_id=proto_view.org_id,
|
2148
|
+
user_id=proto_view.user_id,
|
2149
|
+
file_id=proto_view.file_id,
|
2150
|
+
|
2151
|
+
# 视图配置字段
|
2152
|
+
filter_criteria=MessageToDict(proto_view.filter_criteria) if proto_view.filter_criteria else None,
|
2153
|
+
sort_criteria=MessageToDict(proto_view.sort_criteria) if proto_view.sort_criteria else None,
|
2154
|
+
visible_columns=visible_columns,
|
2155
|
+
group_criteria=MessageToDict(proto_view.group_criteria) if proto_view.group_criteria else None,
|
2156
|
+
|
2157
|
+
# 创建者信息
|
2158
|
+
created_by_role=proto_view.created_by_role,
|
2159
|
+
created_by=proto_view.created_by,
|
2160
|
+
|
2161
|
+
# 视图基本信息
|
2162
|
+
view_name=proto_view.view_name,
|
2163
|
+
view_type=proto_view.view_type,
|
2164
|
+
|
2165
|
+
# 视图状态
|
2166
|
+
is_hidden=proto_view.is_hidden,
|
2167
|
+
is_default=proto_view.is_default,
|
2168
|
+
|
2169
|
+
# 扩展配置
|
2170
|
+
config=MessageToDict(proto_view.config) if proto_view.config else None,
|
2171
|
+
|
2172
|
+
# 时间戳
|
2173
|
+
created_at=timestamp_to_datetime(proto_view.created_at),
|
2174
|
+
updated_at=timestamp_to_datetime(proto_view.updated_at),
|
2175
|
+
deleted_at=timestamp_to_datetime(proto_view.deleted_at) if proto_view.deleted_at else None
|
2176
|
+
)
|
2177
|
+
|
2178
|
+
@retry_with_backoff(max_retries=3)
|
2179
|
+
def batch_create_table_views(
|
2180
|
+
self,
|
2181
|
+
sheet_id: str,
|
2182
|
+
views: List[Dict[str, Any]],
|
2183
|
+
*,
|
2184
|
+
request_id: Optional[str] = None,
|
2185
|
+
**metadata
|
2186
|
+
) -> 'BatchCreateTableViewsResponse':
|
2187
|
+
"""
|
2188
|
+
批量创建表格视图
|
2189
|
+
|
2190
|
+
Args:
|
2191
|
+
sheet_id: 所属工作表ID
|
2192
|
+
views: 要创建的视图列表,每个视图包含以下字段:
|
2193
|
+
- view_name: 视图名称
|
2194
|
+
- view_type: 视图类型
|
2195
|
+
- filter_criteria: 过滤条件(可选)
|
2196
|
+
- sort_criteria: 排序条件(可选)
|
2197
|
+
- visible_columns: 可见列列表(可选)
|
2198
|
+
- group_criteria: 分组条件(可选)
|
2199
|
+
- is_hidden: 是否隐藏(可选,默认False)
|
2200
|
+
- is_default: 是否默认视图(可选,默认False)
|
2201
|
+
- config: 扩展配置(可选)
|
2202
|
+
request_id: 请求ID(可选,如果不提供则自动生成)
|
2203
|
+
**metadata: 额外的元数据
|
2204
|
+
|
2205
|
+
Returns:
|
2206
|
+
BatchCreateTableViewsResponse
|
2207
|
+
"""
|
2208
|
+
from ...rpc.gen import taple_service_pb2, taple_service_pb2_grpc
|
2209
|
+
from ...schemas.taple import BatchCreateTableViewsResponse, BatchCreateTableViewResult
|
2210
|
+
|
2211
|
+
stub = self.client.get_stub(taple_service_pb2_grpc.TapleServiceStub)
|
2212
|
+
|
2213
|
+
# 构建请求
|
2214
|
+
request = taple_service_pb2.BatchCreateTableViewsRequest(sheet_id=sheet_id)
|
2215
|
+
|
2216
|
+
for view_data in views:
|
2217
|
+
view_req = taple_service_pb2.CreateTableViewData(
|
2218
|
+
view_name=view_data['view_name'],
|
2219
|
+
view_type=view_data['view_type'],
|
2220
|
+
visible_columns=view_data.get('visible_columns', []),
|
2221
|
+
is_hidden=view_data.get('is_hidden', False),
|
2222
|
+
is_default=view_data.get('is_default', False),
|
2223
|
+
)
|
2224
|
+
|
2225
|
+
# 处理可选的 JSON 字段
|
2226
|
+
if 'filter_criteria' in view_data:
|
2227
|
+
view_req.filter_criteria.CopyFrom(self._convert_dict_to_struct(view_data['filter_criteria']))
|
2228
|
+
if 'sort_criteria' in view_data:
|
2229
|
+
view_req.sort_criteria.CopyFrom(self._convert_dict_to_struct(view_data['sort_criteria']))
|
2230
|
+
if 'group_criteria' in view_data:
|
2231
|
+
view_req.group_criteria.CopyFrom(self._convert_dict_to_struct(view_data['group_criteria']))
|
2232
|
+
if 'config' in view_data:
|
2233
|
+
view_req.config.CopyFrom(self._convert_dict_to_struct(view_data['config']))
|
2234
|
+
|
2235
|
+
request.views.append(view_req)
|
2236
|
+
|
2237
|
+
response = stub.BatchCreateTableViews(
|
2238
|
+
request,
|
2239
|
+
metadata=self.client.build_metadata(request_id=request_id, **metadata)
|
2240
|
+
)
|
2241
|
+
|
2242
|
+
# 转换响应
|
2243
|
+
results = []
|
2244
|
+
for result in response.results:
|
2245
|
+
results.append(BatchCreateTableViewResult(
|
2246
|
+
success=result.success,
|
2247
|
+
view=self._convert_table_view(result.view) if result.view else None,
|
2248
|
+
error_message=result.error_message if result.error_message else None,
|
2249
|
+
view_name=result.view_name if result.view_name else None
|
2250
|
+
))
|
2251
|
+
|
2252
|
+
return BatchCreateTableViewsResponse(
|
2253
|
+
results=results,
|
2254
|
+
success_count=response.success_count,
|
2255
|
+
failed_count=response.failed_count
|
2256
|
+
)
|