cognite-toolkit 0.6.97__py3-none-any.whl → 0.7.39__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 (198) hide show
  1. cognite_toolkit/_cdf.py +21 -23
  2. cognite_toolkit/_cdf_tk/apps/__init__.py +4 -0
  3. cognite_toolkit/_cdf_tk/apps/_core_app.py +19 -5
  4. cognite_toolkit/_cdf_tk/apps/_data_app.py +1 -1
  5. cognite_toolkit/_cdf_tk/apps/_dev_app.py +86 -0
  6. cognite_toolkit/_cdf_tk/apps/_download_app.py +693 -25
  7. cognite_toolkit/_cdf_tk/apps/_dump_app.py +44 -102
  8. cognite_toolkit/_cdf_tk/apps/_import_app.py +41 -0
  9. cognite_toolkit/_cdf_tk/apps/_landing_app.py +18 -4
  10. cognite_toolkit/_cdf_tk/apps/_migrate_app.py +424 -9
  11. cognite_toolkit/_cdf_tk/apps/_modules_app.py +0 -3
  12. cognite_toolkit/_cdf_tk/apps/_purge.py +15 -43
  13. cognite_toolkit/_cdf_tk/apps/_run.py +11 -0
  14. cognite_toolkit/_cdf_tk/apps/_upload_app.py +45 -6
  15. cognite_toolkit/_cdf_tk/builders/__init__.py +2 -2
  16. cognite_toolkit/_cdf_tk/builders/_base.py +28 -42
  17. cognite_toolkit/_cdf_tk/builders/_raw.py +1 -1
  18. cognite_toolkit/_cdf_tk/cdf_toml.py +20 -1
  19. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +32 -12
  20. cognite_toolkit/_cdf_tk/client/api/infield.py +114 -17
  21. cognite_toolkit/_cdf_tk/client/api/{canvas.py → legacy/canvas.py} +15 -7
  22. cognite_toolkit/_cdf_tk/client/api/{charts.py → legacy/charts.py} +1 -1
  23. cognite_toolkit/_cdf_tk/client/api/{extended_data_modeling.py → legacy/extended_data_modeling.py} +1 -1
  24. cognite_toolkit/_cdf_tk/client/api/{extended_files.py → legacy/extended_files.py} +2 -2
  25. cognite_toolkit/_cdf_tk/client/api/{extended_functions.py → legacy/extended_functions.py} +15 -18
  26. cognite_toolkit/_cdf_tk/client/api/{extended_raw.py → legacy/extended_raw.py} +1 -1
  27. cognite_toolkit/_cdf_tk/client/api/{extended_timeseries.py → legacy/extended_timeseries.py} +5 -2
  28. cognite_toolkit/_cdf_tk/client/api/{location_filters.py → legacy/location_filters.py} +1 -1
  29. cognite_toolkit/_cdf_tk/client/api/legacy/robotics/__init__.py +8 -0
  30. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/capabilities.py +1 -1
  31. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/data_postprocessing.py +1 -1
  32. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/frames.py +1 -1
  33. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/locations.py +1 -1
  34. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/maps.py +1 -1
  35. cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/robots.py +2 -2
  36. cognite_toolkit/_cdf_tk/client/api/{search_config.py → legacy/search_config.py} +5 -1
  37. cognite_toolkit/_cdf_tk/client/api/migration.py +177 -4
  38. cognite_toolkit/_cdf_tk/client/api/project.py +9 -8
  39. cognite_toolkit/_cdf_tk/client/api/search.py +2 -2
  40. cognite_toolkit/_cdf_tk/client/api/streams.py +88 -0
  41. cognite_toolkit/_cdf_tk/client/api/three_d.py +384 -0
  42. cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py +13 -0
  43. cognite_toolkit/_cdf_tk/client/data_classes/base.py +37 -33
  44. cognite_toolkit/_cdf_tk/client/data_classes/charts_data.py +95 -213
  45. cognite_toolkit/_cdf_tk/client/data_classes/infield.py +32 -18
  46. cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +18 -13
  47. cognite_toolkit/_cdf_tk/client/data_classes/legacy/__init__.py +0 -0
  48. cognite_toolkit/_cdf_tk/client/data_classes/{canvas.py → legacy/canvas.py} +47 -4
  49. cognite_toolkit/_cdf_tk/client/data_classes/{charts.py → legacy/charts.py} +3 -3
  50. cognite_toolkit/_cdf_tk/client/data_classes/{migration.py → legacy/migration.py} +10 -2
  51. cognite_toolkit/_cdf_tk/client/data_classes/streams.py +90 -0
  52. cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +112 -0
  53. cognite_toolkit/_cdf_tk/client/testing.py +42 -18
  54. cognite_toolkit/_cdf_tk/commands/__init__.py +7 -6
  55. cognite_toolkit/_cdf_tk/commands/_changes.py +3 -42
  56. cognite_toolkit/_cdf_tk/commands/_download.py +21 -11
  57. cognite_toolkit/_cdf_tk/commands/_migrate/__init__.py +0 -2
  58. cognite_toolkit/_cdf_tk/commands/_migrate/command.py +22 -20
  59. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +140 -92
  60. cognite_toolkit/_cdf_tk/commands/_migrate/creators.py +1 -1
  61. cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +108 -26
  62. cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +448 -45
  63. cognite_toolkit/_cdf_tk/commands/_migrate/data_model.py +1 -0
  64. cognite_toolkit/_cdf_tk/commands/_migrate/default_mappings.py +6 -6
  65. cognite_toolkit/_cdf_tk/commands/_migrate/issues.py +52 -1
  66. cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +377 -11
  67. cognite_toolkit/_cdf_tk/commands/_migrate/selectors.py +9 -4
  68. cognite_toolkit/_cdf_tk/commands/_profile.py +1 -1
  69. cognite_toolkit/_cdf_tk/commands/_purge.py +36 -39
  70. cognite_toolkit/_cdf_tk/commands/_questionary_style.py +16 -0
  71. cognite_toolkit/_cdf_tk/commands/_upload.py +109 -86
  72. cognite_toolkit/_cdf_tk/commands/about.py +221 -0
  73. cognite_toolkit/_cdf_tk/commands/auth.py +19 -12
  74. cognite_toolkit/_cdf_tk/commands/build_cmd.py +16 -62
  75. cognite_toolkit/_cdf_tk/commands/build_v2/__init__.py +0 -0
  76. cognite_toolkit/_cdf_tk/commands/build_v2/build_cmd.py +241 -0
  77. cognite_toolkit/_cdf_tk/commands/build_v2/build_input.py +85 -0
  78. cognite_toolkit/_cdf_tk/commands/build_v2/build_issues.py +27 -0
  79. cognite_toolkit/_cdf_tk/commands/clean.py +63 -16
  80. cognite_toolkit/_cdf_tk/commands/deploy.py +20 -17
  81. cognite_toolkit/_cdf_tk/commands/dump_resource.py +10 -8
  82. cognite_toolkit/_cdf_tk/commands/init.py +225 -3
  83. cognite_toolkit/_cdf_tk/commands/modules.py +20 -44
  84. cognite_toolkit/_cdf_tk/commands/pull.py +6 -19
  85. cognite_toolkit/_cdf_tk/commands/resources.py +179 -0
  86. cognite_toolkit/_cdf_tk/commands/run.py +1 -1
  87. cognite_toolkit/_cdf_tk/constants.py +20 -1
  88. cognite_toolkit/_cdf_tk/cruds/__init__.py +19 -5
  89. cognite_toolkit/_cdf_tk/cruds/_base_cruds.py +14 -70
  90. cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +10 -19
  91. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/__init__.py +4 -1
  92. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/agent.py +11 -9
  93. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +5 -15
  94. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +45 -44
  95. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/configuration.py +5 -12
  96. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/data_organization.py +4 -13
  97. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/datamodel.py +206 -67
  98. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/extraction_pipeline.py +6 -18
  99. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +126 -35
  100. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +7 -28
  101. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/function.py +23 -30
  102. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/hosted_extractors.py +12 -30
  103. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/industrial_tool.py +4 -8
  104. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +4 -16
  105. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/migration.py +5 -13
  106. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/raw.py +5 -11
  107. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +3 -8
  108. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/robotics.py +16 -45
  109. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +94 -0
  110. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/three_d_model.py +3 -7
  111. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +5 -15
  112. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/transformation.py +75 -32
  113. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/workflow.py +20 -40
  114. cognite_toolkit/_cdf_tk/cruds/_worker.py +24 -36
  115. cognite_toolkit/_cdf_tk/data_classes/_module_toml.py +1 -0
  116. cognite_toolkit/_cdf_tk/feature_flags.py +16 -36
  117. cognite_toolkit/_cdf_tk/plugins.py +2 -1
  118. cognite_toolkit/_cdf_tk/resource_classes/__init__.py +4 -0
  119. cognite_toolkit/_cdf_tk/resource_classes/capabilities.py +12 -0
  120. cognite_toolkit/_cdf_tk/resource_classes/functions.py +3 -1
  121. cognite_toolkit/_cdf_tk/resource_classes/infield_cdm_location_config.py +109 -0
  122. cognite_toolkit/_cdf_tk/resource_classes/migration.py +8 -17
  123. cognite_toolkit/_cdf_tk/resource_classes/search_config.py +1 -1
  124. cognite_toolkit/_cdf_tk/resource_classes/streams.py +29 -0
  125. cognite_toolkit/_cdf_tk/resource_classes/workflow_version.py +164 -5
  126. cognite_toolkit/_cdf_tk/storageio/__init__.py +9 -21
  127. cognite_toolkit/_cdf_tk/storageio/_annotations.py +19 -16
  128. cognite_toolkit/_cdf_tk/storageio/_applications.py +340 -28
  129. cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +67 -104
  130. cognite_toolkit/_cdf_tk/storageio/_base.py +61 -29
  131. cognite_toolkit/_cdf_tk/storageio/_datapoints.py +276 -20
  132. cognite_toolkit/_cdf_tk/storageio/_file_content.py +435 -0
  133. cognite_toolkit/_cdf_tk/storageio/_instances.py +35 -3
  134. cognite_toolkit/_cdf_tk/storageio/_raw.py +26 -0
  135. cognite_toolkit/_cdf_tk/storageio/selectors/__init__.py +71 -4
  136. cognite_toolkit/_cdf_tk/storageio/selectors/_base.py +14 -2
  137. cognite_toolkit/_cdf_tk/storageio/selectors/_canvas.py +14 -0
  138. cognite_toolkit/_cdf_tk/storageio/selectors/_charts.py +14 -0
  139. cognite_toolkit/_cdf_tk/storageio/selectors/_datapoints.py +23 -3
  140. cognite_toolkit/_cdf_tk/storageio/selectors/_file_content.py +164 -0
  141. cognite_toolkit/_cdf_tk/storageio/selectors/_three_d.py +34 -0
  142. cognite_toolkit/_cdf_tk/tk_warnings/other.py +4 -0
  143. cognite_toolkit/_cdf_tk/tracker.py +2 -2
  144. cognite_toolkit/_cdf_tk/utils/cdf.py +1 -1
  145. cognite_toolkit/_cdf_tk/utils/dtype_conversion.py +9 -3
  146. cognite_toolkit/_cdf_tk/utils/fileio/__init__.py +2 -0
  147. cognite_toolkit/_cdf_tk/utils/fileio/_base.py +5 -1
  148. cognite_toolkit/_cdf_tk/utils/fileio/_readers.py +112 -20
  149. cognite_toolkit/_cdf_tk/utils/fileio/_writers.py +15 -15
  150. cognite_toolkit/_cdf_tk/utils/http_client/__init__.py +28 -0
  151. cognite_toolkit/_cdf_tk/utils/http_client/_client.py +285 -18
  152. cognite_toolkit/_cdf_tk/utils/http_client/_data_classes.py +56 -4
  153. cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py +247 -0
  154. cognite_toolkit/_cdf_tk/utils/http_client/_tracker.py +5 -2
  155. cognite_toolkit/_cdf_tk/utils/interactive_select.py +60 -18
  156. cognite_toolkit/_cdf_tk/utils/sql_parser.py +2 -3
  157. cognite_toolkit/_cdf_tk/utils/useful_types.py +6 -2
  158. cognite_toolkit/_cdf_tk/validation.py +83 -1
  159. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  160. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  161. cognite_toolkit/_resources/cdf.toml +5 -4
  162. cognite_toolkit/_version.py +1 -1
  163. cognite_toolkit/config.dev.yaml +13 -0
  164. {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.39.dist-info}/METADATA +24 -24
  165. cognite_toolkit-0.7.39.dist-info/RECORD +322 -0
  166. cognite_toolkit-0.7.39.dist-info/WHEEL +4 -0
  167. {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.39.dist-info}/entry_points.txt +1 -0
  168. cognite_toolkit/_cdf_tk/client/api/robotics/__init__.py +0 -3
  169. cognite_toolkit/_cdf_tk/commands/_migrate/canvas.py +0 -201
  170. cognite_toolkit/_cdf_tk/commands/dump_data.py +0 -489
  171. cognite_toolkit/_cdf_tk/commands/featureflag.py +0 -27
  172. cognite_toolkit/_cdf_tk/prototypes/import_app.py +0 -41
  173. cognite_toolkit/_cdf_tk/utils/table_writers.py +0 -434
  174. cognite_toolkit-0.6.97.dist-info/RECORD +0 -306
  175. cognite_toolkit-0.6.97.dist-info/WHEEL +0 -4
  176. cognite_toolkit-0.6.97.dist-info/licenses/LICENSE +0 -18
  177. /cognite_toolkit/_cdf_tk/{prototypes/commands → client/api/legacy}/__init__.py +0 -0
  178. /cognite_toolkit/_cdf_tk/client/api/{dml.py → legacy/dml.py} +0 -0
  179. /cognite_toolkit/_cdf_tk/client/api/{fixed_transformations.py → legacy/fixed_transformations.py} +0 -0
  180. /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/api.py +0 -0
  181. /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/utlis.py +0 -0
  182. /cognite_toolkit/_cdf_tk/client/data_classes/{apm_config_v1.py → legacy/apm_config_v1.py} +0 -0
  183. /cognite_toolkit/_cdf_tk/client/data_classes/{extendable_cognite_file.py → legacy/extendable_cognite_file.py} +0 -0
  184. /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetadata.py → legacy/extended_filemetadata.py} +0 -0
  185. /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetdata.py → legacy/extended_filemetdata.py} +0 -0
  186. /cognite_toolkit/_cdf_tk/client/data_classes/{extended_timeseries.py → legacy/extended_timeseries.py} +0 -0
  187. /cognite_toolkit/_cdf_tk/client/data_classes/{functions.py → legacy/functions.py} +0 -0
  188. /cognite_toolkit/_cdf_tk/client/data_classes/{graphql_data_models.py → legacy/graphql_data_models.py} +0 -0
  189. /cognite_toolkit/_cdf_tk/client/data_classes/{instances.py → legacy/instances.py} +0 -0
  190. /cognite_toolkit/_cdf_tk/client/data_classes/{location_filters.py → legacy/location_filters.py} +0 -0
  191. /cognite_toolkit/_cdf_tk/client/data_classes/{pending_instances_ids.py → legacy/pending_instances_ids.py} +0 -0
  192. /cognite_toolkit/_cdf_tk/client/data_classes/{project.py → legacy/project.py} +0 -0
  193. /cognite_toolkit/_cdf_tk/client/data_classes/{raw.py → legacy/raw.py} +0 -0
  194. /cognite_toolkit/_cdf_tk/client/data_classes/{robotics.py → legacy/robotics.py} +0 -0
  195. /cognite_toolkit/_cdf_tk/client/data_classes/{search_config.py → legacy/search_config.py} +0 -0
  196. /cognite_toolkit/_cdf_tk/client/data_classes/{sequences.py → legacy/sequences.py} +0 -0
  197. /cognite_toolkit/_cdf_tk/client/data_classes/{streamlit_.py → legacy/streamlit_.py} +0 -0
  198. /cognite_toolkit/_cdf_tk/{prototypes/commands/import_.py → commands/_import_cmd.py} +0 -0
@@ -41,6 +41,7 @@ from ._resource_cruds import (
41
41
  HostedExtractorJobCRUD,
42
42
  HostedExtractorMappingCRUD,
43
43
  HostedExtractorSourceCRUD,
44
+ InFieldCDMLocationConfigCRUD,
44
45
  InFieldLocationConfigCRUD,
45
46
  InfieldV1CRUD,
46
47
  LabelCRUD,
@@ -60,6 +61,7 @@ from ._resource_cruds import (
60
61
  SequenceCRUD,
61
62
  SequenceRowCRUD,
62
63
  SpaceCRUD,
64
+ StreamCRUD,
63
65
  StreamlitCRUD,
64
66
  ThreeDModelCRUD,
65
67
  TimeSeriesCRUD,
@@ -76,15 +78,14 @@ from ._worker import ResourceWorker
76
78
  _EXCLUDED_CRUDS: set[type[ResourceCRUD]] = set()
77
79
  if not FeatureFlag.is_enabled(Flags.GRAPHQL):
78
80
  _EXCLUDED_CRUDS.add(GraphQLCRUD)
79
- if not FeatureFlag.is_enabled(Flags.AGENTS):
80
- _EXCLUDED_CRUDS.add(AgentCRUD)
81
81
  if not FeatureFlag.is_enabled(Flags.INFIELD):
82
82
  _EXCLUDED_CRUDS.add(InfieldV1CRUD)
83
83
  _EXCLUDED_CRUDS.add(InFieldLocationConfigCRUD)
84
+ _EXCLUDED_CRUDS.add(InFieldCDMLocationConfigCRUD)
84
85
  if not FeatureFlag.is_enabled(Flags.MIGRATE):
85
86
  _EXCLUDED_CRUDS.add(ResourceViewMappingCRUD)
86
- if not FeatureFlag.is_enabled(Flags.SEARCH_CONFIG):
87
- _EXCLUDED_CRUDS.add(SearchConfigCRUD)
87
+ if not FeatureFlag.is_enabled(Flags.STREAMS):
88
+ _EXCLUDED_CRUDS.add(StreamCRUD)
88
89
 
89
90
 
90
91
  CRUDS_BY_FOLDER_NAME: dict[str, list[type[Loader]]] = {}
@@ -103,6 +104,14 @@ for _loader in itertools.chain(
103
104
  CRUDS_BY_FOLDER_NAME[_loader.folder_name].append(_loader) # type: ignore[arg-type, attr-defined]
104
105
  del _loader # cleanup module namespace
105
106
 
107
+ # For backwards compatibility
108
+ CRUDS_BY_FOLDER_NAME["data_models"] = CRUDS_BY_FOLDER_NAME["data_modeling"] # Todo: Remove in v1.0
109
+ RESOURCE_CRUD_BY_FOLDER_NAME = {
110
+ folder_name: cruds
111
+ for folder_name, loaders in CRUDS_BY_FOLDER_NAME.items()
112
+ if (cruds := [crud for crud in loaders if issubclass(crud, ResourceCRUD)])
113
+ }
114
+
106
115
  CRUD_LIST = list(itertools.chain.from_iterable(CRUDS_BY_FOLDER_NAME.values()))
107
116
  RESOURCE_CRUD_LIST = [loader for loader in CRUD_LIST if issubclass(loader, ResourceCRUD)]
108
117
  RESOURCE_CRUD_CONTAINER_LIST = [loader for loader in CRUD_LIST if issubclass(loader, ResourceContainerCRUD)]
@@ -120,7 +129,8 @@ ResourceTypes: TypeAlias = Literal[
120
129
  "auth",
121
130
  "cdf_applications",
122
131
  "classic",
123
- "data_models",
132
+ "data_modeling",
133
+ "data_models", # Todo: Remove in v1.0
124
134
  "data_sets",
125
135
  "hosted_extractors",
126
136
  "locations",
@@ -132,6 +142,7 @@ ResourceTypes: TypeAlias = Literal[
132
142
  "functions",
133
143
  "raw",
134
144
  "robotics",
145
+ "streams",
135
146
  "streamlit",
136
147
  "workflows",
137
148
  ]
@@ -148,9 +159,11 @@ __all__ = [
148
159
  "CRUDS_BY_FOLDER_NAME",
149
160
  "CRUD_LIST",
150
161
  "KINDS_BY_FOLDER_NAME",
162
+ "RESOURCE_CRUD_BY_FOLDER_NAME",
151
163
  "RESOURCE_CRUD_CONTAINER_LIST",
152
164
  "RESOURCE_CRUD_LIST",
153
165
  "RESOURCE_DATA_CRUD_LIST",
166
+ "AgentCRUD",
154
167
  "AssetCRUD",
155
168
  "CogniteFileCRUD",
156
169
  "ContainerCRUD",
@@ -174,6 +187,7 @@ __all__ = [
174
187
  "HostedExtractorJobCRUD",
175
188
  "HostedExtractorMappingCRUD",
176
189
  "HostedExtractorSourceCRUD",
190
+ "InFieldCDMLocationConfigCRUD",
177
191
  "InFieldLocationConfigCRUD",
178
192
  "LabelCRUD",
179
193
  "LocationFilterCRUD",
@@ -1,7 +1,6 @@
1
- import re
2
1
  import sys
3
2
  from abc import ABC, abstractmethod
4
- from collections.abc import Hashable, Iterable, Sequence, Set, Sized
3
+ from collections.abc import Hashable, Iterable, Sequence, Sized
5
4
  from pathlib import Path
6
5
  from typing import TYPE_CHECKING, Any, Generic, TypeVar
7
6
 
@@ -10,13 +9,8 @@ from cognite.client.utils.useful_types import SequenceNotStr
10
9
  from rich.console import Console
11
10
 
12
11
  from cognite_toolkit._cdf_tk.client import ToolkitClient
13
- from cognite_toolkit._cdf_tk.constants import BUILD_FOLDER_ENCODING, EXCL_FILES
14
- from cognite_toolkit._cdf_tk.protocols import (
15
- T_ResourceRequest,
16
- T_ResourceRequestList,
17
- T_ResourceResponse,
18
- T_ResourceResponseList,
19
- )
12
+ from cognite_toolkit._cdf_tk.constants import BUILD_FOLDER_ENCODING, YAML_SUFFIX
13
+ from cognite_toolkit._cdf_tk.protocols import T_ResourceRequest, T_ResourceResponse
20
14
  from cognite_toolkit._cdf_tk.resource_classes import ToolkitResource
21
15
  from cognite_toolkit._cdf_tk.tk_warnings import ToolkitWarning
22
16
  from cognite_toolkit._cdf_tk.utils import load_yaml_inject_variables, safe_read, sanitize_filename
@@ -31,9 +25,6 @@ else:
31
25
  from typing_extensions import Self
32
26
 
33
27
 
34
- _COMPILED_PATTERN: dict[str, re.Pattern] = {}
35
-
36
-
37
28
  class Loader(ABC):
38
29
  """This is the base class for all loaders
39
30
 
@@ -44,20 +35,16 @@ class Loader(ABC):
44
35
  Class attributes:
45
36
  filetypes: The filetypes that are supported by this loader. This should be set in all subclasses.
46
37
  folder_name: The name of the folder in the build directory where the files are located. This should be set in all subclasses.
47
- filename_pattern: A regex pattern that is used to filter the files that are supported by this loader. This is used
48
- when two loaders have the same folder name to differentiate between them. If not set, all files are supported.
49
38
  dependencies: A set of loaders that must be loaded before this loader.
50
39
  exclude_filetypes: A set of filetypes that should be excluded from the supported filetypes.
51
40
  """
52
41
 
53
- filetypes: frozenset[str]
54
42
  folder_name: str
55
43
  kind: str
56
- filename_pattern: str = ""
57
44
  dependencies: "frozenset[type[ResourceCRUD]]" = frozenset()
58
- exclude_filetypes: frozenset[str] = frozenset()
59
45
  _doc_base_url: str = "https://api-docs.cognite.com/20230101/tag/"
60
46
  _doc_url: str = ""
47
+ sub_folder_name: str | None = None
61
48
 
62
49
  def __init__(self, client: ToolkitClient, build_dir: Path | None, console: Console | None = None) -> None:
63
50
  self.client = client
@@ -85,13 +72,12 @@ class Loader(ABC):
85
72
  def doc_url(cls) -> str:
86
73
  return cls._doc_base_url + cls._doc_url
87
74
 
88
- def find_files(self, dir_or_file: Path | None = None, include_formats: Set[str] | None = None) -> list[Path]:
75
+ def find_files(self, dir_or_file: Path | None = None) -> list[Path]:
89
76
  """Find all files that are supported by this loader in the given directory or file.
90
77
 
91
78
  Args:
92
79
  dir_or_file (Path): The directory or file to search in. If no path is given,
93
80
  the build directory is used.
94
- include_formats (set[str]): A set of file formats to include. If not set, all formats are included.
95
81
 
96
82
  Returns:
97
83
  list[Path]: A sorted list of all files that are supported by this loader.
@@ -105,12 +91,7 @@ class Loader(ABC):
105
91
  raise ValueError("Invalid file type")
106
92
  return [dir_or_file]
107
93
  elif dir_or_file.is_dir():
108
- file_paths = [
109
- file
110
- for file in dir_or_file.glob("**/*")
111
- if self.is_supported_file(file) and (include_formats is None or file.suffix in include_formats)
112
- ]
113
- return sorted(file_paths)
94
+ return sorted([file for file in dir_or_file.rglob("*") if self.is_supported_file(file)])
114
95
  else:
115
96
  return []
116
97
 
@@ -119,41 +100,23 @@ class Loader(ABC):
119
100
  return any(cls.is_supported_file(file) for file in directory.glob("**/*"))
120
101
 
121
102
  @classmethod
122
- def is_supported_file(cls, file: Path, force_pattern: bool = False) -> bool:
103
+ def is_supported_file(cls, file: Path) -> bool:
123
104
  """Check if hte file is supported by this loader.
124
105
 
125
106
  Args:
126
107
  file: The filepath to check.
127
- force_pattern: If True, the filename pattern is used to determine if the file is supported. If False, the
128
- file extension is used to determine if the file is supported (given that the
129
- RequireKind flag is enabled).
130
108
 
131
109
  Returns:
132
110
  bool: True if the file is supported, False otherwise.
133
111
 
134
112
  """
135
- if cls.filetypes and file.suffix[1:] not in cls.filetypes:
136
- return False
137
- if cls.exclude_filetypes and file.suffix[1:] in cls.exclude_filetypes:
138
- return False
139
- if force_pattern is False and not issubclass(cls, DataCRUD):
140
- return file.stem.casefold().endswith(cls.kind.casefold())
141
- else:
142
- if cls.filename_pattern:
143
- if cls.filename_pattern not in _COMPILED_PATTERN:
144
- _COMPILED_PATTERN[cls.filename_pattern] = re.compile(cls.filename_pattern, re.IGNORECASE)
145
- return _COMPILED_PATTERN[cls.filename_pattern].match(file.stem) is not None
146
- return True
113
+ return file.suffix in YAML_SUFFIX and file.stem.casefold().endswith(cls.kind.casefold())
147
114
 
148
115
 
149
116
  T_Loader = TypeVar("T_Loader", bound=Loader)
150
117
 
151
118
 
152
- class ResourceCRUD(
153
- Loader,
154
- ABC,
155
- Generic[T_ID, T_ResourceRequest, T_ResourceResponse, T_ResourceRequestList, T_ResourceResponseList],
156
- ):
119
+ class ResourceCRUD(Loader, ABC, Generic[T_ID, T_ResourceRequest, T_ResourceResponse]):
157
120
  """This is the base class for all resource CRUD.
158
121
 
159
122
  A resource loader consists of the following
@@ -166,13 +129,9 @@ class ResourceCRUD(
166
129
  Class attributes:
167
130
  resource_write_cls: The API write data class for the resource.
168
131
  resource_cls: The API read data class for the resource.
169
- list_cls: The API read list format for this resource.
170
- list_write_cls: The API write list format for this resource.
171
132
  yaml_cls: The File format for this resource. This is used to validate the user input.
172
133
  support_drop: Whether the resource supports the drop flag.
173
134
  support_update: Whether the resource supports the update operation.
174
- filetypes: The filetypes that are supported by this crud. This should not be set in the subclass, it
175
- should always be yaml and yml.
176
135
  dependencies: A set of other resource cruds that must be loaded before this crud.
177
136
  parent_resource: A set of other resource cruds that are parent resources to this resource. This is used
178
137
  to determine if the iterate method should return any resources when filtering by parent ids.
@@ -181,13 +140,10 @@ class ResourceCRUD(
181
140
  # Must be set in the subclass
182
141
  resource_write_cls: type[T_ResourceRequest]
183
142
  resource_cls: type[T_ResourceResponse]
184
- list_cls: type[T_ResourceResponseList]
185
- list_write_cls: type[T_ResourceRequestList]
186
143
  yaml_cls: type[ToolkitResource]
187
144
  # Optional to set in the subclass
188
145
  support_drop = True
189
146
  support_update = True
190
- filetypes = frozenset({"yaml", "yml"})
191
147
  dependencies: "frozenset[type[ResourceCRUD]]" = frozenset()
192
148
  # For example, TransformationNotification and Schedule has Transformation as the parent resource
193
149
  # This is used in the iterate method to ensure that nothing is returned if
@@ -213,14 +169,14 @@ class ResourceCRUD(
213
169
  raise NotImplementedError(f"get_required_capability must be implemented for {cls.__name__}.")
214
170
 
215
171
  @abstractmethod
216
- def create(self, items: T_ResourceRequestList) -> Sized:
172
+ def create(self, items: Sequence[T_ResourceRequest]) -> Sized:
217
173
  raise NotImplementedError
218
174
 
219
175
  @abstractmethod
220
- def retrieve(self, ids: SequenceNotStr[T_ID]) -> T_ResourceResponseList:
176
+ def retrieve(self, ids: SequenceNotStr[T_ID]) -> Sequence[T_ResourceResponse]:
221
177
  raise NotImplementedError
222
178
 
223
- def update(self, items: T_ResourceRequestList) -> Sized:
179
+ def update(self, items: Sequence[T_ResourceRequest]) -> Sized:
224
180
  raise NotImplementedError(f"Update is not supported for {type(self).__name__}.")
225
181
 
226
182
  @abstractmethod
@@ -406,9 +362,7 @@ class ResourceCRUD(
406
362
 
407
363
  # Helper methods
408
364
  @classmethod
409
- def get_ids(
410
- cls, items: Sequence[T_ResourceRequest | T_ResourceResponse | dict] | T_ResourceResponseList
411
- ) -> list[T_ID]:
365
+ def get_ids(cls, items: Sequence[T_ResourceRequest | T_ResourceResponse | dict]) -> list[T_ID]:
412
366
  return [cls.get_id(item) for item in items]
413
367
 
414
368
  @classmethod
@@ -420,10 +374,7 @@ class ResourceCRUD(
420
374
  )
421
375
 
422
376
 
423
- class ResourceContainerCRUD(
424
- ResourceCRUD[T_ID, T_ResourceRequest, T_ResourceResponse, T_ResourceRequestList, T_ResourceResponseList],
425
- ABC,
426
- ):
377
+ class ResourceContainerCRUD(ResourceCRUD[T_ID, T_ResourceRequest, T_ResourceResponse], ABC):
427
378
  """This is the base class for all resource CRUD' containers.
428
379
 
429
380
  A resource container CRUD is a resource that contains data. For example, Timeseries contains datapoints, and another
@@ -471,10 +422,3 @@ class DataCRUD(Loader, ABC):
471
422
  @abstractmethod
472
423
  def upload(self, state: "BuildEnvironment", dry_run: bool) -> Iterable[tuple[str, int]]:
473
424
  raise NotImplementedError
474
-
475
- def _find_data_files(self, directory: Path) -> list[Path]:
476
- return [
477
- path
478
- for path in directory.rglob("*")
479
- if path.is_file() and path.name not in EXCL_FILES and self.is_supported_file(path)
480
- ]
@@ -6,14 +6,12 @@ from typing import TYPE_CHECKING, cast, final
6
6
  import pandas as pd
7
7
  from cognite.client.data_classes import FileMetadataWrite
8
8
 
9
- from cognite_toolkit._cdf_tk.client.data_classes.extendable_cognite_file import ExtendableCogniteFileApply
10
- from cognite_toolkit._cdf_tk.client.data_classes.raw import RawTable
9
+ from cognite_toolkit._cdf_tk.client.data_classes.legacy.extendable_cognite_file import ExtendableCogniteFileApply
10
+ from cognite_toolkit._cdf_tk.client.data_classes.legacy.raw import RawTable
11
11
  from cognite_toolkit._cdf_tk.constants import BUILD_FOLDER_ENCODING
12
12
  from cognite_toolkit._cdf_tk.protocols import (
13
13
  T_ResourceRequest,
14
- T_ResourceRequestList,
15
14
  T_ResourceResponse,
16
- T_ResourceResponseList,
17
15
  )
18
16
  from cognite_toolkit._cdf_tk.utils import read_yaml_content, safe_read
19
17
  from cognite_toolkit._cdf_tk.utils.file import read_csv
@@ -30,7 +28,6 @@ class DatapointsCRUD(DataCRUD):
30
28
  item_name = "datapoints"
31
29
  folder_name = "timeseries"
32
30
  kind = "Datapoints"
33
- filetypes = frozenset({"csv", "parquet"})
34
31
  dependencies = frozenset({TimeSeriesCRUD})
35
32
  _doc_url = "Time-series/operation/postMultiTimeSeriesDatapoints"
36
33
 
@@ -45,7 +42,9 @@ class DatapointsCRUD(DataCRUD):
45
42
  resource_directories = state.built_resources[self.folder_name].get_resource_directories(self.folder_name)
46
43
 
47
44
  for resource_dir in resource_directories:
48
- for datafile in self._find_data_files(resource_dir):
45
+ for datafile in resource_dir.rglob("*"):
46
+ if not datafile.stem.casefold().endswith(self.kind.casefold()):
47
+ continue
49
48
  if datafile.suffix == ".csv":
50
49
  # The replacement is used to ensure that we read exactly the same file on Windows and Linux
51
50
  file_content = datafile.read_bytes().replace(b"\r\n", b"\n").decode("utf-8")
@@ -54,7 +53,7 @@ class DatapointsCRUD(DataCRUD):
54
53
  elif datafile.suffix == ".parquet":
55
54
  data = pd.read_parquet(datafile, engine="pyarrow")
56
55
  else:
57
- raise ValueError(f"Unsupported file type {datafile.suffix} for {datafile.name}")
56
+ continue
58
57
  timeseries_ids = list(data.columns)
59
58
  if len(timeseries_ids) == 1:
60
59
  ts_str = timeseries_ids[0]
@@ -89,12 +88,6 @@ class FileCRUD(DataCRUD):
89
88
  item_name = "file contents"
90
89
  folder_name = "files"
91
90
  kind = "File"
92
- filetypes = frozenset()
93
- exclude_filetype: frozenset[str] = frozenset({})
94
- filename_pattern = (
95
- # Exclude FileMetadata and CogniteFile
96
- r"(?i)^(?!.*(?:FileMetadata|CogniteFile)$).*$"
97
- )
98
91
  dependencies = frozenset({FileMetadataCRUD, CogniteFileCRUD})
99
92
  _doc_url = "Files/operation/initFileUpload"
100
93
 
@@ -135,17 +128,16 @@ class FileCRUD(DataCRUD):
135
128
  @staticmethod
136
129
  def _read_metadata(
137
130
  destination: Path,
138
- loader: type[
139
- ResourceCRUD[T_ID, T_ResourceRequest, T_ResourceResponse, T_ResourceRequestList, T_ResourceResponseList]
140
- ],
131
+ loader: type[ResourceCRUD[T_ID, T_ResourceRequest, T_ResourceResponse]],
141
132
  identifier: T_ID,
142
133
  ) -> T_ResourceRequest:
143
134
  built_content = read_yaml_content(safe_read(destination, encoding=BUILD_FOLDER_ENCODING))
144
135
  if isinstance(built_content, dict):
145
136
  return loader.resource_write_cls._load(built_content)
146
137
  elif isinstance(built_content, list):
138
+ write_resources = (loader.resource_write_cls._load(content) for content in built_content)
147
139
  try:
148
- return next(m for m in loader.list_write_cls.load(built_content) if loader.get_id(m) == identifier)
140
+ return next(m for m in write_resources if loader.get_id(m) == identifier)
149
141
  except StopIteration:
150
142
  raise RuntimeError(f"Missing metadata for {destination.as_posix()}")
151
143
 
@@ -156,7 +148,6 @@ class FileCRUD(DataCRUD):
156
148
  class RawFileCRUD(DataCRUD):
157
149
  item_name = "rows"
158
150
  folder_name = "raw"
159
- filetypes = frozenset({"csv", "parquet"})
160
151
  kind = "Raw"
161
152
  dependencies = frozenset({RawTableCRUD})
162
153
  _doc_url = "Raw/operation/postRows"
@@ -176,7 +167,7 @@ class RawFileCRUD(DataCRUD):
176
167
  datafile = next(
177
168
  (
178
169
  resource.source.path.with_suffix(f".{file_type}")
179
- for file_type in self.filetypes
170
+ for file_type in ["csv", "parquet"]
180
171
  if (resource.source.path.with_suffix(f".{file_type}").exists())
181
172
  ),
182
173
  None,
@@ -13,7 +13,7 @@ from .datamodel import (
13
13
  ViewCRUD,
14
14
  )
15
15
  from .extraction_pipeline import ExtractionPipelineConfigCRUD, ExtractionPipelineCRUD
16
- from .fieldops import InFieldLocationConfigCRUD, InfieldV1CRUD
16
+ from .fieldops import InFieldCDMLocationConfigCRUD, InFieldLocationConfigCRUD, InfieldV1CRUD
17
17
  from .file import CogniteFileCRUD, FileMetadataCRUD
18
18
  from .function import FunctionCRUD, FunctionScheduleCRUD
19
19
  from .group_scoped import GroupResourceScopedCRUD
@@ -35,6 +35,7 @@ from .robotics import (
35
35
  RoboticMapCRUD,
36
36
  RoboticsDataPostProcessingCRUD,
37
37
  )
38
+ from .streams import StreamCRUD
38
39
  from .three_d_model import ThreeDModelCRUD
39
40
  from .timeseries import DatapointSubscriptionCRUD, TimeSeriesCRUD
40
41
  from .transformation import TransformationCRUD, TransformationNotificationCRUD, TransformationScheduleCRUD
@@ -63,6 +64,7 @@ __all__ = [
63
64
  "HostedExtractorJobCRUD",
64
65
  "HostedExtractorMappingCRUD",
65
66
  "HostedExtractorSourceCRUD",
67
+ "InFieldCDMLocationConfigCRUD",
66
68
  "InFieldLocationConfigCRUD",
67
69
  "InfieldV1CRUD",
68
70
  "LabelCRUD",
@@ -82,6 +84,7 @@ __all__ = [
82
84
  "SequenceCRUD",
83
85
  "SequenceRowCRUD",
84
86
  "SpaceCRUD",
87
+ "StreamCRUD",
85
88
  "StreamlitCRUD",
86
89
  "ThreeDModelCRUD",
87
90
  "TimeSeriesCRUD",
@@ -1,8 +1,8 @@
1
1
  from collections.abc import Hashable, Iterable, Sequence
2
2
  from typing import Any
3
3
 
4
- from cognite.client.data_classes.agents import Agent, AgentList, AgentUpsert, AgentUpsertList
5
- from cognite.client.data_classes.capabilities import Capability
4
+ from cognite.client.data_classes.agents import Agent, AgentList, AgentUpsert
5
+ from cognite.client.data_classes.capabilities import AgentsAcl, Capability
6
6
  from cognite.client.exceptions import CogniteAPIError
7
7
  from cognite.client.utils.useful_types import SequenceNotStr
8
8
 
@@ -11,13 +11,10 @@ from cognite_toolkit._cdf_tk.resource_classes import AgentYAML
11
11
  from cognite_toolkit._cdf_tk.utils.diff_list import diff_list_hashable, diff_list_identifiable
12
12
 
13
13
 
14
- class AgentCRUD(ResourceCRUD[str, AgentUpsert, Agent, AgentUpsertList, AgentList]):
14
+ class AgentCRUD(ResourceCRUD[str, AgentUpsert, Agent]):
15
15
  folder_name = "agents"
16
- filename_pattern = r".*\.Agent$" # Matches all yaml files whose stem ends with '.Agent'.
17
16
  resource_cls = Agent
18
17
  resource_write_cls = AgentUpsert
19
- list_cls = AgentList
20
- list_write_cls = AgentUpsertList
21
18
  kind = "Agent"
22
19
  yaml_cls = AgentYAML
23
20
  _doc_base_url = ""
@@ -37,15 +34,20 @@ class AgentCRUD(ResourceCRUD[str, AgentUpsert, Agent, AgentUpsertList, AgentList
37
34
  def get_required_capability(
38
35
  cls, items: Sequence[AgentUpsert] | None, read_only: bool
39
36
  ) -> Capability | list[Capability]:
40
- return []
37
+ if not items and items is not None:
38
+ return []
41
39
 
42
- def create(self, items: AgentUpsertList) -> AgentList:
40
+ actions = [AgentsAcl.Action.READ] if read_only else [AgentsAcl.Action.READ, AgentsAcl.Action.WRITE]
41
+
42
+ return AgentsAcl(actions, AgentsAcl.Scope.All())
43
+
44
+ def create(self, items: Sequence[AgentUpsert]) -> AgentList:
43
45
  return self.client.agents.upsert(items)
44
46
 
45
47
  def retrieve(self, ids: SequenceNotStr[str]) -> AgentList:
46
48
  return self.client.agents.retrieve(ids, ignore_unknown_ids=True)
47
49
 
48
- def update(self, items: AgentUpsertList) -> AgentList:
50
+ def update(self, items: Sequence[AgentUpsert]) -> AgentList:
49
51
  return self.client.agents.upsert(items)
50
52
 
51
53
  def delete(self, ids: SequenceNotStr[str]) -> int:
@@ -30,11 +30,9 @@ from cognite.client.data_classes.iam import (
30
30
  Group,
31
31
  GroupList,
32
32
  GroupWrite,
33
- GroupWriteList,
34
33
  SecurityCategory,
35
34
  SecurityCategoryList,
36
35
  SecurityCategoryWrite,
37
- SecurityCategoryWriteList,
38
36
  )
39
37
  from cognite.client.exceptions import CogniteAPIError, CogniteNotFoundError
40
38
  from cognite.client.utils.useful_types import SequenceNotStr
@@ -43,7 +41,7 @@ from rich.console import Console
43
41
  from rich.markup import escape
44
42
 
45
43
  from cognite_toolkit._cdf_tk.client import ToolkitClient
46
- from cognite_toolkit._cdf_tk.client.data_classes.raw import RawDatabase, RawTable
44
+ from cognite_toolkit._cdf_tk.client.data_classes.legacy.raw import RawDatabase, RawTable
47
45
  from cognite_toolkit._cdf_tk.cruds._base_cruds import ResourceCRUD
48
46
  from cognite_toolkit._cdf_tk.exceptions import ToolkitWrongResourceError
49
47
  from cognite_toolkit._cdf_tk.resource_classes import GroupYAML, SecurityCategoriesYAML
@@ -65,14 +63,11 @@ class _ReplaceMethod:
65
63
  id_name: str
66
64
 
67
65
 
68
- class GroupCRUD(ResourceCRUD[str, GroupWrite, Group, GroupWriteList, GroupList]):
66
+ class GroupCRUD(ResourceCRUD[str, GroupWrite, Group]):
69
67
  folder_name = "auth"
70
- filename_pattern = r"^(?!.*SecurityCategory$).*"
71
68
  kind = "Group"
72
69
  resource_cls = Group
73
70
  resource_write_cls = GroupWrite
74
- list_cls = GroupList
75
- list_write_cls = GroupWriteList
76
71
  yaml_cls = GroupYAML
77
72
  resource_scopes = frozenset(
78
73
  {
@@ -479,14 +474,9 @@ class GroupAllScopedCRUD(GroupCRUD):
479
474
 
480
475
 
481
476
  @final
482
- class SecurityCategoryCRUD(
483
- ResourceCRUD[str, SecurityCategoryWrite, SecurityCategory, SecurityCategoryWriteList, SecurityCategoryList]
484
- ):
485
- filename_pattern = r"^.*SecurityCategory$" # Matches all yaml files who's stem ends with *SecurityCategory.
477
+ class SecurityCategoryCRUD(ResourceCRUD[str, SecurityCategoryWrite, SecurityCategory]):
486
478
  resource_cls = SecurityCategory
487
479
  resource_write_cls = SecurityCategoryWrite
488
- list_cls = SecurityCategoryList
489
- list_write_cls = SecurityCategoryWriteList
490
480
  kind = "SecurityCategory"
491
481
  yaml_cls = SecurityCategoriesYAML
492
482
  folder_name = "auth"
@@ -534,7 +524,7 @@ class SecurityCategoryCRUD(
534
524
  SecurityCategoriesAcl.Scope.All(),
535
525
  )
536
526
 
537
- def create(self, items: SecurityCategoryWriteList) -> SecurityCategoryList:
527
+ def create(self, items: Sequence[SecurityCategoryWrite]) -> SecurityCategoryList:
538
528
  return self.client.iam.security_categories.create(items)
539
529
 
540
530
  def retrieve(self, ids: SequenceNotStr[str]) -> SecurityCategoryList:
@@ -542,7 +532,7 @@ class SecurityCategoryCRUD(
542
532
  categories = self.client.iam.security_categories.list(limit=-1)
543
533
  return SecurityCategoryList([c for c in categories if c.name in names])
544
534
 
545
- def update(self, items: SecurityCategoryWriteList) -> SecurityCategoryList:
535
+ def update(self, items: Sequence[SecurityCategoryWrite]) -> SecurityCategoryList:
546
536
  items_by_name = {item.name: item for item in items}
547
537
  retrieved = self.retrieve(list(items_by_name.keys()))
548
538
  retrieved_by_name = {item.name: item for item in retrieved}