supervisely 6.73.373__py3-none-any.whl → 6.73.375__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.
- supervisely/app/widgets/__init__.py +1 -0
- supervisely/app/widgets/run_app_button/run_app_button.py +22 -2
- supervisely/app/widgets/run_app_button/script.js +105 -45
- supervisely/app/widgets/run_app_button/template.html +5 -10
- supervisely/app/widgets/select_collection/__init__.py +0 -0
- supervisely/app/widgets/select_collection/select_collection.py +693 -0
- supervisely/app/widgets/select_collection/template.html +3 -0
- supervisely/app/widgets/train_val_splits/train_val_splits.py +111 -13
- supervisely/nn/training/gui/gui.py +28 -1
- supervisely/nn/training/gui/train_val_splits_selector.py +133 -30
- supervisely/nn/training/gui/training_logs.py +4 -1
- supervisely/nn/training/gui/utils.py +23 -0
- supervisely/nn/training/train_app.py +47 -5
- supervisely/project/pointcloud_episode_project.py +16 -0
- supervisely/project/pointcloud_project.py +16 -0
- supervisely/project/project.py +57 -0
- supervisely/project/video_project.py +16 -0
- supervisely/project/volume_project.py +16 -0
- {supervisely-6.73.373.dist-info → supervisely-6.73.375.dist-info}/METADATA +1 -1
- {supervisely-6.73.373.dist-info → supervisely-6.73.375.dist-info}/RECORD +24 -21
- {supervisely-6.73.373.dist-info → supervisely-6.73.375.dist-info}/LICENSE +0 -0
- {supervisely-6.73.373.dist-info → supervisely-6.73.375.dist-info}/WHEEL +0 -0
- {supervisely-6.73.373.dist-info → supervisely-6.73.375.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.373.dist-info → supervisely-6.73.375.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
from typing import Callable, Dict, List, Optional, Union
|
|
2
|
+
|
|
3
|
+
import supervisely.io.env as env
|
|
4
|
+
from supervisely.api.api import Api
|
|
5
|
+
from supervisely.app.widgets import Widget
|
|
6
|
+
from supervisely.app.widgets.checkbox.checkbox import Checkbox
|
|
7
|
+
from supervisely.app.widgets.container.container import Container
|
|
8
|
+
from supervisely.app.widgets.select.select import Select
|
|
9
|
+
from supervisely.app.widgets.tree_select.tree_select import TreeSelect
|
|
10
|
+
from supervisely.project.project_type import ProjectType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SelectCollection(Widget):
|
|
14
|
+
"""SelectCollection widget in Supervisely is a widget that allows users to select one or multiple collections (entities collections).
|
|
15
|
+
Read about it in `Developer Portal <https://developer.supervisely.com/app-development/widgets/selection/selectCollection>`_
|
|
16
|
+
(including screenshots and examples).
|
|
17
|
+
|
|
18
|
+
:param default_id: The ID of the collection to be selected by default.
|
|
19
|
+
:type default_id: Union[int, None]
|
|
20
|
+
:param project_id: The ID of the project to read collections from.
|
|
21
|
+
:type project_id: Union[int, None]
|
|
22
|
+
:param multiselect: Whether multiple collections can be selected.
|
|
23
|
+
:type multiselect: bool
|
|
24
|
+
:param compact: Whether the widget should be compact (e.g. no team, workspace, and project selectors).
|
|
25
|
+
:type compact: bool
|
|
26
|
+
:param select_all_collections: Whether all collections should be selected by default.
|
|
27
|
+
:type select_all_collections: bool
|
|
28
|
+
:param allowed_project_types: The list of project types that are allowed to be selected.
|
|
29
|
+
:type allowed_project_types: Optional[List[ProjectType]]
|
|
30
|
+
:param team_is_selectable: Whether the team selector should be selectable.
|
|
31
|
+
:type team_is_selectable: bool
|
|
32
|
+
:param workspace_is_selectable: Whether the workspace selector should be selectable.
|
|
33
|
+
:type workspace_is_selectable: bool
|
|
34
|
+
:param widget_id: The unique identifier of the widget.
|
|
35
|
+
:type widget_id: Union[str, None]
|
|
36
|
+
:param show_select_all_collections_checkbox: Whether the checkbox to select all collections should be shown.
|
|
37
|
+
:type show_select_all_collections_checkbox: bool
|
|
38
|
+
|
|
39
|
+
:Public methods:
|
|
40
|
+
- `set_project_id(project_id: int) -> None`: Set the project ID to read collections from.
|
|
41
|
+
- `get_selected_ids() -> Optional[List[int]]`: Get the IDs of the selected collections.
|
|
42
|
+
- `get_selected_id() -> Optional[int]`: Get the ID of the selected collection.
|
|
43
|
+
- `value_changed(func: Callable) -> Callable`: Decorator to set the callback function for the value changed event.
|
|
44
|
+
- `set_collection_id(collection_id: int) -> None`: Set the ID of the collection to be selected by default.
|
|
45
|
+
- `set_collection_ids(collection_ids: List[int]) -> None`: Set the IDs of the collections to be selected by default.
|
|
46
|
+
- `get_selected_project_id() -> Optional[int]`: Get the ID of the selected project.
|
|
47
|
+
- `get_selected_team_id() -> int`: Get the ID of the selected team.
|
|
48
|
+
- `set_team_id(team_id: int) -> None`: Set the team ID to read workspaces from.
|
|
49
|
+
- `get_selected_workspace_id() -> int`: Get the ID of the selected workspace.
|
|
50
|
+
- `set_workspace_id(workspace_id: int) -> None`: Set the workspace ID to read projects from.
|
|
51
|
+
- `is_all_selected() -> bool`: Check if all collections are selected.
|
|
52
|
+
- `select_all() -> None`: Select all collections.
|
|
53
|
+
- `disable() -> None`: Disable the widget in the UI.
|
|
54
|
+
- `enable() -> None`: Enable the widget in the UI.
|
|
55
|
+
- `set_selected_id(collection_id: int) -> None`: Set the ID of the collection to be selected by default.
|
|
56
|
+
- `set_selected_ids(collection_ids: List[int]) -> None`: Set the IDs of the collections to be selected by default.
|
|
57
|
+
|
|
58
|
+
:Properties:
|
|
59
|
+
- `team_id`: The ID of the team selected in the widget.
|
|
60
|
+
- `workspace_id`: The ID of the workspace selected in the widget.
|
|
61
|
+
- `project_id`: The ID of the project selected in the widget.
|
|
62
|
+
|
|
63
|
+
:Usage example:
|
|
64
|
+
|
|
65
|
+
.. code-block:: python
|
|
66
|
+
from supervisely.app.widgets import SelectCollection
|
|
67
|
+
|
|
68
|
+
project_id = 123
|
|
69
|
+
collection_id = 456
|
|
70
|
+
|
|
71
|
+
select_collection = SelectCollection(
|
|
72
|
+
default_id=collection_id,
|
|
73
|
+
project_id=project_id,
|
|
74
|
+
multiselect=True,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@select_collection.value_changed
|
|
78
|
+
def on_change(selected_ids):
|
|
79
|
+
print(selected_ids) # Output: [456, 789]
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
default_id: Union[int, None] = None,
|
|
85
|
+
project_id: Union[int, None] = None,
|
|
86
|
+
multiselect: bool = False,
|
|
87
|
+
compact: bool = False,
|
|
88
|
+
select_all_collections: bool = False,
|
|
89
|
+
allowed_project_types: Optional[List[ProjectType]] = None,
|
|
90
|
+
team_is_selectable: bool = True,
|
|
91
|
+
workspace_is_selectable: bool = True,
|
|
92
|
+
show_select_all_collections_checkbox: bool = True,
|
|
93
|
+
widget_id: Union[str, None] = None,
|
|
94
|
+
width: int = 193,
|
|
95
|
+
):
|
|
96
|
+
self._api = Api.from_env()
|
|
97
|
+
|
|
98
|
+
if default_id is not None and project_id is None:
|
|
99
|
+
raise ValueError("Project ID must be provided when default collection ID is set.")
|
|
100
|
+
|
|
101
|
+
if not multiselect and select_all_collections:
|
|
102
|
+
raise ValueError("Select all collections is only available in multiselect mode.")
|
|
103
|
+
|
|
104
|
+
# Reading team_id and workspace_id from environment variables.
|
|
105
|
+
# If not found, error will be raised.
|
|
106
|
+
self._team_id = env.team_id()
|
|
107
|
+
self._workspace_id = env.workspace_id()
|
|
108
|
+
|
|
109
|
+
# Using environment variables to set the default values if they are not provided.
|
|
110
|
+
self._project_id = project_id or env.project_id(raise_not_found=False)
|
|
111
|
+
self._collection_id = default_id
|
|
112
|
+
|
|
113
|
+
# Get mapping of collection ID to name for current project.
|
|
114
|
+
self._collections_names_map = None
|
|
115
|
+
self._collections_ids_map = None
|
|
116
|
+
|
|
117
|
+
self._multiselect = multiselect
|
|
118
|
+
self._compact = compact
|
|
119
|
+
|
|
120
|
+
# Extract values from Enum to match the .type property of the ProjectInfo object.
|
|
121
|
+
|
|
122
|
+
self._project_types = None
|
|
123
|
+
if allowed_project_types is not None:
|
|
124
|
+
if all(allowed_project_types) is isinstance(allowed_project_types, ProjectType):
|
|
125
|
+
self._project_types = (
|
|
126
|
+
[project_type.value for project_type in allowed_project_types]
|
|
127
|
+
if allowed_project_types is not None
|
|
128
|
+
else None
|
|
129
|
+
)
|
|
130
|
+
elif all(allowed_project_types) is isinstance(allowed_project_types, str):
|
|
131
|
+
self._project_types = allowed_project_types
|
|
132
|
+
|
|
133
|
+
# Widget components.
|
|
134
|
+
self._select_team = None
|
|
135
|
+
self._select_workspace = None
|
|
136
|
+
self._select_project = None
|
|
137
|
+
self._select_collection = None
|
|
138
|
+
self._select_all_collections_checkbox = None
|
|
139
|
+
self._width = width
|
|
140
|
+
|
|
141
|
+
# List of widgets will be used to create a Container.
|
|
142
|
+
self._widgets = []
|
|
143
|
+
|
|
144
|
+
if not compact:
|
|
145
|
+
# If the widget is not compact, create team, workspace, and project selectors.
|
|
146
|
+
self._create_selectors(team_is_selectable, workspace_is_selectable)
|
|
147
|
+
|
|
148
|
+
# Create the collection selector.
|
|
149
|
+
self._create_collection_selector(select_all_collections)
|
|
150
|
+
|
|
151
|
+
# Create the checkbox to select all collections if needed.
|
|
152
|
+
if show_select_all_collections_checkbox:
|
|
153
|
+
self._create_select_all_collections_checkbox(select_all_collections)
|
|
154
|
+
|
|
155
|
+
# Group the selectors and the collection selector into a container.
|
|
156
|
+
self._content = Container(self._widgets)
|
|
157
|
+
super().__init__(widget_id=widget_id, file_path=__file__)
|
|
158
|
+
|
|
159
|
+
def disable(self):
|
|
160
|
+
"""Disable the widget in the UI."""
|
|
161
|
+
for widget in self._widgets:
|
|
162
|
+
widget.disable()
|
|
163
|
+
|
|
164
|
+
def enable(self) -> None:
|
|
165
|
+
"""Enable the widget in the UI."""
|
|
166
|
+
for widget in self._widgets:
|
|
167
|
+
widget.enable()
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def team_id(self) -> int:
|
|
171
|
+
"""The ID of the team selected in the widget.
|
|
172
|
+
|
|
173
|
+
:return: The ID of the team.
|
|
174
|
+
:rtype: int
|
|
175
|
+
"""
|
|
176
|
+
return self._team_id
|
|
177
|
+
|
|
178
|
+
@team_id.setter
|
|
179
|
+
def team_id(self, team_id: int) -> None:
|
|
180
|
+
"""Set the team ID to read workspaces from.
|
|
181
|
+
|
|
182
|
+
:param team_id: The ID of the team.
|
|
183
|
+
:type team_id: int
|
|
184
|
+
"""
|
|
185
|
+
if not self._compact:
|
|
186
|
+
self._select_team.set_value(team_id)
|
|
187
|
+
self._select_workspace.set(self._get_select_items(team_id=team_id))
|
|
188
|
+
self._team_id = team_id
|
|
189
|
+
|
|
190
|
+
def get_selected_team_id(self) -> int:
|
|
191
|
+
"""Get the ID of the selected team.
|
|
192
|
+
|
|
193
|
+
:return: The ID of the selected team.
|
|
194
|
+
:rtype: int
|
|
195
|
+
"""
|
|
196
|
+
return self.team_id
|
|
197
|
+
|
|
198
|
+
def set_team_id(self, team_id: int) -> None:
|
|
199
|
+
"""Set the team ID to read workspaces from.
|
|
200
|
+
|
|
201
|
+
:param team_id: The ID of the team.
|
|
202
|
+
:type team_id: int
|
|
203
|
+
"""
|
|
204
|
+
self.team_id = team_id
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def workspace_id(self) -> int:
|
|
208
|
+
"""The ID of the workspace selected in the widget.
|
|
209
|
+
|
|
210
|
+
:return: The ID of the workspace.
|
|
211
|
+
:rtype: int
|
|
212
|
+
"""
|
|
213
|
+
return self._workspace_id
|
|
214
|
+
|
|
215
|
+
@workspace_id.setter
|
|
216
|
+
def workspace_id(self, workspace_id: int) -> None:
|
|
217
|
+
"""Set the workspace ID to read projects from.
|
|
218
|
+
|
|
219
|
+
:param workspace_id: The ID of the workspace.
|
|
220
|
+
:type workspace_id: int
|
|
221
|
+
"""
|
|
222
|
+
if not self._compact:
|
|
223
|
+
self._select_workspace.set_value(workspace_id)
|
|
224
|
+
self._select_project.set(self._get_select_items(workspace_id=workspace_id))
|
|
225
|
+
self._workspace_id = workspace_id
|
|
226
|
+
|
|
227
|
+
def get_selected_workspace_id(self) -> int:
|
|
228
|
+
"""Get the ID of the selected workspace.
|
|
229
|
+
|
|
230
|
+
:return: The ID of the selected workspace.
|
|
231
|
+
:rtype: int
|
|
232
|
+
"""
|
|
233
|
+
return self.workspace_id
|
|
234
|
+
|
|
235
|
+
def set_workspace_id(self, workspace_id: int) -> None:
|
|
236
|
+
"""Set the workspace ID to read projects from.
|
|
237
|
+
|
|
238
|
+
:param workspace_id: The ID of the workspace.
|
|
239
|
+
:type workspace_id: int
|
|
240
|
+
"""
|
|
241
|
+
self.workspace_id = workspace_id
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def project_id(self) -> Optional[int]:
|
|
245
|
+
"""The ID of the project selected in the widget.
|
|
246
|
+
|
|
247
|
+
:return: The ID of the project.
|
|
248
|
+
:rtype: Optional[int]
|
|
249
|
+
"""
|
|
250
|
+
return self._project_id
|
|
251
|
+
|
|
252
|
+
@project_id.setter
|
|
253
|
+
def project_id(self, project_id: Optional[int]) -> None:
|
|
254
|
+
"""Set the project ID to read collections from.
|
|
255
|
+
|
|
256
|
+
:param project_id: The ID of the project.
|
|
257
|
+
:type project_id: int
|
|
258
|
+
"""
|
|
259
|
+
if not self._compact:
|
|
260
|
+
self._select_project.set_value(project_id)
|
|
261
|
+
self.set_project_id(project_id)
|
|
262
|
+
|
|
263
|
+
def get_selected_project_id(self) -> Optional[int]:
|
|
264
|
+
"""Get the ID of the selected project.
|
|
265
|
+
|
|
266
|
+
:return: The ID of the selected project.
|
|
267
|
+
:rtype: Optional[int]
|
|
268
|
+
"""
|
|
269
|
+
return self.project_id
|
|
270
|
+
|
|
271
|
+
def set_collection(self, collection: Union[int, str]) -> None:
|
|
272
|
+
"""Set the collection to be selected.
|
|
273
|
+
|
|
274
|
+
:param collection: The ID or name of the collection.
|
|
275
|
+
:type collection: The ID or name of the collection.
|
|
276
|
+
:raise ValueError: If multiselect is enabled.
|
|
277
|
+
"""
|
|
278
|
+
if isinstance(collection, int):
|
|
279
|
+
self.set_selected_id(collection)
|
|
280
|
+
elif isinstance(collection, str):
|
|
281
|
+
self.set_selected_name(collection)
|
|
282
|
+
else:
|
|
283
|
+
raise ValueError("Collection ID must be an integer or a string.")
|
|
284
|
+
|
|
285
|
+
def set_collections(self, collections: Union[List[int], List[str]]) -> None:
|
|
286
|
+
"""Set the collections to be selected.
|
|
287
|
+
|
|
288
|
+
:param collection: The ID or name of the collection.
|
|
289
|
+
:type collection: The ID or name of the collection.
|
|
290
|
+
:raise ValueError: If multiselect is disabled.
|
|
291
|
+
"""
|
|
292
|
+
if not isinstance(collections, list):
|
|
293
|
+
raise ValueError("Collections must be a list of integers or strings.")
|
|
294
|
+
if all(isinstance(i, int) for i in collections) or len(collections) == 0:
|
|
295
|
+
self.set_selected_ids(collections)
|
|
296
|
+
elif all(isinstance(i, str) for i in collections):
|
|
297
|
+
self.set_selected_names(collections)
|
|
298
|
+
else:
|
|
299
|
+
raise ValueError("Collection IDs must be a list of integers or a list of strings.")
|
|
300
|
+
|
|
301
|
+
def value_changed(self, func: Callable) -> Callable:
|
|
302
|
+
"""Decorator to set the callback function for the value changed event.
|
|
303
|
+
|
|
304
|
+
:param func: The callback function.
|
|
305
|
+
:type func: Callable
|
|
306
|
+
:return: The callback function.
|
|
307
|
+
:rtype: Callable
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
@self._select_collection.value_changed
|
|
311
|
+
def _click(items: Union[List[str], str]):
|
|
312
|
+
if isinstance(items, list):
|
|
313
|
+
res = [self._collections_names_map[item].id for item in items]
|
|
314
|
+
else:
|
|
315
|
+
res = self._collections_names_map[items].id
|
|
316
|
+
|
|
317
|
+
func(res)
|
|
318
|
+
|
|
319
|
+
return _click
|
|
320
|
+
|
|
321
|
+
def _create_select_all_collections_checkbox(self, select_all_collections: bool) -> None:
|
|
322
|
+
"""Create the checkbox to select all collections.
|
|
323
|
+
|
|
324
|
+
:param select_all_collections: Whether all collections should be selected by default.
|
|
325
|
+
:type select_all_collections: bool
|
|
326
|
+
"""
|
|
327
|
+
if not self._multiselect:
|
|
328
|
+
# We'll only create the checkbox if multiselect is enabled.
|
|
329
|
+
return
|
|
330
|
+
select_all_collections_checkbox = Checkbox("Select all collections")
|
|
331
|
+
|
|
332
|
+
@select_all_collections_checkbox.value_changed
|
|
333
|
+
def select_all_collections_checkbox_handler(checked: bool) -> None:
|
|
334
|
+
"""Handler function for the event when the checkbox value changes.
|
|
335
|
+
|
|
336
|
+
:param checked: The value of the checkbox.
|
|
337
|
+
:type checked: bool
|
|
338
|
+
"""
|
|
339
|
+
if self._project_id is None:
|
|
340
|
+
return
|
|
341
|
+
|
|
342
|
+
if checked:
|
|
343
|
+
self.select_all()
|
|
344
|
+
else:
|
|
345
|
+
self.deselect_all()
|
|
346
|
+
|
|
347
|
+
self._widgets.append(select_all_collections_checkbox)
|
|
348
|
+
self._select_all_collections_checkbox = select_all_collections_checkbox
|
|
349
|
+
if select_all_collections:
|
|
350
|
+
self.select_all()
|
|
351
|
+
|
|
352
|
+
def _create_collection_selector(self, select_all_collections: bool) -> None:
|
|
353
|
+
"""Create the collection selector.
|
|
354
|
+
|
|
355
|
+
:param select_all_collections: Whether all collections should be selected by default.
|
|
356
|
+
:type select_all_collections: bool
|
|
357
|
+
"""
|
|
358
|
+
items = self._read_collections(self._project_id)
|
|
359
|
+
if items is None or len(items) == 0:
|
|
360
|
+
items = []
|
|
361
|
+
self._select_collection = Select(
|
|
362
|
+
items=items,
|
|
363
|
+
multiple=self._multiselect,
|
|
364
|
+
width_px=self._width,
|
|
365
|
+
placeholder="Select collection",
|
|
366
|
+
filterable=True,
|
|
367
|
+
)
|
|
368
|
+
if self._collection_id is not None:
|
|
369
|
+
info = self._collections_ids_map.get(self._collection_id)
|
|
370
|
+
if info is not None:
|
|
371
|
+
value = [info.name] if self._multiselect else info.name
|
|
372
|
+
self._select_collection.set_value(value)
|
|
373
|
+
if select_all_collections:
|
|
374
|
+
self.select_all()
|
|
375
|
+
|
|
376
|
+
# Adding the collection selector to the list of widgets to be added to the container.
|
|
377
|
+
self._widgets.append(self._select_collection)
|
|
378
|
+
|
|
379
|
+
def _create_selectors(self, team_is_selectable: bool, workspace_is_selectable: bool):
|
|
380
|
+
"""Create the team, workspace, and project selectors.
|
|
381
|
+
|
|
382
|
+
:param team_is_selectable: Whether the team selector should be selectable.
|
|
383
|
+
:type team_is_selectable: bool
|
|
384
|
+
:param workspace_is_selectable: Whether the workspace selector should be selectable.
|
|
385
|
+
:type workspace_is_selectable: bool
|
|
386
|
+
"""
|
|
387
|
+
|
|
388
|
+
def team_selector_handler(team_id: int):
|
|
389
|
+
"""Handler function for the event when the team selector value changes.
|
|
390
|
+
|
|
391
|
+
:param team_id: The ID of the selected team.
|
|
392
|
+
:type team_id: int
|
|
393
|
+
"""
|
|
394
|
+
self._select_workspace.set(items=self._get_select_items(team_id=team_id))
|
|
395
|
+
self._team_id = team_id
|
|
396
|
+
|
|
397
|
+
def workspace_selector_handler(workspace_id: int):
|
|
398
|
+
"""Handler function for the event when the workspace selector value changes.
|
|
399
|
+
|
|
400
|
+
:param workspace_id: The ID of the selected workspace.
|
|
401
|
+
:type workspace_id: int
|
|
402
|
+
"""
|
|
403
|
+
self._select_project.set(items=self._get_select_items(workspace_id=workspace_id))
|
|
404
|
+
self._workspace_id = workspace_id
|
|
405
|
+
|
|
406
|
+
def project_selector_handler(project_id: int):
|
|
407
|
+
"""Handler function for the event when the project selector value changes.
|
|
408
|
+
|
|
409
|
+
:param project_id: The ID of the selected project.
|
|
410
|
+
:type project_id: int
|
|
411
|
+
"""
|
|
412
|
+
self._select_collection.set(self._read_collections(project_id))
|
|
413
|
+
self._project_id = project_id
|
|
414
|
+
|
|
415
|
+
if (
|
|
416
|
+
self._select_all_collections_checkbox is not None
|
|
417
|
+
and self._select_all_collections_checkbox.is_checked()
|
|
418
|
+
):
|
|
419
|
+
self.select_all()
|
|
420
|
+
self._select_collection.hide()
|
|
421
|
+
|
|
422
|
+
self._select_team = Select(
|
|
423
|
+
items=self._get_select_items(),
|
|
424
|
+
placeholder="Select team",
|
|
425
|
+
filterable=True,
|
|
426
|
+
width_px=self._width,
|
|
427
|
+
)
|
|
428
|
+
self._select_team.set_value(self._team_id)
|
|
429
|
+
if not team_is_selectable:
|
|
430
|
+
self._select_team.disable()
|
|
431
|
+
|
|
432
|
+
self._select_workspace = Select(
|
|
433
|
+
items=self._get_select_items(team_id=self._team_id),
|
|
434
|
+
placeholder="Select workspace",
|
|
435
|
+
filterable=True,
|
|
436
|
+
width_px=self._width,
|
|
437
|
+
)
|
|
438
|
+
self._select_workspace.set_value(self._workspace_id)
|
|
439
|
+
if not workspace_is_selectable:
|
|
440
|
+
self._select_workspace.disable()
|
|
441
|
+
|
|
442
|
+
self._select_project = Select(
|
|
443
|
+
items=self._get_select_items(workspace_id=self._workspace_id),
|
|
444
|
+
placeholder="Select project",
|
|
445
|
+
filterable=True,
|
|
446
|
+
width_px=self._width,
|
|
447
|
+
)
|
|
448
|
+
self._select_project.set_value(self._project_id)
|
|
449
|
+
|
|
450
|
+
# Register the event handlers.
|
|
451
|
+
self._select_team.value_changed(team_selector_handler)
|
|
452
|
+
self._select_workspace.value_changed(workspace_selector_handler)
|
|
453
|
+
self._select_project.value_changed(project_selector_handler)
|
|
454
|
+
|
|
455
|
+
# Adding widgets to the list, so they can be added to the container.
|
|
456
|
+
self._widgets.extend([self._select_team, self._select_workspace, self._select_project])
|
|
457
|
+
|
|
458
|
+
def _get_select_items(self, **kwargs) -> List[Select.Item]:
|
|
459
|
+
"""Get the list of items for the team, workspace, and project selectors.
|
|
460
|
+
Possible keyword arguments are 'team_id' and 'workspace_id'.
|
|
461
|
+
|
|
462
|
+
:return: The list of items.
|
|
463
|
+
:rtype: List[Select.Item]
|
|
464
|
+
"""
|
|
465
|
+
if not kwargs:
|
|
466
|
+
items = self._api.team.get_list()
|
|
467
|
+
elif "team_id" in kwargs:
|
|
468
|
+
items = self._api.workspace.get_list(kwargs["team_id"])
|
|
469
|
+
elif "workspace_id" in kwargs:
|
|
470
|
+
projects_list = self._api.project.get_list(kwargs["workspace_id"])
|
|
471
|
+
if self._project_types is not None:
|
|
472
|
+
items = [
|
|
473
|
+
project for project in projects_list if project.type in self._project_types
|
|
474
|
+
] # TODO: Filter project from API, not from here.
|
|
475
|
+
else:
|
|
476
|
+
items = projects_list
|
|
477
|
+
|
|
478
|
+
return [Select.Item(value=item.id, label=item.name) for item in items]
|
|
479
|
+
|
|
480
|
+
def get_json_data(self) -> Dict:
|
|
481
|
+
"""Get the JSON data of the widget.
|
|
482
|
+
|
|
483
|
+
:return: The JSON data.
|
|
484
|
+
:rtype: Dict
|
|
485
|
+
"""
|
|
486
|
+
return {}
|
|
487
|
+
|
|
488
|
+
def get_json_state(self) -> Dict:
|
|
489
|
+
"""Get the JSON state of the widget.
|
|
490
|
+
|
|
491
|
+
:return: The JSON state.
|
|
492
|
+
:rtype: Dict
|
|
493
|
+
"""
|
|
494
|
+
return {}
|
|
495
|
+
|
|
496
|
+
def _read_collections(self, project_id: Optional[int]) -> Optional[List[Select.Item]]:
|
|
497
|
+
"""Get the lisf of Select.Item objects representing the collection hierarchy.
|
|
498
|
+
|
|
499
|
+
:param project_id: The ID of the project.
|
|
500
|
+
:type project_id: Optional[int]
|
|
501
|
+
:return: The list of Select.Item objects.
|
|
502
|
+
:rtype: Optional[List[Select.Item]]
|
|
503
|
+
"""
|
|
504
|
+
self._fetch_collections(project_id)
|
|
505
|
+
|
|
506
|
+
if not self._collections_names_map or not project_id:
|
|
507
|
+
return None
|
|
508
|
+
|
|
509
|
+
collections = [Select.Item(i.name, i.name) for i in self._collections_names_map.values()]
|
|
510
|
+
return collections
|
|
511
|
+
|
|
512
|
+
def _fetch_collections(self, project_id: Optional[int]) -> None:
|
|
513
|
+
"""Get the mapping of collection name to EntitiesCollectionInfo object.
|
|
514
|
+
|
|
515
|
+
:param project_id: The ID of the project.
|
|
516
|
+
:type project_id: Optional[int]
|
|
517
|
+
:return: None
|
|
518
|
+
:rtype: None
|
|
519
|
+
"""
|
|
520
|
+
self._collections_names_map = {}
|
|
521
|
+
self._collections_ids_map = {}
|
|
522
|
+
|
|
523
|
+
if not project_id:
|
|
524
|
+
return
|
|
525
|
+
|
|
526
|
+
for collection in self._api.entities_collection.get_list(project_id):
|
|
527
|
+
self._collections_names_map[collection.name] = collection
|
|
528
|
+
self._collections_ids_map[collection.id] = collection
|
|
529
|
+
|
|
530
|
+
def set_project_id(self, project_id: Optional[int]) -> None:
|
|
531
|
+
"""Set the project ID to read collections from.
|
|
532
|
+
|
|
533
|
+
:param project_id: The ID of the project.
|
|
534
|
+
:type project_id: int
|
|
535
|
+
"""
|
|
536
|
+
self._project_id = project_id
|
|
537
|
+
items = self._read_collections(project_id)
|
|
538
|
+
if items is None or len(items) == 0:
|
|
539
|
+
self._select_collection.set([])
|
|
540
|
+
return
|
|
541
|
+
self._select_collection.set(items)
|
|
542
|
+
if self._multiselect:
|
|
543
|
+
if self._select_all_collections_checkbox.is_checked():
|
|
544
|
+
self.select_all()
|
|
545
|
+
else:
|
|
546
|
+
self.deselect_all()
|
|
547
|
+
else:
|
|
548
|
+
self._select_collection.set_value("")
|
|
549
|
+
|
|
550
|
+
def _get_selected(self) -> Optional[Union[List[int], int]]:
|
|
551
|
+
"""Get the ID of the selected collection(s).
|
|
552
|
+
|
|
553
|
+
:return: The ID of the selected collection(s).
|
|
554
|
+
:rtype: Optional[Union[List[int], int]]
|
|
555
|
+
"""
|
|
556
|
+
selected = self._select_collection.get_value()
|
|
557
|
+
if not selected:
|
|
558
|
+
return None
|
|
559
|
+
|
|
560
|
+
if isinstance(selected, list):
|
|
561
|
+
return [self._collections_names_map[item].id for item in selected]
|
|
562
|
+
else:
|
|
563
|
+
return self._collections_names_map[selected].id
|
|
564
|
+
|
|
565
|
+
def get_selected_ids(self) -> Optional[List[int]]:
|
|
566
|
+
"""Get the IDs of the selected collections.
|
|
567
|
+
|
|
568
|
+
raise ValueError if multiselect is disabled.
|
|
569
|
+
return: The IDs of the selected collections.
|
|
570
|
+
rtype: Optional[List[int]]
|
|
571
|
+
"""
|
|
572
|
+
if not self._multiselect:
|
|
573
|
+
raise ValueError("This method can only be called when multiselect is enabled.")
|
|
574
|
+
return self._get_selected()
|
|
575
|
+
|
|
576
|
+
def get_selected_id(self) -> Optional[int]:
|
|
577
|
+
"""Get the ID of the selected collection.
|
|
578
|
+
|
|
579
|
+
raise ValueError if multiselect is enabled.
|
|
580
|
+
return: The ID of the selected collection.
|
|
581
|
+
rtype: Optional[int]
|
|
582
|
+
"""
|
|
583
|
+
if self._multiselect:
|
|
584
|
+
raise ValueError("This method can only be called when multiselect is disabled.")
|
|
585
|
+
return self._get_selected()
|
|
586
|
+
|
|
587
|
+
def set_selected_ids(self, collection_ids: List[int]) -> None:
|
|
588
|
+
"""Set the IDs of the collections to be selected by default.
|
|
589
|
+
|
|
590
|
+
:param collection_ids: The IDs of the collections.
|
|
591
|
+
:type collection_ids: List[int]
|
|
592
|
+
"""
|
|
593
|
+
if not self._multiselect:
|
|
594
|
+
raise ValueError("This method can only be called when multiselect is enabled.")
|
|
595
|
+
self._set_selected_by_ids(collection_ids)
|
|
596
|
+
|
|
597
|
+
def set_selected_id(self, collection_id: int) -> None:
|
|
598
|
+
"""Set the ID of the collection to be selected by default.
|
|
599
|
+
|
|
600
|
+
:param collection_id: The ID of the collection.
|
|
601
|
+
:type collection_id: int
|
|
602
|
+
"""
|
|
603
|
+
if self._multiselect:
|
|
604
|
+
raise ValueError("This method can only be called when multiselect is disabled.")
|
|
605
|
+
self._set_selected_by_ids([collection_id])
|
|
606
|
+
|
|
607
|
+
def _set_selected_by_ids(self, collection_ids: List[int]) -> None:
|
|
608
|
+
"""Set the IDs of the collections to be selected by default.
|
|
609
|
+
|
|
610
|
+
:param collection_ids: The IDs of the collections.
|
|
611
|
+
:type collection_ids: List[int]
|
|
612
|
+
"""
|
|
613
|
+
if not self._collections_ids_map:
|
|
614
|
+
return
|
|
615
|
+
|
|
616
|
+
selected = []
|
|
617
|
+
for i in collection_ids:
|
|
618
|
+
if i in self._collections_ids_map:
|
|
619
|
+
selected.append(self._collections_ids_map[i].name)
|
|
620
|
+
|
|
621
|
+
if self._multiselect:
|
|
622
|
+
if len(selected) == len(self._collections_ids_map):
|
|
623
|
+
self.select_all()
|
|
624
|
+
else:
|
|
625
|
+
self.deselect_all()
|
|
626
|
+
self._select_collection.set_value(selected)
|
|
627
|
+
|
|
628
|
+
else:
|
|
629
|
+
if len(selected) > 1:
|
|
630
|
+
raise ValueError("More than one collection found, but multiselect is disabled.")
|
|
631
|
+
value = selected[0] if selected else ""
|
|
632
|
+
self._select_collection.set_value(value)
|
|
633
|
+
|
|
634
|
+
def set_selected_name(self, collection_name: str) -> None:
|
|
635
|
+
"""Set the name of the collection to be selected by default.
|
|
636
|
+
|
|
637
|
+
:param collection_name: The name of the collection.
|
|
638
|
+
:type collection_name: str
|
|
639
|
+
"""
|
|
640
|
+
if self._multiselect:
|
|
641
|
+
raise ValueError("This method can only be called when multiselect is disabled.")
|
|
642
|
+
self._set_selected_by_names([collection_name])
|
|
643
|
+
|
|
644
|
+
def set_selected_names(self, collection_names: List[str]) -> None:
|
|
645
|
+
"""Set the names of the collections to be selected by default.
|
|
646
|
+
|
|
647
|
+
:param collection_names: The names of the collections.
|
|
648
|
+
:type collection_names: List[str]
|
|
649
|
+
"""
|
|
650
|
+
if not self._multiselect:
|
|
651
|
+
raise ValueError("This method can only be called when multiselect is enabled.")
|
|
652
|
+
self._set_selected_by_names(collection_names)
|
|
653
|
+
|
|
654
|
+
def _set_selected_by_names(self, collection_names: List[str]) -> None:
|
|
655
|
+
"""Set the names of the collections to be selected by default.
|
|
656
|
+
|
|
657
|
+
:param collection_names: The names of the collections.
|
|
658
|
+
:type collection_names: List[str]
|
|
659
|
+
"""
|
|
660
|
+
if not self._collections_names_map:
|
|
661
|
+
return
|
|
662
|
+
|
|
663
|
+
ids = []
|
|
664
|
+
for i in collection_names:
|
|
665
|
+
if i in self._collections_names_map:
|
|
666
|
+
ids.append(self._collections_names_map[i].id)
|
|
667
|
+
self._set_selected_by_ids(ids)
|
|
668
|
+
|
|
669
|
+
def is_all_selected(self) -> bool:
|
|
670
|
+
"""Check if all collections are selected.
|
|
671
|
+
|
|
672
|
+
return: True if all collections are selected, False otherwise.
|
|
673
|
+
rtype: bool
|
|
674
|
+
"""
|
|
675
|
+
return len(self._select_collection.get_value()) == len(self._collections_names_map)
|
|
676
|
+
|
|
677
|
+
def select_all(self) -> None:
|
|
678
|
+
"""Select all collections."""
|
|
679
|
+
if not self._multiselect:
|
|
680
|
+
raise ValueError("This method can only be called when multiselect is enabled.")
|
|
681
|
+
self._select_collection.set_value(list(self._collections_names_map.keys()))
|
|
682
|
+
if self._select_all_collections_checkbox is not None:
|
|
683
|
+
self._select_all_collections_checkbox.check()
|
|
684
|
+
self._select_collection.hide()
|
|
685
|
+
|
|
686
|
+
def deselect_all(self) -> None:
|
|
687
|
+
"""Deselect all collections."""
|
|
688
|
+
if not self._multiselect:
|
|
689
|
+
raise ValueError("This method can only be called when multiselect is enabled.")
|
|
690
|
+
self._select_collection.set_value([])
|
|
691
|
+
if self._select_all_collections_checkbox is not None:
|
|
692
|
+
self._select_all_collections_checkbox.uncheck()
|
|
693
|
+
self._select_collection.show()
|