openbayes-cli 0.4.5__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.
Files changed (55) hide show
  1. bayes/__init__.py +0 -0
  2. bayes/client/__init__.py +0 -0
  3. bayes/client/base.py +20 -0
  4. bayes/client/dataset_version_client.py +91 -0
  5. bayes/client/gear_client.py +468 -0
  6. bayes/client/job_run_client.py +504 -0
  7. bayes/client/job_upload_client.py +114 -0
  8. bayes/client/org_client.py +67 -0
  9. bayes/client/resource_client.py +108 -0
  10. bayes/client/runtime_client.py +32 -0
  11. bayes/client/ssh_client.py +66 -0
  12. bayes/client/status_client.py +14 -0
  13. bayes/client/user_client.py +58 -0
  14. bayes/commands/__init__.py +0 -0
  15. bayes/commands/gear.py +691 -0
  16. bayes/commands/org.py +100 -0
  17. bayes/commands/ssh.py +70 -0
  18. bayes/error.py +9 -0
  19. bayes/model/__init__.py +0 -0
  20. bayes/model/dataset_version.py +14 -0
  21. bayes/model/file/__init__.py +0 -0
  22. bayes/model/file/data_bindings.py +42 -0
  23. bayes/model/file/openbayes_data.py +133 -0
  24. bayes/model/file/openbayes_gear.py +72 -0
  25. bayes/model/file/openbayes_ignore.py +75 -0
  26. bayes/model/file/openbayes_yaml.py +180 -0
  27. bayes/model/file/settings.py +157 -0
  28. bayes/model/party.py +35 -0
  29. bayes/model/resource.py +147 -0
  30. bayes/model/runtime.py +34 -0
  31. bayes/root.py +170 -0
  32. bayes/templates/openbayes_zh-Hans.yaml +134 -0
  33. bayes/templates/openbayesignore.yaml +3 -0
  34. bayes/usercases/__init__.py +0 -0
  35. bayes/usercases/archive_usecase.py +79 -0
  36. bayes/usercases/auth_usecase.py +35 -0
  37. bayes/usercases/dataset_version_usecase.py +61 -0
  38. bayes/usercases/disk_usecase.py +98 -0
  39. bayes/usercases/gear_download_usecese.py +113 -0
  40. bayes/usercases/gear_logs_usecase.py +44 -0
  41. bayes/usercases/gear_open_usecase.py +16 -0
  42. bayes/usercases/gear_run_usecase.py +123 -0
  43. bayes/usercases/gear_upload_usecase.py +248 -0
  44. bayes/usercases/gear_usecase.py +301 -0
  45. bayes/usercases/openbayes_data_usecase.py +73 -0
  46. bayes/usercases/org_usecase.py +85 -0
  47. bayes/usercases/resource_usecase.py +74 -0
  48. bayes/usercases/runtime_usecase.py +58 -0
  49. bayes/usercases/ssh_usecase.py +110 -0
  50. bayes/usercases/switch_usercase.py +19 -0
  51. bayes/utils.py +163 -0
  52. openbayes_cli-0.4.5.dist-info/METADATA +62 -0
  53. openbayes_cli-0.4.5.dist-info/RECORD +55 -0
  54. openbayes_cli-0.4.5.dist-info/WHEEL +4 -0
  55. openbayes_cli-0.4.5.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,504 @@
1
+ import sys
2
+ from enum import Enum
3
+ from typing import Tuple, Optional, List
4
+
5
+ import pydantic
6
+ import typer
7
+ from gql.transport.exceptions import TransportQueryError
8
+ from pydantic import BaseModel, Field
9
+
10
+ from bayes.client.base import BayesGQLClient
11
+ from bayes.client.gear_client import Job, JobOutputBinding, DatasetBinding
12
+ from bayes.model.file.openbayes_yaml import ParameterSpec, HyperTuning, DEFAULT_PARALLEL_COUNT
13
+ from bayes.model.file.settings import DefaultJobModeTask, DefaultBatchJobModeTask, DefaultJobModeWorkSpace, \
14
+ DefaultBatchJobModeWorkSpace
15
+ from bayes.model.party import ModeEnum
16
+ from bayes.utils import Utils
17
+ from bayes.client.gear_client import DataBinding as job_dataBindings
18
+
19
+
20
+ class BindingAuthType(str, Enum):
21
+ READ_ONLY = "READ_ONLY"
22
+ READ_WRITE = "READ_WRITE"
23
+
24
+
25
+ class DataBinding(BaseModel):
26
+ path: str
27
+ name: str
28
+ bindingAuth: BindingAuthType
29
+
30
+
31
+ class BatchTask(BaseModel):
32
+ size: int
33
+ command: str
34
+ code: str
35
+
36
+
37
+ class NewTaskInput(BaseModel):
38
+ command: str
39
+ code: str
40
+
41
+
42
+ class NewWorkspaceInput(BaseModel):
43
+ code: str
44
+
45
+
46
+ class BatchWorkSpace(BaseModel):
47
+ code: str
48
+ size: int
49
+
50
+
51
+ class TagInput(BaseModel):
52
+ name: Optional[str] = None
53
+
54
+
55
+ class VariableInput(BaseModel):
56
+ mode: str
57
+ projectId: str
58
+ runtime: str
59
+ resource: str
60
+ newBatchTask: Optional[BatchTask] = None
61
+ newTask: Optional[NewTaskInput] = None
62
+ newWorkspace: Optional[NewWorkspaceInput] = None
63
+ newBatchWorkspace: Optional[BatchWorkSpace] = None
64
+ dataBindings: Optional[List[DataBinding]] = None
65
+ tagNames: Optional[List[TagInput]] = None
66
+
67
+
68
+ class Algorithm(Enum):
69
+ GRID = "Grid"
70
+ RANDOM = "Random"
71
+ BAYESIAN = "Bayesian"
72
+
73
+
74
+ class IntegerParameterSpecInput(BaseModel):
75
+ scaleType: str
76
+ minValue: int
77
+ maxValue: int
78
+
79
+
80
+ class DoubleParameterSpecInput(BaseModel):
81
+ scaleType: str
82
+ minValue: float
83
+ maxValue: float
84
+
85
+
86
+ class DiscreteParameterSpecInput(BaseModel):
87
+ discreteValues: List[float]
88
+
89
+
90
+ class CategoryParameterSpecInput(BaseModel):
91
+ categoricalValues: List[str]
92
+
93
+
94
+ class ParameterSpecInput(BaseModel):
95
+ name: str
96
+ integerParameterSpec: Optional[IntegerParameterSpecInput] = None
97
+ doubleParameterSpec: Optional[DoubleParameterSpecInput] = None
98
+ discreteParameterSpec: Optional[DiscreteParameterSpecInput] = None
99
+ categoryParameterSpec: Optional[CategoryParameterSpecInput] = None
100
+
101
+
102
+ class CreateHypertuningInput(BaseModel):
103
+ tagNames: Optional[List[TagInput]] = None
104
+ runtime: str
105
+ resource: str
106
+ projectId: Optional[str] = None
107
+ code: str
108
+ command: str
109
+ dataBindings: Optional[List[DataBinding]] = None
110
+ maxJobCount: int
111
+ parallelCount: int
112
+ hyperparameterMetric: str
113
+ sideMetrics: Optional[List[str]] = None
114
+ goal: str
115
+ algorithm: str
116
+ parameterSpecs: List[ParameterSpecInput]
117
+
118
+
119
+ def build_command(command: str, parameters: dict) -> str:
120
+ if not parameters:
121
+ return command
122
+ else:
123
+ param_str = " ".join([f"--{k}={v}" for k, v in parameters.items()])
124
+ return f"{command} {param_str}"
125
+
126
+
127
+ def get_graphql_binding_datasets(datasets: List[str]) -> List[DataBinding]:
128
+ result = []
129
+
130
+ for dataset in datasets:
131
+ list_parts = dataset.split(":")
132
+ if len(list_parts) == 2:
133
+ result.append(DataBinding(
134
+ name=list_parts[0],
135
+ path=list_parts[1],
136
+ bindingAuth=BindingAuthType.READ_ONLY
137
+ ))
138
+ elif len(list_parts) == 3:
139
+ auth = list_parts[2].upper()
140
+ if auth == "RO" or auth == "READ_ONLY":
141
+ result.append(DataBinding(
142
+ name=list_parts[0],
143
+ path=list_parts[1],
144
+ bindingAuth=BindingAuthType.READ_ONLY
145
+ ))
146
+ elif auth == "RW" or auth == "READ_WRITE":
147
+ result.append(DataBinding(
148
+ name=list_parts[0],
149
+ path=list_parts[1],
150
+ bindingAuth=BindingAuthType.READ_WRITE
151
+ ))
152
+
153
+ return result
154
+
155
+
156
+ def create(party_name, client: BayesGQLClient, mode, project_id, datasets: List[str], runtime, resource, code_id,
157
+ command, parameters,
158
+ node_count):
159
+ input_data = VariableInput(
160
+ mode=mode,
161
+ projectId=project_id,
162
+ runtime=runtime,
163
+ resource=resource
164
+ )
165
+ input_data.tagNames = [TagInput(name="BUSINESS_CHANNEL_ML")]
166
+
167
+ upper_mode = mode.upper()
168
+ if upper_mode == DefaultJobModeTask or upper_mode == DefaultBatchJobModeTask:
169
+ if node_count > 1:
170
+ input_data.newBatchTask = BatchTask(
171
+ size=node_count,
172
+ code=code_id,
173
+ command=build_command(command, parameters)
174
+ )
175
+ input_data.mode = "BATCH_TASK"
176
+ else:
177
+ input_data.mode = "TASK"
178
+ input_data.newTask = NewTaskInput(
179
+ code=code_id,
180
+ command=build_command(command, parameters)
181
+ )
182
+ elif upper_mode == DefaultJobModeWorkSpace or upper_mode == DefaultBatchJobModeWorkSpace:
183
+ if node_count > 1:
184
+ input_data.mode = "BATCH_WORKSPACE"
185
+ input_data.newBatchWorkspace = BatchWorkSpace(
186
+ code=code_id,
187
+ size=node_count
188
+ )
189
+ else:
190
+ input_data.mode = "WORKSPACE"
191
+ input_data.newWorkspace = NewWorkspaceInput(
192
+ code=code_id
193
+ )
194
+
195
+ if datasets and len(datasets) > 0:
196
+ input_data.dataBindings = get_graphql_binding_datasets(datasets)
197
+
198
+ query = """
199
+ mutation CreateJob($userId: String!, $input: CreateJobInput) {
200
+ createJob(userId: $userId, input: $input) {
201
+ id
202
+ links {
203
+ name
204
+ value
205
+ }
206
+ }
207
+ }
208
+ """
209
+
210
+ variables = {"userId": party_name, "input": input_data.model_dump()}
211
+ try:
212
+ response = client.exec(query, variables)
213
+ except Exception as e:
214
+ if hasattr(e, 'errors') and e.errors:
215
+ error_message = e.errors[0]['message']
216
+ else:
217
+ error_message = str(e)
218
+ print(error_message, file=sys.stderr) # 打印错误消息到标准错误流
219
+ raise ValueError(f"create job error: {error_message}")
220
+
221
+ result = response.get("createJob")
222
+ if result is None:
223
+ raise ValueError("create job result is none")
224
+
225
+ return Job(**result)
226
+
227
+
228
+ class UpdateJobInput(BaseModel):
229
+ description: Optional[str] = None
230
+
231
+
232
+ def convert_to_parameter_spec_input(spec):
233
+ if spec.type == 'DOUBLE':
234
+ return ParameterSpecInput(
235
+ name=spec.name,
236
+ doubleParameterSpec=DoubleParameterSpecInput(
237
+ scaleType=spec.scale_type,
238
+ minValue=spec.min_value,
239
+ maxValue=spec.max_value
240
+ )
241
+ )
242
+ elif spec.type == 'INTEGER':
243
+ return ParameterSpecInput(
244
+ name=spec.name,
245
+ integerParameterSpec=IntegerParameterSpecInput(
246
+ scaleType=spec.scale_type,
247
+ minValue=spec.min_value,
248
+ maxValue=spec.max_value
249
+ )
250
+ )
251
+ elif spec.type == 'DISCRETE':
252
+ return ParameterSpecInput(
253
+ name=spec.name,
254
+ discreteParameterSpec=DiscreteParameterSpecInput(
255
+ discreteValues=spec.discrete_values
256
+ )
257
+ )
258
+ elif spec.type == 'CATEGORICAL':
259
+ return ParameterSpecInput(
260
+ name=spec.name,
261
+ categoryParameterSpec=CategoryParameterSpecInput(
262
+ categoricalValues=spec.categorical_values
263
+ )
264
+ )
265
+ else:
266
+ raise ValueError(f"Unsupported parameter spec type: {spec.type}")
267
+
268
+
269
+ class BindingAuth(str, Enum):
270
+ READ_ONLY = "READ_ONLY"
271
+ READ_WRITE = "READ_WRITE"
272
+
273
+
274
+ class DataBindingInput(BaseModel):
275
+ name: str
276
+ path: str
277
+ bindingType: Optional[str] = None
278
+ bindingAuth: Optional[BindingAuth] = None
279
+
280
+
281
+ class JobParameterInput(BaseModel):
282
+ name: str
283
+ value: pydantic.Json
284
+
285
+
286
+ class RestartWorkspaceInput(BaseModel):
287
+ dataBindings: List[DataBindingInput]
288
+ runtime: str
289
+ resource: str
290
+ parameters: Optional[List[JobParameterInput]] = Field(default_factory=list)
291
+ useRDMADevices: bool = False
292
+
293
+
294
+ class RestartBatchWorkspaceInput(BaseModel):
295
+ dataBindings: List[DataBindingInput]
296
+ runtime: str
297
+ resource: str
298
+ parameters: Optional[List[JobParameterInput]] = Field(default_factory=list)
299
+ useRDMADevices: bool = False
300
+ size: Optional[int] = None
301
+
302
+
303
+ def create_hypertuning(party_name, client: BayesGQLClient, project_id, datasets, runtime, resource, code_id,
304
+ command, hyper_tuning: HyperTuning):
305
+ # 判断hyper_tuning的参数,不为空字符串和None
306
+ if Utils.is_empty_or_none(hyper_tuning.hyperparameter_metric) or Utils.is_empty_or_none(
307
+ hyper_tuning.algorithm) or Utils.is_empty_or_none(hyper_tuning.goal):
308
+ print("请检查输入的 hupertuning 参数")
309
+ raise typer.Exit(code=1)
310
+
311
+ if Utils.is_empty_or_none(command):
312
+ print("command 不能为空")
313
+ raise typer.Exit(code=1)
314
+
315
+ if Utils.is_empty_or_none(code_id):
316
+ print("code_id 不能为空")
317
+ raise typer.Exit(code=1)
318
+
319
+ hyper_tuning_algorithm = Algorithm(hyper_tuning.algorithm.capitalize())
320
+
321
+ parameter_specs = hyper_tuning.get_parameter_specs()
322
+ parameter_specs_input = [convert_to_parameter_spec_input(spec) for spec in parameter_specs]
323
+
324
+ input_data = CreateHypertuningInput(
325
+ projectId=project_id,
326
+ runtime=runtime,
327
+ resource=resource,
328
+ command=command,
329
+ code=code_id,
330
+ maxJobCount=hyper_tuning.max_job_count,
331
+ hyperparameterMetric=hyper_tuning.hyperparameter_metric,
332
+ goal=hyper_tuning.goal,
333
+ algorithm=hyper_tuning_algorithm,
334
+ parameterSpecs=parameter_specs_input,
335
+ sideMetrics=hyper_tuning.side_metrics,
336
+ parallelCount=max(DEFAULT_PARALLEL_COUNT, hyper_tuning.parallel_count)
337
+ )
338
+
339
+ if datasets and len(datasets) > 0:
340
+ input_data.dataBindings = get_graphql_binding_datasets(datasets)
341
+
342
+ input_data.tagNames = [TagInput(name="BUSINESS_CHANNEL_ML")]
343
+
344
+ query = """
345
+ mutation CreateHypertuning($userId: String!, $input: CreateHypertuningInput!) {
346
+ createHypertuning(userId: $userId, input: $input) {
347
+ id
348
+ links {
349
+ name
350
+ value
351
+ }
352
+ }
353
+ }
354
+ """
355
+ variables = {"userId": party_name, "input": input_data.model_dump()}
356
+
357
+ try:
358
+ response = client.exec(query, variables)
359
+ except TransportQueryError as e:
360
+ error_message = e.errors[0]['message'] if e.errors else str(e)
361
+ print(error_message, file=sys.stderr)
362
+ sys.exit(1)
363
+
364
+ result = response.get("createHypertuning")
365
+ if result is None:
366
+ raise ValueError("create hypertuning result is none")
367
+
368
+ return Job(**result)
369
+
370
+
371
+ def update_job_description(client: BayesGQLClient, party_name, job_id, message):
372
+ query = """
373
+ mutation UpdateJob($username: String!, $jobId: String!, $input: UpdateJobInput!) {
374
+ updateJob(userId: $username, jobId: $jobId, input: $input) {
375
+ id
376
+ }
377
+ }
378
+ """
379
+ input_data = UpdateJobInput(description=message)
380
+ variables = {"username": party_name, "jobId": job_id, "input": input_data.model_dump()}
381
+ try:
382
+ response = client.exec(query, variables)
383
+ except TransportQueryError as e:
384
+ error_message = e.errors[0]['message'] if e.errors else str(e)
385
+ print(error_message, file=sys.stderr)
386
+ sys.exit(1)
387
+
388
+ result = response.get("updateJob")
389
+ if result is None:
390
+ raise ValueError("updateJob result is none")
391
+
392
+ return Job(**result)
393
+
394
+
395
+ def get_restart_job_graphql_dataBindings(dataBindings: List[str]) -> DataBindingInput:
396
+ result = []
397
+ for dataBinding in dataBindings:
398
+ list_parts = dataBinding.split(":")
399
+ if len(list_parts) == 2:
400
+ name = list_parts[0]
401
+ path = list_parts[1]
402
+
403
+ binding_type = "OUTPUT" if name.endswith("/output") else None
404
+ result.append(DataBindingInput(
405
+ name=name,
406
+ path=path,
407
+ bindingAuth=BindingAuthType.READ_ONLY,
408
+ bindingType=binding_type
409
+ ))
410
+ elif len(list_parts) == 3:
411
+ name = list_parts[0]
412
+ path = list_parts[1]
413
+
414
+ binding_type = "OUTPUT" if name.endswith("/output") else None
415
+
416
+ auth = list_parts[2].upper()
417
+ if auth == "RO" or auth == "READ_ONLY":
418
+ result.append(DataBindingInput(
419
+ name=name,
420
+ path=path,
421
+ bindingAuth=BindingAuthType.READ_ONLY
422
+ ))
423
+ elif auth == "RW" or auth == "READ_WRITE":
424
+ result.append(DataBindingInput(
425
+ name=name,
426
+ path=path,
427
+ bindingAuth=BindingAuthType.READ_WRITE
428
+ ))
429
+
430
+ return result
431
+
432
+
433
+ def restart_workspace(client: BayesGQLClient, party_name, jid, runtime, resource, dataBindings, parameters):
434
+ query = """
435
+ mutation RestartWorkspace($userId: String!, $jobId: String!, $input: RestartWorkspaceInput!) {
436
+ restartWorkspace(userId: $userId, jobId: $jobId, input: $input) {
437
+ id
438
+ status
439
+ links {
440
+ name
441
+ value
442
+ }
443
+ }
444
+ }
445
+ """
446
+
447
+ input_data = RestartWorkspaceInput(
448
+ runtime=runtime,
449
+ resource=resource,
450
+ parameters=parameters,
451
+ dataBindings=get_restart_job_graphql_dataBindings(dataBindings) if dataBindings else []
452
+ )
453
+
454
+ variables = {"userId": party_name, "jobId": jid, "input": input_data.model_dump()}
455
+
456
+ try:
457
+ response = client.exec(query, variables)
458
+ except TransportQueryError as e:
459
+ error_message = e.errors[0]['message'] if e.errors else str(e)
460
+ print(error_message, file=sys.stderr)
461
+ sys.exit(1)
462
+
463
+ result = response.get("restartWorkspace")
464
+ if result is None:
465
+ raise ValueError("restartWorkspace result is none")
466
+
467
+ return Job(**result)
468
+
469
+
470
+ def restart_batch_workspace(client: BayesGQLClient, party_name, jid, runtime, resource, dataBindings, parameters):
471
+ query = """
472
+ mutation RestartBatchWorkspace($userId: String!, $jobId: String!, $input: RestartBatchWorkspaceInput!) {
473
+ restartBatchWorkspace(userId: $userId, jobId: $jobId, input: $input) {
474
+ id
475
+ status
476
+ links {
477
+ name
478
+ value
479
+ }
480
+ }
481
+ }
482
+ """
483
+
484
+ input_data = RestartBatchWorkspaceInput(
485
+ runtime=runtime,
486
+ resource=resource,
487
+ parameters=parameters,
488
+ dataBindings=get_restart_job_graphql_dataBindings(dataBindings) if dataBindings else []
489
+ )
490
+
491
+ variables = {"userId": party_name, "jobId": jid, "input": input_data.model_dump()}
492
+
493
+ try:
494
+ response = client.exec(query, variables)
495
+ except TransportQueryError as e:
496
+ error_message = e.errors[0]['message'] if e.errors else str(e)
497
+ print(error_message, file=sys.stderr)
498
+ sys.exit(1)
499
+
500
+ result = response.get("restartBatchWorkspace")
501
+ if result is None:
502
+ raise ValueError("restartBatchWorkspace result is none")
503
+
504
+ return Job(**result)
@@ -0,0 +1,114 @@
1
+ import logging
2
+ import os
3
+ import base64
4
+ import json
5
+ from typing import Optional, Tuple
6
+
7
+ import requests
8
+ from tusclient import client
9
+ from tusclient.storage import filestorage
10
+ from tqdm import tqdm
11
+ from tusclient.exceptions import TusCommunicationError
12
+
13
+ from pydantic import BaseModel
14
+
15
+ from bayes.error import Error
16
+ from bayes.model.file.settings import BayesEnvConfig, BayesSettings
17
+ from bayes.utils import Utils
18
+
19
+
20
+ class RequestUploadUrl(BaseModel):
21
+ upload_url: str
22
+ token: str
23
+
24
+
25
+ def upload_request() -> Tuple[Optional[RequestUploadUrl], Optional[Exception]]:
26
+ # https://beta.openbayes.com/api/users/Qion1/jobs/upload-request?protocol=tusd
27
+ default_env = BayesSettings().default_env
28
+ url = f"{default_env.endpoint}/api/users/{default_env.username}/jobs/upload-request?protocol=tusd"
29
+ print(f"upload_request url:{url}")
30
+ auth_token = default_env.token
31
+
32
+ try:
33
+ response = requests.post(url, headers={"Authorization": f"Bearer {auth_token}"})
34
+ except requests.RequestException as e:
35
+ return None, e
36
+
37
+ logging.info(response)
38
+
39
+ if response.status_code != 200:
40
+ err = request_failed(response.status_code)
41
+ return None, err
42
+
43
+ try:
44
+ result = response.json()
45
+ upload_request = RequestUploadUrl(**result)
46
+ return upload_request, None
47
+ except ValueError as e:
48
+ # JSON 解码错误
49
+ return None, e
50
+
51
+
52
+ def upload_file(
53
+ file_path: str, upload_url: str, token: str
54
+ ) -> Tuple[bool, Optional[str], Optional[Exception]]:
55
+ try:
56
+ my_client = client.TusClient(
57
+ upload_url, headers={"Authorization": f"Bearer {token}"}
58
+ )
59
+ file_size = os.path.getsize(file_path)
60
+
61
+ # Create a FileStorage instance for resumability
62
+ storage = filestorage.FileStorage(
63
+ os.path.join(os.path.dirname(file_path), ".tus_storage")
64
+ )
65
+
66
+ # Prepare metadata
67
+ filename = os.path.basename(file_path)
68
+ metadata = {"filename": filename}
69
+
70
+ with tqdm(total=file_size, unit="B", unit_scale=True, desc="Uploading") as pbar:
71
+ uploader = my_client.uploader(
72
+ file_path,
73
+ chunk_size=2 * 1024 * 1024,
74
+ store_url=True,
75
+ url_storage=storage,
76
+ upload_checksum=False,
77
+ metadata=metadata,
78
+ )
79
+
80
+ while uploader.offset < file_size:
81
+ uploader.upload_chunk()
82
+ pbar.update(uploader.offset - pbar.n)
83
+
84
+ print(f"File uploaded successfully: {file_path}")
85
+
86
+ # Remove filestorage after upload successfully
87
+ storage_path = os.path.join(os.path.dirname(file_path), ".tus_storage")
88
+ if os.path.exists(storage_path):
89
+ os.remove(storage_path)
90
+ print(f"Removed filestorage: {storage_path}")
91
+
92
+ # Decode the JWT token to get the payload
93
+ payload_part = token.split(".")[1]
94
+ padded_payload = payload_part + "=" * (4 - len(payload_part) % 4)
95
+ decoded_payload = base64.urlsafe_b64decode(padded_payload).decode("utf-8")
96
+ payload_data = json.loads(decoded_payload)
97
+ sub_payload = json.loads(payload_data["sub"])["payload"]
98
+
99
+ return True, sub_payload, None
100
+
101
+ except TusCommunicationError as e:
102
+ print(f"TUS Communication Error: {e}")
103
+ print(f"Response status: {e.status_code}")
104
+ print(f"Response body: {e.response_content}")
105
+ return False, None, e
106
+ except Exception as e:
107
+ print(f"Unexpected error: {e}")
108
+ return False, None, e
109
+
110
+
111
+ def request_failed(status_code: int):
112
+ if status_code >= 400:
113
+ return Exception(f"Request failed with status code {status_code}")
114
+ return None
@@ -0,0 +1,67 @@
1
+ from typing import List
2
+
3
+ from gql.transport.exceptions import TransportQueryError
4
+
5
+ from .base import BayesGQLClient
6
+
7
+ from pydantic import BaseModel
8
+ from bayes.error import Error
9
+
10
+
11
+ class OrgModel(BaseModel):
12
+ id: str
13
+ displayName: str
14
+
15
+
16
+ class UserOrgsModel(BaseModel):
17
+ role: str
18
+ org: OrgModel
19
+
20
+
21
+ def get_org(client: BayesGQLClient, org_id: str) -> OrgModel:
22
+ query = """
23
+ query Org($orgId: ID!) {
24
+ org(id: $orgId) {
25
+ id
26
+ displayName
27
+ }
28
+ }
29
+ """
30
+ variables = {"orgId": org_id}
31
+ try:
32
+ response = client.exec(query, variables)
33
+ except TransportQueryError as e:
34
+ raise Error(e.errors[0]['message'] if e.errors else str(e))
35
+
36
+ org_data = response.get("org")
37
+ if org_data is None:
38
+ raise Error("get org is none")
39
+
40
+ return OrgModel(**org_data)
41
+
42
+
43
+ def get_user_orgs(client: BayesGQLClient, username: str) -> List[UserOrgsModel]:
44
+ query = """
45
+ query Orgs($username: String!) {
46
+ user(username: $username) {
47
+ orgs {
48
+ role
49
+ org {
50
+ id
51
+ displayName
52
+ }
53
+ }
54
+ }
55
+ }
56
+ """
57
+ variables = {"username": username}
58
+ try:
59
+ response = client.exec(query, variables)
60
+ except TransportQueryError as e:
61
+ raise Error(e.errors[0]['message'] if e.errors else str(e))
62
+
63
+ user_orgs_data = response.get("user", {}).get("orgs")
64
+ if not user_orgs_data:
65
+ return None
66
+
67
+ return [UserOrgsModel(**org) for org in user_orgs_data]