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.
- cognite_toolkit/_cdf.py +21 -23
- cognite_toolkit/_cdf_tk/apps/__init__.py +4 -0
- cognite_toolkit/_cdf_tk/apps/_core_app.py +19 -5
- cognite_toolkit/_cdf_tk/apps/_data_app.py +1 -1
- cognite_toolkit/_cdf_tk/apps/_dev_app.py +86 -0
- cognite_toolkit/_cdf_tk/apps/_download_app.py +693 -25
- cognite_toolkit/_cdf_tk/apps/_dump_app.py +44 -102
- cognite_toolkit/_cdf_tk/apps/_import_app.py +41 -0
- cognite_toolkit/_cdf_tk/apps/_landing_app.py +18 -4
- cognite_toolkit/_cdf_tk/apps/_migrate_app.py +424 -9
- cognite_toolkit/_cdf_tk/apps/_modules_app.py +0 -3
- cognite_toolkit/_cdf_tk/apps/_purge.py +15 -43
- cognite_toolkit/_cdf_tk/apps/_run.py +11 -0
- cognite_toolkit/_cdf_tk/apps/_upload_app.py +45 -6
- cognite_toolkit/_cdf_tk/builders/__init__.py +2 -2
- cognite_toolkit/_cdf_tk/builders/_base.py +28 -42
- cognite_toolkit/_cdf_tk/builders/_raw.py +1 -1
- cognite_toolkit/_cdf_tk/cdf_toml.py +20 -1
- cognite_toolkit/_cdf_tk/client/_toolkit_client.py +32 -12
- cognite_toolkit/_cdf_tk/client/api/infield.py +114 -17
- cognite_toolkit/_cdf_tk/client/api/{canvas.py → legacy/canvas.py} +15 -7
- cognite_toolkit/_cdf_tk/client/api/{charts.py → legacy/charts.py} +1 -1
- cognite_toolkit/_cdf_tk/client/api/{extended_data_modeling.py → legacy/extended_data_modeling.py} +1 -1
- cognite_toolkit/_cdf_tk/client/api/{extended_files.py → legacy/extended_files.py} +2 -2
- cognite_toolkit/_cdf_tk/client/api/{extended_functions.py → legacy/extended_functions.py} +15 -18
- cognite_toolkit/_cdf_tk/client/api/{extended_raw.py → legacy/extended_raw.py} +1 -1
- cognite_toolkit/_cdf_tk/client/api/{extended_timeseries.py → legacy/extended_timeseries.py} +5 -2
- cognite_toolkit/_cdf_tk/client/api/{location_filters.py → legacy/location_filters.py} +1 -1
- cognite_toolkit/_cdf_tk/client/api/legacy/robotics/__init__.py +8 -0
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/capabilities.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/data_postprocessing.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/frames.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/locations.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/maps.py +1 -1
- cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/robots.py +2 -2
- cognite_toolkit/_cdf_tk/client/api/{search_config.py → legacy/search_config.py} +5 -1
- cognite_toolkit/_cdf_tk/client/api/migration.py +177 -4
- cognite_toolkit/_cdf_tk/client/api/project.py +9 -8
- cognite_toolkit/_cdf_tk/client/api/search.py +2 -2
- cognite_toolkit/_cdf_tk/client/api/streams.py +88 -0
- cognite_toolkit/_cdf_tk/client/api/three_d.py +384 -0
- cognite_toolkit/_cdf_tk/client/data_classes/api_classes.py +13 -0
- cognite_toolkit/_cdf_tk/client/data_classes/base.py +37 -33
- cognite_toolkit/_cdf_tk/client/data_classes/charts_data.py +95 -213
- cognite_toolkit/_cdf_tk/client/data_classes/infield.py +32 -18
- cognite_toolkit/_cdf_tk/client/data_classes/instance_api.py +18 -13
- cognite_toolkit/_cdf_tk/client/data_classes/legacy/__init__.py +0 -0
- cognite_toolkit/_cdf_tk/client/data_classes/{canvas.py → legacy/canvas.py} +47 -4
- cognite_toolkit/_cdf_tk/client/data_classes/{charts.py → legacy/charts.py} +3 -3
- cognite_toolkit/_cdf_tk/client/data_classes/{migration.py → legacy/migration.py} +10 -2
- cognite_toolkit/_cdf_tk/client/data_classes/streams.py +90 -0
- cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +112 -0
- cognite_toolkit/_cdf_tk/client/testing.py +42 -18
- cognite_toolkit/_cdf_tk/commands/__init__.py +7 -6
- cognite_toolkit/_cdf_tk/commands/_changes.py +3 -42
- cognite_toolkit/_cdf_tk/commands/_download.py +21 -11
- cognite_toolkit/_cdf_tk/commands/_migrate/__init__.py +0 -2
- cognite_toolkit/_cdf_tk/commands/_migrate/command.py +22 -20
- cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +140 -92
- cognite_toolkit/_cdf_tk/commands/_migrate/creators.py +1 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +108 -26
- cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +448 -45
- cognite_toolkit/_cdf_tk/commands/_migrate/data_model.py +1 -0
- cognite_toolkit/_cdf_tk/commands/_migrate/default_mappings.py +6 -6
- cognite_toolkit/_cdf_tk/commands/_migrate/issues.py +52 -1
- cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +377 -11
- cognite_toolkit/_cdf_tk/commands/_migrate/selectors.py +9 -4
- cognite_toolkit/_cdf_tk/commands/_profile.py +1 -1
- cognite_toolkit/_cdf_tk/commands/_purge.py +36 -39
- cognite_toolkit/_cdf_tk/commands/_questionary_style.py +16 -0
- cognite_toolkit/_cdf_tk/commands/_upload.py +109 -86
- cognite_toolkit/_cdf_tk/commands/about.py +221 -0
- cognite_toolkit/_cdf_tk/commands/auth.py +19 -12
- cognite_toolkit/_cdf_tk/commands/build_cmd.py +16 -62
- cognite_toolkit/_cdf_tk/commands/build_v2/__init__.py +0 -0
- cognite_toolkit/_cdf_tk/commands/build_v2/build_cmd.py +241 -0
- cognite_toolkit/_cdf_tk/commands/build_v2/build_input.py +85 -0
- cognite_toolkit/_cdf_tk/commands/build_v2/build_issues.py +27 -0
- cognite_toolkit/_cdf_tk/commands/clean.py +63 -16
- cognite_toolkit/_cdf_tk/commands/deploy.py +20 -17
- cognite_toolkit/_cdf_tk/commands/dump_resource.py +10 -8
- cognite_toolkit/_cdf_tk/commands/init.py +225 -3
- cognite_toolkit/_cdf_tk/commands/modules.py +20 -44
- cognite_toolkit/_cdf_tk/commands/pull.py +6 -19
- cognite_toolkit/_cdf_tk/commands/resources.py +179 -0
- cognite_toolkit/_cdf_tk/commands/run.py +1 -1
- cognite_toolkit/_cdf_tk/constants.py +20 -1
- cognite_toolkit/_cdf_tk/cruds/__init__.py +19 -5
- cognite_toolkit/_cdf_tk/cruds/_base_cruds.py +14 -70
- cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +10 -19
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/__init__.py +4 -1
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/agent.py +11 -9
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +5 -15
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +45 -44
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/configuration.py +5 -12
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/data_organization.py +4 -13
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/datamodel.py +206 -67
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/extraction_pipeline.py +6 -18
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +126 -35
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +7 -28
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/function.py +23 -30
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/hosted_extractors.py +12 -30
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/industrial_tool.py +4 -8
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +4 -16
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/migration.py +5 -13
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/raw.py +5 -11
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +3 -8
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/robotics.py +16 -45
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +94 -0
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/three_d_model.py +3 -7
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +5 -15
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/transformation.py +75 -32
- cognite_toolkit/_cdf_tk/cruds/_resource_cruds/workflow.py +20 -40
- cognite_toolkit/_cdf_tk/cruds/_worker.py +24 -36
- cognite_toolkit/_cdf_tk/data_classes/_module_toml.py +1 -0
- cognite_toolkit/_cdf_tk/feature_flags.py +16 -36
- cognite_toolkit/_cdf_tk/plugins.py +2 -1
- cognite_toolkit/_cdf_tk/resource_classes/__init__.py +4 -0
- cognite_toolkit/_cdf_tk/resource_classes/capabilities.py +12 -0
- cognite_toolkit/_cdf_tk/resource_classes/functions.py +3 -1
- cognite_toolkit/_cdf_tk/resource_classes/infield_cdm_location_config.py +109 -0
- cognite_toolkit/_cdf_tk/resource_classes/migration.py +8 -17
- cognite_toolkit/_cdf_tk/resource_classes/search_config.py +1 -1
- cognite_toolkit/_cdf_tk/resource_classes/streams.py +29 -0
- cognite_toolkit/_cdf_tk/resource_classes/workflow_version.py +164 -5
- cognite_toolkit/_cdf_tk/storageio/__init__.py +9 -21
- cognite_toolkit/_cdf_tk/storageio/_annotations.py +19 -16
- cognite_toolkit/_cdf_tk/storageio/_applications.py +340 -28
- cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +67 -104
- cognite_toolkit/_cdf_tk/storageio/_base.py +61 -29
- cognite_toolkit/_cdf_tk/storageio/_datapoints.py +276 -20
- cognite_toolkit/_cdf_tk/storageio/_file_content.py +435 -0
- cognite_toolkit/_cdf_tk/storageio/_instances.py +35 -3
- cognite_toolkit/_cdf_tk/storageio/_raw.py +26 -0
- cognite_toolkit/_cdf_tk/storageio/selectors/__init__.py +71 -4
- cognite_toolkit/_cdf_tk/storageio/selectors/_base.py +14 -2
- cognite_toolkit/_cdf_tk/storageio/selectors/_canvas.py +14 -0
- cognite_toolkit/_cdf_tk/storageio/selectors/_charts.py +14 -0
- cognite_toolkit/_cdf_tk/storageio/selectors/_datapoints.py +23 -3
- cognite_toolkit/_cdf_tk/storageio/selectors/_file_content.py +164 -0
- cognite_toolkit/_cdf_tk/storageio/selectors/_three_d.py +34 -0
- cognite_toolkit/_cdf_tk/tk_warnings/other.py +4 -0
- cognite_toolkit/_cdf_tk/tracker.py +2 -2
- cognite_toolkit/_cdf_tk/utils/cdf.py +1 -1
- cognite_toolkit/_cdf_tk/utils/dtype_conversion.py +9 -3
- cognite_toolkit/_cdf_tk/utils/fileio/__init__.py +2 -0
- cognite_toolkit/_cdf_tk/utils/fileio/_base.py +5 -1
- cognite_toolkit/_cdf_tk/utils/fileio/_readers.py +112 -20
- cognite_toolkit/_cdf_tk/utils/fileio/_writers.py +15 -15
- cognite_toolkit/_cdf_tk/utils/http_client/__init__.py +28 -0
- cognite_toolkit/_cdf_tk/utils/http_client/_client.py +285 -18
- cognite_toolkit/_cdf_tk/utils/http_client/_data_classes.py +56 -4
- cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py +247 -0
- cognite_toolkit/_cdf_tk/utils/http_client/_tracker.py +5 -2
- cognite_toolkit/_cdf_tk/utils/interactive_select.py +60 -18
- cognite_toolkit/_cdf_tk/utils/sql_parser.py +2 -3
- cognite_toolkit/_cdf_tk/utils/useful_types.py +6 -2
- cognite_toolkit/_cdf_tk/validation.py +83 -1
- cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
- cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
- cognite_toolkit/_resources/cdf.toml +5 -4
- cognite_toolkit/_version.py +1 -1
- cognite_toolkit/config.dev.yaml +13 -0
- {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.39.dist-info}/METADATA +24 -24
- cognite_toolkit-0.7.39.dist-info/RECORD +322 -0
- cognite_toolkit-0.7.39.dist-info/WHEEL +4 -0
- {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.39.dist-info}/entry_points.txt +1 -0
- cognite_toolkit/_cdf_tk/client/api/robotics/__init__.py +0 -3
- cognite_toolkit/_cdf_tk/commands/_migrate/canvas.py +0 -201
- cognite_toolkit/_cdf_tk/commands/dump_data.py +0 -489
- cognite_toolkit/_cdf_tk/commands/featureflag.py +0 -27
- cognite_toolkit/_cdf_tk/prototypes/import_app.py +0 -41
- cognite_toolkit/_cdf_tk/utils/table_writers.py +0 -434
- cognite_toolkit-0.6.97.dist-info/RECORD +0 -306
- cognite_toolkit-0.6.97.dist-info/WHEEL +0 -4
- cognite_toolkit-0.6.97.dist-info/licenses/LICENSE +0 -18
- /cognite_toolkit/_cdf_tk/{prototypes/commands → client/api/legacy}/__init__.py +0 -0
- /cognite_toolkit/_cdf_tk/client/api/{dml.py → legacy/dml.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/api/{fixed_transformations.py → legacy/fixed_transformations.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/api.py +0 -0
- /cognite_toolkit/_cdf_tk/client/api/{robotics → legacy/robotics}/utlis.py +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{apm_config_v1.py → legacy/apm_config_v1.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{extendable_cognite_file.py → legacy/extendable_cognite_file.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetadata.py → legacy/extended_filemetadata.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{extended_filemetdata.py → legacy/extended_filemetdata.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{extended_timeseries.py → legacy/extended_timeseries.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{functions.py → legacy/functions.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{graphql_data_models.py → legacy/graphql_data_models.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{instances.py → legacy/instances.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{location_filters.py → legacy/location_filters.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{pending_instances_ids.py → legacy/pending_instances_ids.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{project.py → legacy/project.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{raw.py → legacy/raw.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{robotics.py → legacy/robotics.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{search_config.py → legacy/search_config.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{sequences.py → legacy/sequences.py} +0 -0
- /cognite_toolkit/_cdf_tk/client/data_classes/{streamlit_.py → legacy/streamlit_.py} +0 -0
- /cognite_toolkit/_cdf_tk/{prototypes/commands/import_.py → commands/_import_cmd.py} +0 -0
|
@@ -1,25 +1,68 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from collections.abc import Callable, Sequence
|
|
4
|
+
from typing import Generic, Literal, cast
|
|
5
|
+
from uuid import uuid4
|
|
3
6
|
|
|
4
|
-
from cognite.client.data_classes.
|
|
5
|
-
|
|
7
|
+
from cognite.client.data_classes.data_modeling import (
|
|
8
|
+
EdgeApply,
|
|
9
|
+
InstanceApply,
|
|
10
|
+
NodeApply,
|
|
11
|
+
NodeId,
|
|
12
|
+
View,
|
|
13
|
+
ViewId,
|
|
6
14
|
)
|
|
7
|
-
from cognite.client.
|
|
15
|
+
from cognite.client.exceptions import CogniteException
|
|
8
16
|
|
|
9
17
|
from cognite_toolkit._cdf_tk.client import ToolkitClient
|
|
10
|
-
from cognite_toolkit._cdf_tk.client.data_classes.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
from cognite_toolkit._cdf_tk.client.data_classes.charts_data import (
|
|
19
|
+
ChartCoreTimeseries,
|
|
20
|
+
ChartSource,
|
|
21
|
+
ChartTimeseries,
|
|
22
|
+
)
|
|
23
|
+
from cognite_toolkit._cdf_tk.client.data_classes.instance_api import InstanceIdentifier
|
|
24
|
+
from cognite_toolkit._cdf_tk.client.data_classes.legacy.canvas import (
|
|
25
|
+
ContainerReferenceApply,
|
|
26
|
+
FdmInstanceContainerReferenceApply,
|
|
27
|
+
IndustrialCanvas,
|
|
28
|
+
IndustrialCanvasApply,
|
|
29
|
+
)
|
|
30
|
+
from cognite_toolkit._cdf_tk.client.data_classes.legacy.charts import Chart, ChartWrite
|
|
31
|
+
from cognite_toolkit._cdf_tk.client.data_classes.legacy.migration import ResourceViewMappingApply
|
|
32
|
+
from cognite_toolkit._cdf_tk.client.data_classes.three_d import (
|
|
33
|
+
AssetMappingDMRequest,
|
|
34
|
+
AssetMappingResponse,
|
|
35
|
+
NodeReference,
|
|
36
|
+
RevisionStatus,
|
|
37
|
+
ThreeDModelResponse,
|
|
38
|
+
)
|
|
39
|
+
from cognite_toolkit._cdf_tk.commands._migrate.conversion import DirectRelationCache, asset_centric_to_dm
|
|
40
|
+
from cognite_toolkit._cdf_tk.commands._migrate.data_classes import (
|
|
41
|
+
Model,
|
|
42
|
+
ThreeDMigrationRequest,
|
|
43
|
+
ThreeDRevisionMigrationRequest,
|
|
44
|
+
)
|
|
45
|
+
from cognite_toolkit._cdf_tk.commands._migrate.default_mappings import create_default_mappings
|
|
46
|
+
from cognite_toolkit._cdf_tk.commands._migrate.issues import (
|
|
47
|
+
CanvasMigrationIssue,
|
|
48
|
+
ChartMigrationIssue,
|
|
49
|
+
ConversionIssue,
|
|
50
|
+
MigrationIssue,
|
|
51
|
+
ThreeDModelMigrationIssue,
|
|
52
|
+
)
|
|
15
53
|
from cognite_toolkit._cdf_tk.constants import MISSING_INSTANCE_SPACE
|
|
16
|
-
from cognite_toolkit._cdf_tk.exceptions import ToolkitValueError
|
|
17
|
-
from cognite_toolkit._cdf_tk.
|
|
54
|
+
from cognite_toolkit._cdf_tk.exceptions import ToolkitMigrationError, ToolkitValueError
|
|
55
|
+
from cognite_toolkit._cdf_tk.protocols import T_ResourceRequest, T_ResourceResponse
|
|
56
|
+
from cognite_toolkit._cdf_tk.storageio._base import T_Selector
|
|
57
|
+
from cognite_toolkit._cdf_tk.storageio.selectors import CanvasSelector, ChartSelector, ThreeDSelector
|
|
18
58
|
from cognite_toolkit._cdf_tk.utils import humanize_collection
|
|
19
|
-
from cognite_toolkit._cdf_tk.utils.useful_types import
|
|
59
|
+
from cognite_toolkit._cdf_tk.utils.useful_types import T_AssetCentricResourceExtended
|
|
20
60
|
|
|
61
|
+
from .data_classes import AssetCentricMapping
|
|
62
|
+
from .selectors import AssetCentricMigrationSelector
|
|
21
63
|
|
|
22
|
-
|
|
64
|
+
|
|
65
|
+
class DataMapper(Generic[T_Selector, T_ResourceResponse, T_ResourceRequest], ABC):
|
|
23
66
|
def prepare(self, source_selector: T_Selector) -> None:
|
|
24
67
|
"""Prepare the data mapper with the given source selector.
|
|
25
68
|
|
|
@@ -31,7 +74,7 @@ class DataMapper(Generic[T_Selector, T_CogniteResource, T_WriteCogniteResource],
|
|
|
31
74
|
pass
|
|
32
75
|
|
|
33
76
|
@abstractmethod
|
|
34
|
-
def map(self, source:
|
|
77
|
+
def map(self, source: Sequence[T_ResourceResponse]) -> Sequence[tuple[T_ResourceRequest | None, MigrationIssue]]:
|
|
35
78
|
"""Map a chunk of source data to the target format.
|
|
36
79
|
|
|
37
80
|
Args:
|
|
@@ -45,28 +88,27 @@ class DataMapper(Generic[T_Selector, T_CogniteResource, T_WriteCogniteResource],
|
|
|
45
88
|
|
|
46
89
|
|
|
47
90
|
class AssetCentricMapper(
|
|
48
|
-
DataMapper[AssetCentricMigrationSelector, AssetCentricMapping[
|
|
91
|
+
DataMapper[AssetCentricMigrationSelector, AssetCentricMapping[T_AssetCentricResourceExtended], InstanceApply]
|
|
49
92
|
):
|
|
50
93
|
def __init__(self, client: ToolkitClient) -> None:
|
|
51
94
|
self.client = client
|
|
52
95
|
self._ingestion_view_by_id: dict[ViewId, View] = {}
|
|
53
|
-
self._view_mapping_by_id: dict[str,
|
|
54
|
-
|
|
55
|
-
# to them from files, events, and time series.
|
|
56
|
-
self._asset_mapping_by_id: dict[int, DirectRelationReference] = {}
|
|
57
|
-
self._source_system_mapping_by_id: dict[str, DirectRelationReference] = {}
|
|
96
|
+
self._view_mapping_by_id: dict[str, ResourceViewMappingApply] = {}
|
|
97
|
+
self._direct_relation_cache = DirectRelationCache(client)
|
|
58
98
|
|
|
59
99
|
def prepare(self, source_selector: AssetCentricMigrationSelector) -> None:
|
|
60
100
|
ingestion_view_ids = source_selector.get_ingestion_mappings()
|
|
61
101
|
ingestion_views = self.client.migration.resource_view_mapping.retrieve(ingestion_view_ids)
|
|
62
|
-
|
|
102
|
+
defaults = {mapping.external_id: mapping for mapping in create_default_mappings()}
|
|
103
|
+
# Custom mappings from CDF override the default mappings
|
|
104
|
+
self._view_mapping_by_id = defaults | {view.external_id: view.as_write() for view in ingestion_views}
|
|
63
105
|
missing_mappings = set(ingestion_view_ids) - set(self._view_mapping_by_id.keys())
|
|
64
106
|
if missing_mappings:
|
|
65
107
|
raise ToolkitValueError(
|
|
66
108
|
f"The following ingestion views were not found: {humanize_collection(missing_mappings)}"
|
|
67
109
|
)
|
|
68
110
|
|
|
69
|
-
view_ids = list({
|
|
111
|
+
view_ids = list({mapping.view_id for mapping in self._view_mapping_by_id.values()})
|
|
70
112
|
views = self.client.data_modeling.views.retrieve(view_ids)
|
|
71
113
|
self._ingestion_view_by_id = {view.as_id(): view for view in views}
|
|
72
114
|
missing_views = set(view_ids) - set(self._ingestion_view_by_id.keys())
|
|
@@ -74,22 +116,23 @@ class AssetCentricMapper(
|
|
|
74
116
|
raise ToolkitValueError(
|
|
75
117
|
f"The following ingestion views were not found in Data Modeling: {humanize_collection(missing_views)}"
|
|
76
118
|
)
|
|
77
|
-
# We just look-up all source system for now. This can be optimized to only
|
|
78
|
-
# look-up the ones that are actually used in the ingestion views. However, SourceSystem is typically in
|
|
79
|
-
# the order ~10 instances, so this is not a big deal for now. See task CDF-25974.
|
|
80
|
-
source_systems = self.client.migration.created_source_system.list(limit=-1)
|
|
81
|
-
self._source_system_mapping_by_id = {
|
|
82
|
-
source_system.source: source_system.as_direct_relation_reference() for source_system in source_systems
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# We look-up all existing asset mappings to be able to create direct relations to already mapped assets.
|
|
86
|
-
# This is needed in case the migration is run multiple times, or if assets are mapped
|
|
87
|
-
asset_mappings = self.client.migration.instance_source.list(resource_type="asset", limit=-1)
|
|
88
|
-
self._asset_mapping_by_id = {mapping.id_: mapping.as_direct_relation_reference() for mapping in asset_mappings}
|
|
89
119
|
|
|
90
|
-
def map(
|
|
120
|
+
def map(
|
|
121
|
+
self, source: Sequence[AssetCentricMapping[T_AssetCentricResourceExtended]]
|
|
122
|
+
) -> Sequence[tuple[InstanceApply | None, ConversionIssue]]:
|
|
91
123
|
"""Map a chunk of asset-centric data to InstanceApplyList format."""
|
|
92
|
-
|
|
124
|
+
# We update the direct relation cache in bulk for all resources in the chunk.
|
|
125
|
+
self._direct_relation_cache.update(item.resource for item in source)
|
|
126
|
+
output: list[tuple[InstanceApply | None, ConversionIssue]] = []
|
|
127
|
+
for item in source:
|
|
128
|
+
instance, conversion_issue = self._map_single_item(item)
|
|
129
|
+
output.append((instance, conversion_issue))
|
|
130
|
+
return output
|
|
131
|
+
|
|
132
|
+
def _map_single_item(
|
|
133
|
+
self, item: AssetCentricMapping[T_AssetCentricResourceExtended]
|
|
134
|
+
) -> tuple[NodeApply | EdgeApply | None, ConversionIssue]:
|
|
135
|
+
mapping = item.mapping
|
|
93
136
|
ingestion_view = mapping.get_ingestion_view()
|
|
94
137
|
try:
|
|
95
138
|
view_source = self._view_mapping_by_id[ingestion_view]
|
|
@@ -99,19 +142,379 @@ class AssetCentricMapper(
|
|
|
99
142
|
f"Failed to lookup mapping or view for ingestion view '{ingestion_view}'. Did you forget to call .prepare()?"
|
|
100
143
|
) from e
|
|
101
144
|
instance, conversion_issue = asset_centric_to_dm(
|
|
102
|
-
|
|
145
|
+
item.resource,
|
|
103
146
|
instance_id=mapping.instance_id,
|
|
104
147
|
view_source=view_source,
|
|
105
148
|
view_properties=view_properties,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
file_instance_id_by_id={}, # Todo implement file direct relations
|
|
149
|
+
direct_relation_cache=self._direct_relation_cache,
|
|
150
|
+
preferred_consumer_view=mapping.preferred_consumer_view,
|
|
109
151
|
)
|
|
110
152
|
if mapping.instance_id.space == MISSING_INSTANCE_SPACE:
|
|
111
153
|
conversion_issue.missing_instance_space = f"Missing instance space for dataset ID {mapping.data_set_id!r}"
|
|
112
|
-
|
|
113
|
-
if mapping.resource_type == "asset":
|
|
114
|
-
self._asset_mapping_by_id[mapping.id] = DirectRelationReference(
|
|
115
|
-
space=mapping.instance_id.space, external_id=mapping.instance_id.external_id
|
|
116
|
-
)
|
|
117
154
|
return instance, conversion_issue
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class ChartMapper(DataMapper[ChartSelector, Chart, ChartWrite]):
|
|
158
|
+
def __init__(self, client: ToolkitClient) -> None:
|
|
159
|
+
self.client = client
|
|
160
|
+
|
|
161
|
+
def map(self, source: Sequence[Chart]) -> Sequence[tuple[ChartWrite | None, MigrationIssue]]:
|
|
162
|
+
self._populate_cache(source)
|
|
163
|
+
output: list[tuple[ChartWrite | None, MigrationIssue]] = []
|
|
164
|
+
for item in source:
|
|
165
|
+
mapped_item, issue = self._map_single_item(item)
|
|
166
|
+
output.append((mapped_item, issue))
|
|
167
|
+
return output
|
|
168
|
+
|
|
169
|
+
def _populate_cache(self, source: Sequence[Chart]) -> None:
|
|
170
|
+
"""Populate the internal cache with timeseries from the source charts.
|
|
171
|
+
|
|
172
|
+
Note that the consumption views are also cached as part of the timeseries lookup.
|
|
173
|
+
"""
|
|
174
|
+
timeseries_ids: set[int] = set()
|
|
175
|
+
timeseries_external_ids: set[str] = set()
|
|
176
|
+
for chart in source:
|
|
177
|
+
for item in chart.data.time_series_collection or []:
|
|
178
|
+
if item.ts_id:
|
|
179
|
+
timeseries_ids.add(item.ts_id)
|
|
180
|
+
if item.ts_external_id:
|
|
181
|
+
timeseries_external_ids.add(item.ts_external_id)
|
|
182
|
+
if timeseries_ids:
|
|
183
|
+
self.client.migration.lookup.time_series(list(timeseries_ids))
|
|
184
|
+
if timeseries_external_ids:
|
|
185
|
+
self.client.migration.lookup.time_series(external_id=list(timeseries_external_ids))
|
|
186
|
+
|
|
187
|
+
def _map_single_item(self, item: Chart) -> tuple[ChartWrite | None, ChartMigrationIssue]:
|
|
188
|
+
issue = ChartMigrationIssue(chart_external_id=item.external_id)
|
|
189
|
+
time_series_collection = item.data.time_series_collection or []
|
|
190
|
+
timeseries_core_collection = self._create_timeseries_core_collection(time_series_collection, issue)
|
|
191
|
+
if issue.has_issues:
|
|
192
|
+
return None, issue
|
|
193
|
+
|
|
194
|
+
updated_source_collection = self._update_source_collection(
|
|
195
|
+
item.data.source_collection or [], time_series_collection, timeseries_core_collection
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
mapped_chart = item.as_write()
|
|
199
|
+
mapped_chart.data.core_timeseries_collection = timeseries_core_collection
|
|
200
|
+
mapped_chart.data.time_series_collection = None
|
|
201
|
+
mapped_chart.data.source_collection = updated_source_collection
|
|
202
|
+
return mapped_chart, issue
|
|
203
|
+
|
|
204
|
+
def _create_timeseries_core_collection(
|
|
205
|
+
self, time_series_collection: list[ChartTimeseries], issue: ChartMigrationIssue
|
|
206
|
+
) -> list[ChartCoreTimeseries]:
|
|
207
|
+
timeseries_core_collection: list[ChartCoreTimeseries] = []
|
|
208
|
+
for ts_item in time_series_collection or []:
|
|
209
|
+
node_id, consumer_view_id = self._get_node_id_consumer_view_id(ts_item)
|
|
210
|
+
|
|
211
|
+
if node_id is None:
|
|
212
|
+
if ts_item.ts_id is not None:
|
|
213
|
+
issue.missing_timeseries_ids.append(ts_item.ts_id)
|
|
214
|
+
elif ts_item.ts_external_id is not None:
|
|
215
|
+
issue.missing_timeseries_external_ids.append(ts_item.ts_external_id)
|
|
216
|
+
else:
|
|
217
|
+
issue.missing_timeseries_identifier.append(ts_item.id or "unknown")
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
core_timeseries = self._create_new_timeseries_core(ts_item, node_id, consumer_view_id)
|
|
221
|
+
timeseries_core_collection.append(core_timeseries)
|
|
222
|
+
return timeseries_core_collection
|
|
223
|
+
|
|
224
|
+
def _create_new_timeseries_core(
|
|
225
|
+
self, ts_item: ChartTimeseries, node_id: NodeId, consumer_view_id: ViewId | None
|
|
226
|
+
) -> ChartCoreTimeseries:
|
|
227
|
+
dumped = ts_item.model_dump(mode="json", by_alias=True, exclude_unset=True)
|
|
228
|
+
dumped["nodeReference"] = node_id
|
|
229
|
+
dumped["viewReference"] = consumer_view_id
|
|
230
|
+
new_uuid = str(uuid4())
|
|
231
|
+
dumped["id"] = new_uuid
|
|
232
|
+
dumped["type"] = "coreTimeseries"
|
|
233
|
+
# We ignore extra here to only include the fields that are shared between ChartTimeseries and ChartCoreTimeseries
|
|
234
|
+
core_timeseries = ChartCoreTimeseries.model_validate(dumped, extra="ignore")
|
|
235
|
+
return core_timeseries
|
|
236
|
+
|
|
237
|
+
def _get_node_id_consumer_view_id(self, ts_item: ChartTimeseries) -> tuple[NodeId | None, ViewId | None]:
|
|
238
|
+
"""Look up the node ID and consumer view ID for a given timeseries item.
|
|
239
|
+
|
|
240
|
+
Prioritizes lookup by internal ID, then by external ID.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
ts_item: The ChartTimeseries item to look up.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
A tuple containing the consumer view ID and node ID, or None if not found.
|
|
247
|
+
"""
|
|
248
|
+
node_id: NodeId | None = None
|
|
249
|
+
consumer_view_id: ViewId | None = None
|
|
250
|
+
for id_name, id_value in [("id", ts_item.ts_id), ("external_id", ts_item.ts_external_id)]:
|
|
251
|
+
if id_value is None:
|
|
252
|
+
continue
|
|
253
|
+
arg = {id_name: id_value}
|
|
254
|
+
node_id = self.client.migration.lookup.time_series(**arg) # type: ignore[arg-type]
|
|
255
|
+
consumer_view_id = self.client.migration.lookup.time_series.consumer_view(**arg) # type: ignore[arg-type]
|
|
256
|
+
if node_id is not None:
|
|
257
|
+
break
|
|
258
|
+
return node_id, consumer_view_id
|
|
259
|
+
|
|
260
|
+
def _update_source_collection(
|
|
261
|
+
self,
|
|
262
|
+
source_collection: list[ChartSource],
|
|
263
|
+
time_series_collection: list[ChartTimeseries],
|
|
264
|
+
timeseries_core_collection: list[ChartCoreTimeseries],
|
|
265
|
+
) -> list[ChartSource]:
|
|
266
|
+
remove_ids = {ts_item.id for ts_item in time_series_collection if ts_item.id is not None}
|
|
267
|
+
updated_source_collection = [ts_item for ts_item in source_collection if ts_item.id not in remove_ids]
|
|
268
|
+
for core_ts_item in timeseries_core_collection:
|
|
269
|
+
# We cast there two as we set them in the _create_timeseries_core_collection method
|
|
270
|
+
new_source_item = ChartSource(id=cast(str, core_ts_item.id), type=cast(str, core_ts_item.type))
|
|
271
|
+
updated_source_collection.append(new_source_item)
|
|
272
|
+
return updated_source_collection
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class CanvasMapper(DataMapper[CanvasSelector, IndustrialCanvas, IndustrialCanvasApply]):
|
|
276
|
+
# Note sequences are not supported in Canvas, so we do not include them here.
|
|
277
|
+
asset_centric_resource_types = tuple(("asset", "event", "timeseries", "file"))
|
|
278
|
+
DEFAULT_ASSET_VIEW = ViewId("cdf_cdm", "CogniteAsset", "v1")
|
|
279
|
+
DEFAULT_EVENT_VIEW = ViewId("cdf_cdm", "CogniteActivity", "v1")
|
|
280
|
+
DEFAULT_FILE_VIEW = ViewId("cdf_cdm", "CogniteFile", "v1")
|
|
281
|
+
DEFAULT_TIMESERIES_VIEW = ViewId("cdf_cdm", "CogniteTimeSeries", "v1")
|
|
282
|
+
|
|
283
|
+
def __init__(self, client: ToolkitClient, dry_run: bool, skip_on_missing_ref: bool = False) -> None:
|
|
284
|
+
self.client = client
|
|
285
|
+
self.dry_run = dry_run
|
|
286
|
+
self.skip_on_missing_ref = skip_on_missing_ref
|
|
287
|
+
|
|
288
|
+
def map(self, source: Sequence[IndustrialCanvas]) -> Sequence[tuple[IndustrialCanvasApply | None, MigrationIssue]]:
|
|
289
|
+
self._populate_cache(source)
|
|
290
|
+
output: list[tuple[IndustrialCanvasApply | None, MigrationIssue]] = []
|
|
291
|
+
for item in source:
|
|
292
|
+
mapped_item, issue = self._map_single_item(item)
|
|
293
|
+
output.append((mapped_item, issue))
|
|
294
|
+
return output
|
|
295
|
+
|
|
296
|
+
@property
|
|
297
|
+
def lookup_methods(self) -> dict[str, Callable]:
|
|
298
|
+
return {
|
|
299
|
+
"asset": self.client.migration.lookup.assets,
|
|
300
|
+
"event": self.client.migration.lookup.events,
|
|
301
|
+
"timeseries": self.client.migration.lookup.time_series,
|
|
302
|
+
"file": self.client.migration.lookup.files,
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
def _populate_cache(self, source: Sequence[IndustrialCanvas]) -> None:
|
|
306
|
+
"""Populate the internal cache with references from the source canvases.
|
|
307
|
+
|
|
308
|
+
Note that the consumption views are also cached as part of the timeseries lookup.
|
|
309
|
+
"""
|
|
310
|
+
ids_by_type: dict[str, set[int]] = defaultdict(set)
|
|
311
|
+
for canvas in source:
|
|
312
|
+
for ref in canvas.container_references or []:
|
|
313
|
+
if ref.container_reference_type in self.asset_centric_resource_types:
|
|
314
|
+
ids_by_type[ref.container_reference_type].add(ref.resource_id)
|
|
315
|
+
|
|
316
|
+
for resource_type, lookup_method in self.lookup_methods.items():
|
|
317
|
+
ids = ids_by_type.get(resource_type)
|
|
318
|
+
if ids:
|
|
319
|
+
lookup_method(list(ids))
|
|
320
|
+
|
|
321
|
+
def _get_node_id(self, resource_id: int, resource_type: str) -> NodeId | None:
|
|
322
|
+
"""Look up the node ID for a given resource ID and type."""
|
|
323
|
+
try:
|
|
324
|
+
return self.lookup_methods[resource_type](resource_id)
|
|
325
|
+
except KeyError:
|
|
326
|
+
raise ToolkitValueError(f"Unsupported resource type '{resource_type}' for container reference migration.")
|
|
327
|
+
|
|
328
|
+
def _get_consumer_view_id(self, resource_id: int, resource_type: str) -> ViewId:
|
|
329
|
+
"""Look up the consumer view ID for a given resource ID and type."""
|
|
330
|
+
lookup_map = {
|
|
331
|
+
"asset": (self.client.migration.lookup.assets.consumer_view, self.DEFAULT_ASSET_VIEW),
|
|
332
|
+
"event": (self.client.migration.lookup.events.consumer_view, self.DEFAULT_EVENT_VIEW),
|
|
333
|
+
"timeseries": (self.client.migration.lookup.time_series.consumer_view, self.DEFAULT_TIMESERIES_VIEW),
|
|
334
|
+
"file": (self.client.migration.lookup.files.consumer_view, self.DEFAULT_FILE_VIEW),
|
|
335
|
+
}
|
|
336
|
+
if lookup_tuple := lookup_map.get(resource_type):
|
|
337
|
+
method, fallback = lookup_tuple
|
|
338
|
+
return method(resource_id) or fallback
|
|
339
|
+
|
|
340
|
+
raise ToolkitValueError(f"Unsupported resource type '{resource_type}' for container reference migration.")
|
|
341
|
+
|
|
342
|
+
def _map_single_item(self, canvas: IndustrialCanvas) -> tuple[IndustrialCanvasApply | None, CanvasMigrationIssue]:
|
|
343
|
+
update = canvas.as_write()
|
|
344
|
+
issue = CanvasMigrationIssue(canvas_external_id=canvas.canvas.external_id, canvas_name=canvas.canvas.name)
|
|
345
|
+
|
|
346
|
+
remaining_container_references: list[ContainerReferenceApply] = []
|
|
347
|
+
new_fdm_references: list[FdmInstanceContainerReferenceApply] = []
|
|
348
|
+
for ref in update.container_references or []:
|
|
349
|
+
if ref.container_reference_type not in self.asset_centric_resource_types:
|
|
350
|
+
remaining_container_references.append(ref)
|
|
351
|
+
continue
|
|
352
|
+
node_id = self._get_node_id(ref.resource_id, ref.container_reference_type)
|
|
353
|
+
if node_id is None:
|
|
354
|
+
issue.missing_reference_ids.append(ref.as_asset_centric_id())
|
|
355
|
+
else:
|
|
356
|
+
consumer_view = self._get_consumer_view_id(ref.resource_id, ref.container_reference_type)
|
|
357
|
+
fdm_ref = self.migrate_container_reference(ref, canvas.canvas.external_id, node_id, consumer_view)
|
|
358
|
+
new_fdm_references.append(fdm_ref)
|
|
359
|
+
if issue.missing_reference_ids and self.skip_on_missing_ref:
|
|
360
|
+
return None, issue
|
|
361
|
+
|
|
362
|
+
update.container_references = remaining_container_references
|
|
363
|
+
update.fdm_instance_container_references.extend(new_fdm_references)
|
|
364
|
+
if not self.dry_run:
|
|
365
|
+
backup = canvas.as_write().create_backup()
|
|
366
|
+
try:
|
|
367
|
+
self.client.canvas.industrial.create(backup)
|
|
368
|
+
except CogniteException as e:
|
|
369
|
+
raise ToolkitMigrationError(
|
|
370
|
+
f"Failed to create backup for canvas '{canvas.canvas.name}': {e!s}. "
|
|
371
|
+
) from e
|
|
372
|
+
|
|
373
|
+
return update, issue
|
|
374
|
+
|
|
375
|
+
@classmethod
|
|
376
|
+
def migrate_container_reference(
|
|
377
|
+
cls, reference: ContainerReferenceApply, canvas_external_id: str, instance_id: NodeId, consumer_view: ViewId
|
|
378
|
+
) -> FdmInstanceContainerReferenceApply:
|
|
379
|
+
"""Migrate a single container reference by replacing the asset-centric ID with the data model instance ID."""
|
|
380
|
+
new_id = str(uuid4())
|
|
381
|
+
new_external_id = f"{canvas_external_id}_{new_id}"
|
|
382
|
+
return FdmInstanceContainerReferenceApply(
|
|
383
|
+
external_id=new_external_id,
|
|
384
|
+
id_=new_id,
|
|
385
|
+
container_reference_type="fdmInstance",
|
|
386
|
+
instance_space=instance_id.space,
|
|
387
|
+
instance_external_id=instance_id.external_id,
|
|
388
|
+
view_space=consumer_view.space,
|
|
389
|
+
view_external_id=consumer_view.external_id,
|
|
390
|
+
view_version=consumer_view.version,
|
|
391
|
+
label=reference.label,
|
|
392
|
+
properties_=reference.properties_,
|
|
393
|
+
x=reference.x,
|
|
394
|
+
y=reference.y,
|
|
395
|
+
width=reference.width,
|
|
396
|
+
height=reference.height,
|
|
397
|
+
max_width=reference.max_width,
|
|
398
|
+
max_height=reference.max_height,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
class ThreeDMapper(DataMapper[ThreeDSelector, ThreeDModelResponse, ThreeDMigrationRequest]):
|
|
403
|
+
def __init__(self, client: ToolkitClient) -> None:
|
|
404
|
+
self.client = client
|
|
405
|
+
|
|
406
|
+
def map(
|
|
407
|
+
self, source: Sequence[ThreeDModelResponse]
|
|
408
|
+
) -> Sequence[tuple[ThreeDMigrationRequest | None, MigrationIssue]]:
|
|
409
|
+
self._populate_cache(source)
|
|
410
|
+
output: list[tuple[ThreeDMigrationRequest | None, MigrationIssue]] = []
|
|
411
|
+
for item in source:
|
|
412
|
+
mapped_item, issue = self._map_single_item(item)
|
|
413
|
+
output.append((mapped_item, issue))
|
|
414
|
+
return output
|
|
415
|
+
|
|
416
|
+
def _populate_cache(self, source: Sequence[ThreeDModelResponse]) -> None:
|
|
417
|
+
dataset_ids: set[int] = set()
|
|
418
|
+
for model in source:
|
|
419
|
+
if model.data_set_id is not None:
|
|
420
|
+
dataset_ids.add(model.data_set_id)
|
|
421
|
+
self.client.migration.space_source.retrieve(list(dataset_ids))
|
|
422
|
+
|
|
423
|
+
def _map_single_item(
|
|
424
|
+
self, item: ThreeDModelResponse
|
|
425
|
+
) -> tuple[ThreeDMigrationRequest | None, ThreeDModelMigrationIssue]:
|
|
426
|
+
issue = ThreeDModelMigrationIssue(model_name=item.name, model_id=item.id)
|
|
427
|
+
instance_space: str | None = None
|
|
428
|
+
last_revision_id: int | None = None
|
|
429
|
+
model_type: Literal["CAD", "PointCloud", "Image360"] | None = None
|
|
430
|
+
if item.data_set_id is None:
|
|
431
|
+
issue.error_message.append("3D model is not associated with any dataset.")
|
|
432
|
+
else:
|
|
433
|
+
space_source = self.client.migration.space_source.retrieve(item.data_set_id)
|
|
434
|
+
if space_source is not None:
|
|
435
|
+
instance_space = space_source.instance_space
|
|
436
|
+
if instance_space is None and item.data_set_id is not None:
|
|
437
|
+
issue.error_message.append(f"Missing instance space for dataset ID {item.data_set_id!r}")
|
|
438
|
+
if item.last_revision_info is None:
|
|
439
|
+
issue.error_message.append("3D model has no revisions.")
|
|
440
|
+
else:
|
|
441
|
+
model_type = self._get_type(item.last_revision_info)
|
|
442
|
+
last_revision_id = item.last_revision_info.revision_id
|
|
443
|
+
if last_revision_id is None:
|
|
444
|
+
issue.error_message.append("3D model's last revision has no revision ID.")
|
|
445
|
+
|
|
446
|
+
if model_type is None:
|
|
447
|
+
issue.error_message.append("3D model's last revision has no recognized type.")
|
|
448
|
+
|
|
449
|
+
if instance_space is None or last_revision_id is None or model_type is None or issue.has_issues:
|
|
450
|
+
return None, issue
|
|
451
|
+
|
|
452
|
+
mapped_request = ThreeDMigrationRequest(
|
|
453
|
+
modelId=item.id,
|
|
454
|
+
type=model_type,
|
|
455
|
+
space=instance_space,
|
|
456
|
+
revision=ThreeDRevisionMigrationRequest(
|
|
457
|
+
space=instance_space,
|
|
458
|
+
type=model_type,
|
|
459
|
+
revisionId=last_revision_id,
|
|
460
|
+
model=Model(
|
|
461
|
+
instanceId=InstanceIdentifier(
|
|
462
|
+
space=instance_space,
|
|
463
|
+
external_id=f"cog_3d_model_{item.id!s}",
|
|
464
|
+
)
|
|
465
|
+
),
|
|
466
|
+
),
|
|
467
|
+
)
|
|
468
|
+
return mapped_request, issue
|
|
469
|
+
|
|
470
|
+
@staticmethod
|
|
471
|
+
def _get_type(revision: RevisionStatus) -> Literal["CAD", "PointCloud", "Image360"] | None:
|
|
472
|
+
types = revision.types or []
|
|
473
|
+
if any("gltf-directory" in t for t in types):
|
|
474
|
+
return "CAD"
|
|
475
|
+
elif any("ept-pointcloud" in t for t in types):
|
|
476
|
+
return "PointCloud"
|
|
477
|
+
else:
|
|
478
|
+
return None
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class ThreeDAssetMapper(DataMapper[ThreeDSelector, AssetMappingResponse, AssetMappingDMRequest]):
|
|
482
|
+
def __init__(self, client: ToolkitClient) -> None:
|
|
483
|
+
self.client = client
|
|
484
|
+
|
|
485
|
+
def map(
|
|
486
|
+
self, source: Sequence[AssetMappingResponse]
|
|
487
|
+
) -> Sequence[tuple[AssetMappingDMRequest | None, MigrationIssue]]:
|
|
488
|
+
output: list[tuple[AssetMappingDMRequest | None, MigrationIssue]] = []
|
|
489
|
+
self._populate_cache(source)
|
|
490
|
+
for item in source:
|
|
491
|
+
mapped_item, issue = self._map_single_item(item)
|
|
492
|
+
output.append((mapped_item, issue))
|
|
493
|
+
return output
|
|
494
|
+
|
|
495
|
+
def _populate_cache(self, source: Sequence[AssetMappingResponse]) -> None:
|
|
496
|
+
asset_ids: set[int] = set()
|
|
497
|
+
for mapping in source:
|
|
498
|
+
if mapping.asset_id is not None:
|
|
499
|
+
asset_ids.add(mapping.asset_id)
|
|
500
|
+
self.client.migration.lookup.assets(list(asset_ids))
|
|
501
|
+
|
|
502
|
+
def _map_single_item(
|
|
503
|
+
self, item: AssetMappingResponse
|
|
504
|
+
) -> tuple[AssetMappingDMRequest | None, ThreeDModelMigrationIssue]:
|
|
505
|
+
issue = ThreeDModelMigrationIssue(model_name=f"AssetMapping_{item.model_id}", model_id=item.model_id)
|
|
506
|
+
asset_instance_id = item.asset_instance_id
|
|
507
|
+
if item.asset_id and asset_instance_id is None:
|
|
508
|
+
asset_node_id = self.client.migration.lookup.assets(item.asset_id)
|
|
509
|
+
if asset_node_id is None:
|
|
510
|
+
issue.error_message.append(f"Missing asset instance for asset ID {item.asset_id!r}")
|
|
511
|
+
return None, issue
|
|
512
|
+
asset_instance_id = NodeReference(space=asset_node_id.space, externalId=asset_node_id.external_id)
|
|
513
|
+
|
|
514
|
+
if asset_instance_id is None:
|
|
515
|
+
issue.error_message.append("Neither assetInstanceId nor assetId provided for mapping.")
|
|
516
|
+
return None, issue
|
|
517
|
+
mapped_request = AssetMappingDMRequest(
|
|
518
|
+
modelId=item.model_id, revisionId=item.revision_id, nodeId=item.node_id, assetInstanceId=asset_instance_id
|
|
519
|
+
)
|
|
520
|
+
return mapped_request, issue
|
|
@@ -4,6 +4,7 @@ from cognite.client.data_classes.data_modeling.containers import BTreeIndex
|
|
|
4
4
|
SPACE = dm.SpaceApply(
|
|
5
5
|
"cognite_migration", description="Space for the asset-centric to data modeling migration", name="cdf_migration"
|
|
6
6
|
)
|
|
7
|
+
COGNITE_MIGRATION_SPACE_ID = SPACE.space
|
|
7
8
|
|
|
8
9
|
RESOURCE_VIEW_MAPPING = dm.ContainerApply(
|
|
9
10
|
space=SPACE.space,
|
|
@@ -2,7 +2,7 @@ from functools import lru_cache
|
|
|
2
2
|
|
|
3
3
|
from cognite.client.data_classes.data_modeling import ViewId
|
|
4
4
|
|
|
5
|
-
from cognite_toolkit._cdf_tk.client.data_classes.migration import ResourceViewMappingApply
|
|
5
|
+
from cognite_toolkit._cdf_tk.client.data_classes.legacy.migration import ResourceViewMappingApply
|
|
6
6
|
|
|
7
7
|
ASSET_ID = "cdf_asset_mapping"
|
|
8
8
|
EVENT_ID = "cdf_event_mapping"
|
|
@@ -93,7 +93,7 @@ def create_default_mappings() -> list[ResourceViewMappingApply]:
|
|
|
93
93
|
view_id=ViewId("cdf_cdm", "CogniteDiagramAnnotation", "v1"),
|
|
94
94
|
property_mapping={
|
|
95
95
|
# We are ignoring the symbol region in the default mapping.
|
|
96
|
-
"
|
|
96
|
+
"annotatedResourceId": "edge.startNode",
|
|
97
97
|
"annotationType": "edge.type.externalId",
|
|
98
98
|
"creatingUser": "sourceCreatedUser",
|
|
99
99
|
"creatingApp": "sourceId",
|
|
@@ -114,16 +114,16 @@ def create_default_mappings() -> list[ResourceViewMappingApply]:
|
|
|
114
114
|
ResourceViewMappingApply(
|
|
115
115
|
external_id=FILE_ANNOTATIONS_ID,
|
|
116
116
|
resource_type="fileAnnotation",
|
|
117
|
-
view_id=ViewId("cdf_cdm", "
|
|
117
|
+
view_id=ViewId("cdf_cdm", "CogniteDiagramAnnotation", "v1"),
|
|
118
118
|
property_mapping={
|
|
119
|
-
"
|
|
119
|
+
"annotatedResourceId": "edge.startNode",
|
|
120
120
|
"annotationType": "edge.type.externalId",
|
|
121
121
|
"creatingUser": "sourceCreatedUser",
|
|
122
122
|
"creatingApp": "sourceId",
|
|
123
123
|
"creatingAppVersion": "sourceContext",
|
|
124
124
|
"status": "status",
|
|
125
|
-
"data.fileRef.id": "edge.
|
|
126
|
-
"data.fileRef.externalId": "edge.
|
|
125
|
+
"data.fileRef.id": "edge.endNode",
|
|
126
|
+
"data.fileRef.externalId": "edge.endNode",
|
|
127
127
|
"data.description": "description",
|
|
128
128
|
"data.pageNumber": "startNodePageNumber",
|
|
129
129
|
"data.textRegion.confidence": "confidence",
|
|
@@ -6,7 +6,7 @@ from cognite.client.utils._identifier import InstanceId
|
|
|
6
6
|
from cognite.client.utils._text import to_camel_case
|
|
7
7
|
from pydantic import BaseModel, Field, field_serializer
|
|
8
8
|
|
|
9
|
-
from cognite_toolkit._cdf_tk.client.data_classes.migration import AssetCentricId
|
|
9
|
+
from cognite_toolkit._cdf_tk.client.data_classes.legacy.migration import AssetCentricId
|
|
10
10
|
from cognite_toolkit._cdf_tk.utils.useful_types import JsonVal
|
|
11
11
|
|
|
12
12
|
|
|
@@ -30,6 +30,57 @@ class MigrationIssue(MigrationObject):
|
|
|
30
30
|
return True
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
class ThreeDModelMigrationIssue(MigrationIssue):
|
|
34
|
+
"""Represents a 3D model migration issue encountered during migration.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
model_external_id (str): The external ID of the 3D model that could not be migrated.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
type: ClassVar[str] = "threeDModelMigration"
|
|
41
|
+
model_name: str
|
|
42
|
+
model_id: int
|
|
43
|
+
error_message: list[str] = Field(default_factory=list)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def has_issues(self) -> bool:
|
|
47
|
+
"""Check if there are any issues recorded in this ThreeDModelMigrationIssue."""
|
|
48
|
+
return bool(self.error_message)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ChartMigrationIssue(MigrationIssue):
|
|
52
|
+
"""Represents a chart migration issue encountered during migration.
|
|
53
|
+
|
|
54
|
+
Attributes:
|
|
55
|
+
chart_external_id (str): The external ID of the chart that could not be migrated.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
type: ClassVar[str] = "chartMigration"
|
|
59
|
+
chart_external_id: str
|
|
60
|
+
missing_timeseries_ids: list[int] = Field(default_factory=list)
|
|
61
|
+
missing_timeseries_external_ids: list[str] = Field(default_factory=list)
|
|
62
|
+
missing_timeseries_identifier: list[str] = Field(default_factory=list)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def has_issues(self) -> bool:
|
|
66
|
+
"""Check if there are any issues recorded in this ChartMigrationIssue."""
|
|
67
|
+
return bool(
|
|
68
|
+
self.missing_timeseries_ids or self.missing_timeseries_external_ids or self.missing_timeseries_identifier
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class CanvasMigrationIssue(MigrationIssue):
|
|
73
|
+
type: ClassVar[str] = "canvasMigration"
|
|
74
|
+
canvas_external_id: str
|
|
75
|
+
canvas_name: str
|
|
76
|
+
missing_reference_ids: list[AssetCentricId] = Field(default_factory=list)
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def has_issues(self) -> bool:
|
|
80
|
+
"""Check if there are any issues recorded in this CanvasMigrationIssue."""
|
|
81
|
+
return bool(self.missing_reference_ids)
|
|
82
|
+
|
|
83
|
+
|
|
33
84
|
class ReadIssue(MigrationIssue):
|
|
34
85
|
"""Represents a read issue encountered during migration."""
|
|
35
86
|
|