simstack 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- simstack/__init__.py +0 -0
- simstack/_version.py +34 -0
- simstack/core/__init__.py +0 -0
- simstack/core/artifacts.py +447 -0
- simstack/core/asnyc_helper.py +14 -0
- simstack/core/config_file.py +60 -0
- simstack/core/context.py +167 -0
- simstack/core/data.py +139 -0
- simstack/core/definitions.py +21 -0
- simstack/core/engine.py +116 -0
- simstack/core/find_simstack_modules.py +43 -0
- simstack/core/hash.py +177 -0
- simstack/core/model_table.py +199 -0
- simstack/core/node.py +911 -0
- simstack/core/node_runner.py +254 -0
- simstack/core/node_table.py +380 -0
- simstack/core/recompute_artifacts.py +120 -0
- simstack/core/reset_db.py +12 -0
- simstack/core/resources.py +101 -0
- simstack/core/run_node.py +69 -0
- simstack/core/runner.py +435 -0
- simstack/core/simstack_result.py +41 -0
- simstack/core/submit_node.py +143 -0
- simstack/core/task_id.py +22 -0
- simstack/data/__init__.py +0 -0
- simstack/methods/__init__.py +0 -0
- simstack/methods/get_file.py +10 -0
- simstack/methods/multiple_executor.py +307 -0
- simstack/models/__init__.py +37 -0
- simstack/models/array_list.py +48 -0
- simstack/models/array_storage.py +54 -0
- simstack/models/artifact_models.py +127 -0
- simstack/models/base_types.py +208 -0
- simstack/models/charts_artifact.py +411 -0
- simstack/models/dataset.py +432 -0
- simstack/models/dataset_metadata.py +303 -0
- simstack/models/file_instance.py +77 -0
- simstack/models/file_list.py +321 -0
- simstack/models/files.py +229 -0
- simstack/models/log_entry_model.py +37 -0
- simstack/models/models.py +40 -0
- simstack/models/node_registry.py +105 -0
- simstack/models/pandas_model.py +210 -0
- simstack/models/parameters.py +388 -0
- simstack/models/pickle_models.py +311 -0
- simstack/models/runner_model.py +39 -0
- simstack/models/simple_table.py +32 -0
- simstack/models/simstack_model.py +97 -0
- simstack/models/slurm_info.py +17 -0
- simstack/models/table_artifact.py +87 -0
- simstack/util/__init__.py +0 -0
- simstack/util/async_zip_utils.py +18 -0
- simstack/util/cleaned_json_schema.py +20 -0
- simstack/util/config_reader.py +273 -0
- simstack/util/custom_model_dump.py +75 -0
- simstack/util/db.py +457 -0
- simstack/util/db_logger.py +117 -0
- simstack/util/default_from_dict.py +57 -0
- simstack/util/directory_iterator.py +216 -0
- simstack/util/file_hashing.py +68 -0
- simstack/util/files.py +396 -0
- simstack/util/find_python_files.py +33 -0
- simstack/util/generate_ui_schema.py +80 -0
- simstack/util/geometry_utils.py +118 -0
- simstack/util/get_module_path.py +26 -0
- simstack/util/git_pull.py +65 -0
- simstack/util/git_repository_status.py +93 -0
- simstack/util/import_module.py +65 -0
- simstack/util/importer.py +163 -0
- simstack/util/make_graph.py +107 -0
- simstack/util/make_table.py +827 -0
- simstack/util/minimal_route_finder.py +72 -0
- simstack/util/mock_db.py +144 -0
- simstack/util/module_path_checker.py +38 -0
- simstack/util/path_manager.py +234 -0
- simstack/util/project_root_finder.py +36 -0
- simstack/util/queue_watcher.py +262 -0
- simstack/util/route_finder.py +74 -0
- simstack/util/runner_util.py +162 -0
- simstack/util/safe_code_executor.py +194 -0
- simstack/util/sample_config.py +98 -0
- simstack/util/sensitive_content_filter.py +48 -0
- simstack/util/setup_logging.py +49 -0
- simstack/util/submit_to_watchdog.py +106 -0
- simstack/util/ui_tools.py +213 -0
- simstack-0.2.0.dist-info/METADATA +361 -0
- simstack-0.2.0.dist-info/RECORD +91 -0
- simstack-0.2.0.dist-info/WHEEL +5 -0
- simstack-0.2.0.dist-info/entry_points.txt +10 -0
- simstack-0.2.0.dist-info/licenses/LICENSE +21 -0
- simstack-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
from typing import Dict, Iterator, Union, Tuple, KeysView, ValuesView, ItemsView, List
|
|
2
|
+
|
|
3
|
+
from odmantic import Model, ObjectId, EmbeddedModel, Field, Reference
|
|
4
|
+
|
|
5
|
+
from simstack.core.asnyc_helper import async_helper
|
|
6
|
+
from simstack.core.context import context
|
|
7
|
+
from simstack.core.engine import current_engine_context
|
|
8
|
+
from simstack.models import simstack_model
|
|
9
|
+
from simstack.models.dataset_metadata import DataSetMetadata
|
|
10
|
+
from simstack.util.importer import import_class_by_name
|
|
11
|
+
from simstack.util.make_table import make_column_defs_instance
|
|
12
|
+
from simstack.util.make_table import make_table_entries_helper
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@simstack_model
|
|
16
|
+
class DataSetSection(EmbeddedModel):
|
|
17
|
+
"""
|
|
18
|
+
Represents a section of a dataset containing tuples of models.
|
|
19
|
+
|
|
20
|
+
A DataSetSection is a list of tuples where all tuples contain the same types of models.
|
|
21
|
+
For example, if one tuple contains (ModelA, ModelB), then all tuples in this section
|
|
22
|
+
must contain (ModelA, ModelB) instances.
|
|
23
|
+
|
|
24
|
+
:ivar model_types: List of model class names that define the structure of each tuple.
|
|
25
|
+
:type model_types: List[str]
|
|
26
|
+
:ivar data: List of tuples, where each tuple contains model IDs corresponding to model_types.
|
|
27
|
+
:type data: List[List[ObjectId]]
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
model_types: List[str] = Field(
|
|
31
|
+
default_factory=list
|
|
32
|
+
) # Class names of models in each tuple
|
|
33
|
+
data: List[List[ObjectId]] = Field(
|
|
34
|
+
default_factory=list
|
|
35
|
+
) # List of tuples (as lists of ObjectIds)
|
|
36
|
+
|
|
37
|
+
model_config = {"extra": "forbid"}
|
|
38
|
+
|
|
39
|
+
def add_model_group(self, models: Union[Model, Tuple[Model, ...]]) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Add a tuple of models to this section.
|
|
42
|
+
|
|
43
|
+
:param models: Tuple of model instances to add
|
|
44
|
+
:raises ValueError: If the model types don't match the section's expected types
|
|
45
|
+
"""
|
|
46
|
+
if isinstance(models, Model):
|
|
47
|
+
models = (models,)
|
|
48
|
+
model_names = [model.__class__.__name__ for model in models]
|
|
49
|
+
model_ids = [model.id for model in models]
|
|
50
|
+
|
|
51
|
+
# If this is the first tuple, set the model types
|
|
52
|
+
if not self.model_types:
|
|
53
|
+
self.model_types = model_names
|
|
54
|
+
else:
|
|
55
|
+
# Verify that the model types match
|
|
56
|
+
if model_names != self.model_types:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"Model types {model_names} don't match section's expected types {self.model_types}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
self.data.append(model_ids)
|
|
62
|
+
|
|
63
|
+
async def make_column_defs(self):
|
|
64
|
+
"""
|
|
65
|
+
Generate ag-grid column definitions for all model types in this section.
|
|
66
|
+
|
|
67
|
+
:return: List of column definitions for ag-grid
|
|
68
|
+
"""
|
|
69
|
+
column_defs = []
|
|
70
|
+
if len(self.data) == 0:
|
|
71
|
+
return column_defs
|
|
72
|
+
engine = current_engine_context.get()
|
|
73
|
+
for model_group_id, model_type in zip(self.data[0], self.model_types):
|
|
74
|
+
model_class = await import_class_by_name(model_type)
|
|
75
|
+
model_instance = await engine.find_one(
|
|
76
|
+
model_class, model_class.id == model_group_id
|
|
77
|
+
)
|
|
78
|
+
model_columns = make_column_defs_instance(model_instance)
|
|
79
|
+
column_defs.extend(model_columns)
|
|
80
|
+
return column_defs
|
|
81
|
+
|
|
82
|
+
async def make_table_entries(self):
|
|
83
|
+
all_data = []
|
|
84
|
+
engine = current_engine_context.get()
|
|
85
|
+
for model_group_ids in self.data:
|
|
86
|
+
data = []
|
|
87
|
+
for model_group_id, model_type in zip(model_group_ids, self.model_types):
|
|
88
|
+
model_class = await import_class_by_name(model_type)
|
|
89
|
+
model_instance = await engine.find_one(
|
|
90
|
+
model_class, model_class.id == model_group_id
|
|
91
|
+
)
|
|
92
|
+
model_data = make_table_entries_helper(model_instance)
|
|
93
|
+
data.append(model_data)
|
|
94
|
+
all_data.append(data)
|
|
95
|
+
return all_data
|
|
96
|
+
|
|
97
|
+
@async_helper
|
|
98
|
+
async def get_model_group(self, index: int) -> Tuple[Model, ...]:
|
|
99
|
+
"""
|
|
100
|
+
Retrieve a tuple of models at the specified index.
|
|
101
|
+
|
|
102
|
+
:param index: Index of the tuple to retrieve
|
|
103
|
+
:return: Tuple of model instances
|
|
104
|
+
"""
|
|
105
|
+
if index >= len(self.data):
|
|
106
|
+
raise IndexError(
|
|
107
|
+
f"Index {index} out of range for section with {len(self.data)} model groups"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
model_ids = self.data[index]
|
|
111
|
+
models = []
|
|
112
|
+
|
|
113
|
+
for model_type, model_id in zip(self.model_types, model_ids):
|
|
114
|
+
model_class = await import_class_by_name(model_type)
|
|
115
|
+
model_instance = await context.db.find_one(
|
|
116
|
+
model_class, model_class.id == model_id
|
|
117
|
+
)
|
|
118
|
+
if model_instance is None:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f"Model of type {model_type} with id {model_id} not found"
|
|
121
|
+
)
|
|
122
|
+
models.append(model_instance)
|
|
123
|
+
|
|
124
|
+
if len(models) == 1:
|
|
125
|
+
return models[0]
|
|
126
|
+
return tuple(models)
|
|
127
|
+
|
|
128
|
+
def get_all_model_groups(self) -> List[Tuple[Model, ...]]:
|
|
129
|
+
"""
|
|
130
|
+
Retrieve all tuples in this section.
|
|
131
|
+
|
|
132
|
+
:return: List of tuples of model instances
|
|
133
|
+
"""
|
|
134
|
+
all_tuples = []
|
|
135
|
+
for i in range(len(self.data)):
|
|
136
|
+
tuple_models = self.get_model_group(i)
|
|
137
|
+
all_tuples.append(tuple_models)
|
|
138
|
+
return all_tuples
|
|
139
|
+
|
|
140
|
+
# List-like behavior methods
|
|
141
|
+
def __len__(self) -> int:
|
|
142
|
+
"""Return the number of model groups in this section."""
|
|
143
|
+
return len(self.data)
|
|
144
|
+
|
|
145
|
+
def __getitem__(
|
|
146
|
+
self, index: Union[int, slice]
|
|
147
|
+
) -> Union[Tuple[Model, ...], List[Tuple[Model, ...]]]:
|
|
148
|
+
"""
|
|
149
|
+
Get model group(s) at the specified index or slice.
|
|
150
|
+
|
|
151
|
+
:param index: Index or slice to retrieve
|
|
152
|
+
:return: Single tuple or list of tuples of model instances
|
|
153
|
+
"""
|
|
154
|
+
if isinstance(index, slice):
|
|
155
|
+
indices = list(range(*index.indices(len(self.data))))
|
|
156
|
+
return [self.get_model_group(i) for i in indices]
|
|
157
|
+
else:
|
|
158
|
+
return self.get_model_group(index)
|
|
159
|
+
|
|
160
|
+
def __setitem__(self, index: int, value: Tuple[Model, ...]) -> None:
|
|
161
|
+
"""
|
|
162
|
+
Set model group at the specified index.
|
|
163
|
+
|
|
164
|
+
:param index: Index to set
|
|
165
|
+
:param value: Tuple of model instances to set
|
|
166
|
+
"""
|
|
167
|
+
if index >= len(self.data):
|
|
168
|
+
raise IndexError(
|
|
169
|
+
f"Index {index} out of range for section with {len(self.data)} model groups"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
model_names = [model.__class__.__name__ for model in value]
|
|
173
|
+
model_ids = [model.id for model in value]
|
|
174
|
+
|
|
175
|
+
# Verify that the model types match
|
|
176
|
+
if self.model_types and model_names != self.model_types:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"Model types {model_names} don't match section's expected types {self.model_types}"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
self.data[index] = model_ids
|
|
182
|
+
|
|
183
|
+
def __delitem__(self, index: int) -> None:
|
|
184
|
+
"""
|
|
185
|
+
Delete model group at the specified index.
|
|
186
|
+
|
|
187
|
+
:param index: Index to delete
|
|
188
|
+
"""
|
|
189
|
+
if index >= len(self.data):
|
|
190
|
+
raise IndexError(
|
|
191
|
+
f"Index {index} out of range for section with {len(self.data)} model groups"
|
|
192
|
+
)
|
|
193
|
+
del self.data[index]
|
|
194
|
+
|
|
195
|
+
def append(self, models: Tuple[Model, ...]) -> None:
|
|
196
|
+
"""
|
|
197
|
+
Append a tuple of models to the section.
|
|
198
|
+
|
|
199
|
+
:param models: Tuple of model instances to append
|
|
200
|
+
"""
|
|
201
|
+
self.add_model_group(models)
|
|
202
|
+
|
|
203
|
+
def insert(self, index: int, models: Tuple[Model, ...]) -> None:
|
|
204
|
+
"""
|
|
205
|
+
Insert a tuple of models at the specified index.
|
|
206
|
+
|
|
207
|
+
:param index: Index to insert at
|
|
208
|
+
:param models: Tuple of model instances to insert
|
|
209
|
+
"""
|
|
210
|
+
model_names = [model.__class__.__name__ for model in models]
|
|
211
|
+
model_ids = [model.id for model in models]
|
|
212
|
+
|
|
213
|
+
# If this is the first tuple, set the model types
|
|
214
|
+
if not self.model_types:
|
|
215
|
+
self.model_types = model_names
|
|
216
|
+
else:
|
|
217
|
+
# Verify that the model types match
|
|
218
|
+
if model_names != self.model_types:
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f"Model types {model_names} don't match section's expected types {self.model_types}"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
self.data.insert(index, model_ids)
|
|
224
|
+
|
|
225
|
+
def extend(self, models_list: List[Tuple[Model, ...]]) -> None:
|
|
226
|
+
"""
|
|
227
|
+
Extend the section with multiple tuples of models.
|
|
228
|
+
|
|
229
|
+
:param models_list: List of tuples of model instances to extend with
|
|
230
|
+
"""
|
|
231
|
+
for models in models_list:
|
|
232
|
+
self.add_model_group(models)
|
|
233
|
+
|
|
234
|
+
def pop(self, index: int = -1) -> Tuple[Model, ...]:
|
|
235
|
+
"""
|
|
236
|
+
Remove and return a model group at the specified index (default last).
|
|
237
|
+
|
|
238
|
+
:param index: Index to pop (default -1 for last)
|
|
239
|
+
:return: Tuple of model instances that was removed
|
|
240
|
+
"""
|
|
241
|
+
if len(self.data) == 0:
|
|
242
|
+
raise IndexError("pop from empty DataSetSection")
|
|
243
|
+
|
|
244
|
+
# Get the models first before removing
|
|
245
|
+
models = self.get_model_group(index)
|
|
246
|
+
del self.data[index]
|
|
247
|
+
return models
|
|
248
|
+
|
|
249
|
+
def remove(self, models: Tuple[Model, ...]) -> None:
|
|
250
|
+
"""
|
|
251
|
+
Remove the first occurrence of the specified tuple of models.
|
|
252
|
+
|
|
253
|
+
:param models: Tuple of model instances to remove
|
|
254
|
+
:raises ValueError: If the tuple is not found
|
|
255
|
+
"""
|
|
256
|
+
model_ids = [model.id for model in models]
|
|
257
|
+
try:
|
|
258
|
+
self.data.remove(model_ids)
|
|
259
|
+
except ValueError:
|
|
260
|
+
raise ValueError(f"Tuple {models} not found in DataSetSection")
|
|
261
|
+
|
|
262
|
+
def clear(self) -> None:
|
|
263
|
+
"""Remove all model groups from the section."""
|
|
264
|
+
self.data.clear()
|
|
265
|
+
self.model_types.clear()
|
|
266
|
+
|
|
267
|
+
async def index(
|
|
268
|
+
self, models: Tuple[Model, ...], start: int = 0, stop: int = None
|
|
269
|
+
) -> int:
|
|
270
|
+
"""
|
|
271
|
+
Return the index of the first occurrence of the specified tuple of models.
|
|
272
|
+
|
|
273
|
+
:param models: Tuple of model instances to find
|
|
274
|
+
:param start: Start index for search
|
|
275
|
+
:param stop: Stop index for search
|
|
276
|
+
:return: Index of the tuple
|
|
277
|
+
:raises ValueError: If the tuple is not found
|
|
278
|
+
"""
|
|
279
|
+
model_ids = [model.id for model in models]
|
|
280
|
+
if stop is None:
|
|
281
|
+
stop = len(self.data)
|
|
282
|
+
|
|
283
|
+
for i in range(start, min(stop, len(self.data))):
|
|
284
|
+
if self.data[i] == model_ids:
|
|
285
|
+
return i
|
|
286
|
+
|
|
287
|
+
raise ValueError(f"Tuple {models} not found in DataSetSection")
|
|
288
|
+
|
|
289
|
+
def count(self, models: Tuple[Model, ...]) -> int:
|
|
290
|
+
"""
|
|
291
|
+
Return the number of occurrences of the specified tuple of models.
|
|
292
|
+
|
|
293
|
+
:param models: Tuple of model instances to count
|
|
294
|
+
:return: Number of occurrences
|
|
295
|
+
"""
|
|
296
|
+
model_ids = [model.id for model in models]
|
|
297
|
+
return self.data.count(model_ids)
|
|
298
|
+
|
|
299
|
+
def reverse(self) -> None:
|
|
300
|
+
"""Reverse the order of model groups in the section."""
|
|
301
|
+
self.data.reverse()
|
|
302
|
+
|
|
303
|
+
def __iter__(self):
|
|
304
|
+
"""
|
|
305
|
+
Iterate over model groups in the section.
|
|
306
|
+
|
|
307
|
+
:return: Async iterator over tuples of model instances
|
|
308
|
+
"""
|
|
309
|
+
for i in range(len(self.data)):
|
|
310
|
+
yield self.get_model_group(i)
|
|
311
|
+
|
|
312
|
+
def __contains__(self, models: Tuple[Model, ...]) -> bool:
|
|
313
|
+
"""
|
|
314
|
+
Check if the specified tuple of models exists in the section.
|
|
315
|
+
|
|
316
|
+
:param models: Tuple of model instances to check for
|
|
317
|
+
:return: True if found, False otherwise
|
|
318
|
+
"""
|
|
319
|
+
model_ids = [model.id for model in models]
|
|
320
|
+
return model_ids in self.data
|
|
321
|
+
|
|
322
|
+
def __bool__(self) -> bool:
|
|
323
|
+
"""Return True if the section is not empty."""
|
|
324
|
+
return len(self.data) > 0
|
|
325
|
+
|
|
326
|
+
def __repr__(self) -> str:
|
|
327
|
+
"""Return string representation of the section."""
|
|
328
|
+
return (
|
|
329
|
+
f"DataSetSection(model_types={self.model_types}, length={len(self.data)})"
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@simstack_model
|
|
334
|
+
class DataSet(Model):
|
|
335
|
+
name: str = Field(default="dataset")
|
|
336
|
+
metadata: DataSetMetadata = Reference()
|
|
337
|
+
sections: Dict[str, DataSetSection] = Field(default_factory=dict)
|
|
338
|
+
|
|
339
|
+
model_config = {"extra": "forbid"}
|
|
340
|
+
|
|
341
|
+
@property
|
|
342
|
+
def dataset_type(self) -> str:
|
|
343
|
+
return self.metadata.dataset_type
|
|
344
|
+
|
|
345
|
+
async def save(self, engine):
|
|
346
|
+
# engine = current_engine_context.get()
|
|
347
|
+
structure = self.collect_structure()
|
|
348
|
+
ok = await self.metadata.validate_dict(structure)
|
|
349
|
+
if not ok:
|
|
350
|
+
raise ValueError("Metadata validation failed")
|
|
351
|
+
await engine.save_unchecked(self)
|
|
352
|
+
|
|
353
|
+
async def custom_model_dump(self, **kwargs) -> Dict[str, str]:
|
|
354
|
+
"""
|
|
355
|
+
:return: dict with id
|
|
356
|
+
"""
|
|
357
|
+
return {"id": str(self.id)}
|
|
358
|
+
|
|
359
|
+
def collect_structure(self) -> Dict[str, List[str]]:
|
|
360
|
+
"""
|
|
361
|
+
Returns a dictionary where keys are section names and values are lists of model types.
|
|
362
|
+
|
|
363
|
+
:return: Dictionary mapping section names to their model types
|
|
364
|
+
:rtype: Dict[str, List[str]]
|
|
365
|
+
"""
|
|
366
|
+
return {
|
|
367
|
+
section_name: section.model_types if len(section) > 0 else None
|
|
368
|
+
for section_name, section in self.sections.items()
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
# Dict-like behavior methods
|
|
372
|
+
def __getitem__(self, key: str) -> DataSetSection:
|
|
373
|
+
return self.sections[key]
|
|
374
|
+
|
|
375
|
+
def __setitem__(self, key: str, value: DataSetSection) -> None:
|
|
376
|
+
self.sections[key] = value
|
|
377
|
+
|
|
378
|
+
def __delitem__(self, key: str) -> None:
|
|
379
|
+
del self.sections[key]
|
|
380
|
+
|
|
381
|
+
def __len__(self) -> int:
|
|
382
|
+
return len(self.sections)
|
|
383
|
+
|
|
384
|
+
def __iter__(self) -> Iterator[str]:
|
|
385
|
+
return iter(self.sections)
|
|
386
|
+
|
|
387
|
+
def __contains__(self, key: str) -> bool:
|
|
388
|
+
return key in self.sections
|
|
389
|
+
|
|
390
|
+
def keys(self) -> KeysView[str]:
|
|
391
|
+
return self.sections.keys()
|
|
392
|
+
|
|
393
|
+
def values(self) -> ValuesView[DataSetSection]:
|
|
394
|
+
return self.sections.values()
|
|
395
|
+
|
|
396
|
+
def items(self) -> ItemsView[str, DataSetSection]:
|
|
397
|
+
return self.sections.items()
|
|
398
|
+
|
|
399
|
+
def get(self, key: str, default: DataSetSection = None) -> DataSetSection:
|
|
400
|
+
return self.sections.get(key, default)
|
|
401
|
+
|
|
402
|
+
def pop(self, key: str, default=None) -> DataSetSection:
|
|
403
|
+
if default is None:
|
|
404
|
+
return self.sections.pop(key)
|
|
405
|
+
return self.sections.pop(key, default)
|
|
406
|
+
|
|
407
|
+
def popitem(self) -> Tuple[str, DataSetSection]:
|
|
408
|
+
return self.sections.popitem()
|
|
409
|
+
|
|
410
|
+
def clear(self) -> None:
|
|
411
|
+
self.sections.clear()
|
|
412
|
+
|
|
413
|
+
def update(
|
|
414
|
+
self, other: Union[Dict[str, DataSetSection], "DataSet"] = None, **kwargs
|
|
415
|
+
) -> None:
|
|
416
|
+
if other is not None:
|
|
417
|
+
if hasattr(other, "sections"):
|
|
418
|
+
self.sections.update(other.sections)
|
|
419
|
+
else:
|
|
420
|
+
self.sections.update(other)
|
|
421
|
+
self.sections.update(kwargs)
|
|
422
|
+
|
|
423
|
+
def setdefault(self, key: str, default: DataSetSection = None) -> DataSetSection:
|
|
424
|
+
return self.sections.setdefault(key, default)
|
|
425
|
+
|
|
426
|
+
@classmethod
|
|
427
|
+
def ui_schema(cls) -> dict:
|
|
428
|
+
return {
|
|
429
|
+
"ui:field": "DataSetField",
|
|
430
|
+
"metadata": {"ui:widget": "hidden"},
|
|
431
|
+
"sections": {"ui:widget": "hidden"},
|
|
432
|
+
}
|