cognite-neat 0.104.0__py3-none-any.whl → 0.105.1__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.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_client/_api/data_modeling_loaders.py +83 -23
- cognite/neat/_client/_api/schema.py +2 -1
- cognite/neat/_client/data_classes/neat_sequence.py +261 -0
- cognite/neat/_client/data_classes/schema.py +5 -1
- cognite/neat/_client/testing.py +33 -0
- cognite/neat/_constants.py +57 -0
- cognite/neat/_graph/extractors/_classic_cdf/_base.py +6 -5
- cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +225 -11
- cognite/neat/_graph/extractors/_mock_graph_generator.py +1 -1
- cognite/neat/_graph/loaders/_rdf2dms.py +31 -5
- cognite/neat/_graph/transformers/__init__.py +3 -1
- cognite/neat/_graph/transformers/_classic_cdf.py +39 -51
- cognite/neat/_graph/transformers/_rdfpath.py +14 -15
- cognite/neat/_graph/transformers/_value_type.py +72 -0
- cognite/neat/_issues/__init__.py +0 -2
- cognite/neat/_issues/_base.py +19 -35
- cognite/neat/_issues/warnings/__init__.py +6 -1
- cognite/neat/_issues/warnings/_general.py +7 -0
- cognite/neat/_issues/warnings/_properties.py +11 -0
- cognite/neat/_issues/warnings/_resources.py +11 -0
- cognite/neat/_rules/exporters/_rules2dms.py +35 -1
- cognite/neat/_rules/exporters/_rules2excel.py +2 -2
- cognite/neat/_rules/importers/_dms2rules.py +66 -55
- cognite/neat/_rules/models/_base_rules.py +4 -1
- cognite/neat/_rules/models/entities/_wrapped.py +10 -5
- cognite/neat/_rules/models/mapping/_classic2core.yaml +239 -38
- cognite/neat/_rules/transformers/__init__.py +8 -2
- cognite/neat/_rules/transformers/_converters.py +271 -188
- cognite/neat/_rules/transformers/_mapping.py +75 -59
- cognite/neat/_rules/transformers/_verification.py +2 -3
- cognite/neat/_session/_inspect.py +3 -1
- cognite/neat/_session/_prepare.py +112 -24
- cognite/neat/_session/_read.py +33 -70
- cognite/neat/_session/_state.py +2 -2
- cognite/neat/_session/_to.py +2 -2
- cognite/neat/_store/_rules_store.py +4 -8
- cognite/neat/_utils/reader/_base.py +27 -0
- cognite/neat/_version.py +1 -1
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/METADATA +4 -3
- cognite_neat-0.105.1.dist-info/RECORD +179 -0
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/WHEEL +1 -1
- cognite/neat/_app/api/__init__.py +0 -0
- cognite/neat/_app/api/asgi/metrics.py +0 -4
- cognite/neat/_app/api/configuration.py +0 -98
- cognite/neat/_app/api/context_manager/__init__.py +0 -3
- cognite/neat/_app/api/context_manager/manager.py +0 -16
- cognite/neat/_app/api/data_classes/__init__.py +0 -0
- cognite/neat/_app/api/data_classes/rest.py +0 -59
- cognite/neat/_app/api/explorer.py +0 -66
- cognite/neat/_app/api/routers/configuration.py +0 -25
- cognite/neat/_app/api/routers/crud.py +0 -102
- cognite/neat/_app/api/routers/metrics.py +0 -10
- cognite/neat/_app/api/routers/workflows.py +0 -224
- cognite/neat/_app/api/utils/__init__.py +0 -0
- cognite/neat/_app/api/utils/data_mapping.py +0 -17
- cognite/neat/_app/api/utils/logging.py +0 -26
- cognite/neat/_app/api/utils/query_templates.py +0 -92
- cognite/neat/_app/main.py +0 -17
- cognite/neat/_app/monitoring/__init__.py +0 -0
- cognite/neat/_app/monitoring/metrics.py +0 -69
- cognite/neat/_app/ui/index.html +0 -1
- cognite/neat/_app/ui/neat-app/.gitignore +0 -23
- cognite/neat/_app/ui/neat-app/README.md +0 -70
- cognite/neat/_app/ui/neat-app/build/asset-manifest.json +0 -14
- cognite/neat/_app/ui/neat-app/build/favicon.ico +0 -0
- cognite/neat/_app/ui/neat-app/build/img/architect-icon.svg +0 -116
- cognite/neat/_app/ui/neat-app/build/img/developer-icon.svg +0 -112
- cognite/neat/_app/ui/neat-app/build/img/sme-icon.svg +0 -34
- cognite/neat/_app/ui/neat-app/build/index.html +0 -1
- cognite/neat/_app/ui/neat-app/build/logo192.png +0 -0
- cognite/neat/_app/ui/neat-app/build/manifest.json +0 -25
- cognite/neat/_app/ui/neat-app/build/robots.txt +0 -3
- cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css +0 -2
- cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css.map +0 -1
- cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js +0 -3
- cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.LICENSE.txt +0 -88
- cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.map +0 -1
- cognite/neat/_app/ui/neat-app/build/static/media/logo.8093b84df9ed36a174c629d6fe0b730d.svg +0 -1
- cognite/neat/_app/ui/neat-app/package-lock.json +0 -18306
- cognite/neat/_app/ui/neat-app/package.json +0 -62
- cognite/neat/_app/ui/neat-app/public/favicon.ico +0 -0
- cognite/neat/_app/ui/neat-app/public/img/architect-icon.svg +0 -116
- cognite/neat/_app/ui/neat-app/public/img/developer-icon.svg +0 -112
- cognite/neat/_app/ui/neat-app/public/img/sme-icon.svg +0 -34
- cognite/neat/_app/ui/neat-app/public/index.html +0 -43
- cognite/neat/_app/ui/neat-app/public/logo192.png +0 -0
- cognite/neat/_app/ui/neat-app/public/manifest.json +0 -25
- cognite/neat/_app/ui/neat-app/public/robots.txt +0 -3
- cognite/neat/_app/ui/neat-app/src/App.css +0 -38
- cognite/neat/_app/ui/neat-app/src/App.js +0 -17
- cognite/neat/_app/ui/neat-app/src/App.test.js +0 -8
- cognite/neat/_app/ui/neat-app/src/MainContainer.tsx +0 -70
- cognite/neat/_app/ui/neat-app/src/components/JsonViewer.tsx +0 -43
- cognite/neat/_app/ui/neat-app/src/components/LocalUploader.tsx +0 -124
- cognite/neat/_app/ui/neat-app/src/components/OverviewComponentEditorDialog.tsx +0 -63
- cognite/neat/_app/ui/neat-app/src/components/StepEditorDialog.tsx +0 -511
- cognite/neat/_app/ui/neat-app/src/components/TabPanel.tsx +0 -36
- cognite/neat/_app/ui/neat-app/src/components/Utils.tsx +0 -56
- cognite/neat/_app/ui/neat-app/src/components/WorkflowDeleteDialog.tsx +0 -60
- cognite/neat/_app/ui/neat-app/src/components/WorkflowExecutionReport.tsx +0 -112
- cognite/neat/_app/ui/neat-app/src/components/WorkflowImportExportDialog.tsx +0 -67
- cognite/neat/_app/ui/neat-app/src/components/WorkflowMetadataDialog.tsx +0 -79
- cognite/neat/_app/ui/neat-app/src/index.css +0 -13
- cognite/neat/_app/ui/neat-app/src/index.js +0 -13
- cognite/neat/_app/ui/neat-app/src/logo.svg +0 -1
- cognite/neat/_app/ui/neat-app/src/reportWebVitals.js +0 -13
- cognite/neat/_app/ui/neat-app/src/setupTests.js +0 -5
- cognite/neat/_app/ui/neat-app/src/types/WorkflowTypes.ts +0 -388
- cognite/neat/_app/ui/neat-app/src/views/AboutView.tsx +0 -61
- cognite/neat/_app/ui/neat-app/src/views/ConfigView.tsx +0 -184
- cognite/neat/_app/ui/neat-app/src/views/GlobalConfigView.tsx +0 -180
- cognite/neat/_app/ui/neat-app/src/views/WorkflowView.tsx +0 -570
- cognite/neat/_app/ui/neat-app/tsconfig.json +0 -27
- cognite/neat/_workflows/__init__.py +0 -17
- cognite/neat/_workflows/base.py +0 -590
- cognite/neat/_workflows/cdf_store.py +0 -393
- cognite/neat/_workflows/examples/Export_DMS/workflow.yaml +0 -89
- cognite/neat/_workflows/examples/Export_Semantic_Data_Model/workflow.yaml +0 -66
- cognite/neat/_workflows/examples/Import_DMS/workflow.yaml +0 -65
- cognite/neat/_workflows/examples/Validate_Rules/workflow.yaml +0 -67
- cognite/neat/_workflows/examples/Validate_Solution_Model/workflow.yaml +0 -64
- cognite/neat/_workflows/manager.py +0 -292
- cognite/neat/_workflows/model.py +0 -203
- cognite/neat/_workflows/steps/__init__.py +0 -0
- cognite/neat/_workflows/steps/data_contracts.py +0 -109
- cognite/neat/_workflows/steps/lib/__init__.py +0 -0
- cognite/neat/_workflows/steps/lib/current/__init__.py +0 -6
- cognite/neat/_workflows/steps/lib/current/graph_extractor.py +0 -100
- cognite/neat/_workflows/steps/lib/current/graph_loader.py +0 -51
- cognite/neat/_workflows/steps/lib/current/graph_store.py +0 -48
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +0 -537
- cognite/neat/_workflows/steps/lib/current/rules_importer.py +0 -323
- cognite/neat/_workflows/steps/lib/current/rules_validator.py +0 -106
- cognite/neat/_workflows/steps/lib/io/__init__.py +0 -1
- cognite/neat/_workflows/steps/lib/io/io_steps.py +0 -393
- cognite/neat/_workflows/steps/step_model.py +0 -79
- cognite/neat/_workflows/steps_registry.py +0 -218
- cognite/neat/_workflows/tasks.py +0 -18
- cognite/neat/_workflows/triggers.py +0 -169
- cognite/neat/_workflows/utils.py +0 -19
- cognite_neat-0.104.0.dist-info/RECORD +0 -276
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/LICENSE +0 -0
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
configs: []
|
|
2
|
-
description: null
|
|
3
|
-
implementation_module: null
|
|
4
|
-
name: Validate Solution Model
|
|
5
|
-
steps:
|
|
6
|
-
- complex_configs: {}
|
|
7
|
-
configs: {}
|
|
8
|
-
description: null
|
|
9
|
-
enabled: true
|
|
10
|
-
id: step_769298
|
|
11
|
-
label: Upload File
|
|
12
|
-
max_retries: 0
|
|
13
|
-
method: null
|
|
14
|
-
params:
|
|
15
|
-
file_type: rules
|
|
16
|
-
retry_delay: 3
|
|
17
|
-
stype: file_uploader
|
|
18
|
-
system_component_id: null
|
|
19
|
-
transition_to:
|
|
20
|
-
- step_399494
|
|
21
|
-
trigger: true
|
|
22
|
-
ui_config:
|
|
23
|
-
pos_x: 627
|
|
24
|
-
pos_y: -19
|
|
25
|
-
- complex_configs: {}
|
|
26
|
-
configs:
|
|
27
|
-
File name: ''
|
|
28
|
-
Report formatter: BasicHTML
|
|
29
|
-
Role: infer
|
|
30
|
-
description: null
|
|
31
|
-
enabled: true
|
|
32
|
-
id: step_399494
|
|
33
|
-
label: Import Excel
|
|
34
|
-
max_retries: 0
|
|
35
|
-
method: ExcelToRules
|
|
36
|
-
params: {}
|
|
37
|
-
retry_delay: 3
|
|
38
|
-
stype: stdstep
|
|
39
|
-
system_component_id: null
|
|
40
|
-
transition_to:
|
|
41
|
-
- step_273233
|
|
42
|
-
trigger: false
|
|
43
|
-
ui_config:
|
|
44
|
-
pos_x: 627
|
|
45
|
-
pos_y: 66
|
|
46
|
-
- complex_configs: {}
|
|
47
|
-
configs:
|
|
48
|
-
Report Formatter: BasicHTML
|
|
49
|
-
description: null
|
|
50
|
-
enabled: true
|
|
51
|
-
id: step_273233
|
|
52
|
-
label: Validate Against CDF
|
|
53
|
-
max_retries: 0
|
|
54
|
-
method: ValidateRulesAgainstCDF
|
|
55
|
-
params: {}
|
|
56
|
-
retry_delay: 3
|
|
57
|
-
stype: stdstep
|
|
58
|
-
system_component_id: null
|
|
59
|
-
transition_to: []
|
|
60
|
-
trigger: false
|
|
61
|
-
ui_config:
|
|
62
|
-
pos_x: 627
|
|
63
|
-
pos_y: 154
|
|
64
|
-
system_components: []
|
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import os
|
|
3
|
-
import shutil
|
|
4
|
-
import sys
|
|
5
|
-
import time
|
|
6
|
-
import traceback
|
|
7
|
-
|
|
8
|
-
from cognite.client import CogniteClient
|
|
9
|
-
from prometheus_client import Gauge
|
|
10
|
-
from pydantic import BaseModel
|
|
11
|
-
|
|
12
|
-
from cognite.neat._config import Config
|
|
13
|
-
from cognite.neat._workflows import BaseWorkflow
|
|
14
|
-
from cognite.neat._workflows.base import WorkflowDefinition
|
|
15
|
-
from cognite.neat._workflows.model import FlowMessage, InstanceStartMethod, WorkflowState, WorkflowStepDefinition
|
|
16
|
-
from cognite.neat._workflows.steps_registry import StepsRegistry
|
|
17
|
-
from cognite.neat._workflows.tasks import WorkflowTaskBuilder
|
|
18
|
-
|
|
19
|
-
live_workflow_instances = Gauge("neat_workflow_live_instances", "Count of live workflow instances", ["itype"])
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class WorkflowStartStatus(BaseModel, arbitrary_types_allowed=True):
|
|
23
|
-
workflow_instance: BaseWorkflow | None = None
|
|
24
|
-
is_success: bool = True
|
|
25
|
-
status_text: str | None = None
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class WorkflowManager:
|
|
29
|
-
"""Workflow manager is responsible for loading, saving and managing workflows
|
|
30
|
-
client: CogniteClient
|
|
31
|
-
config: Config
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
def __init__(self, client: CogniteClient, config: Config):
|
|
35
|
-
self.client = client
|
|
36
|
-
self.data_set_id = config.cdf_default_dataset_id
|
|
37
|
-
self.data_store_path = config.data_store_path
|
|
38
|
-
self.workflow_registry: dict[str, BaseWorkflow] = {}
|
|
39
|
-
self.ephemeral_instance_registry: dict[str, BaseWorkflow] = {}
|
|
40
|
-
self.workflows_storage_type = config.workflows_store_type
|
|
41
|
-
self.config = config
|
|
42
|
-
self.workflows_storage_path = config.workflows_store_path
|
|
43
|
-
self.rules_storage_path = config.rules_store_path
|
|
44
|
-
self.task_builder = WorkflowTaskBuilder(client, self)
|
|
45
|
-
self.steps_registry = StepsRegistry(self.config)
|
|
46
|
-
self.steps_registry.load_step_classes()
|
|
47
|
-
|
|
48
|
-
def update_cdf_client(self, client: CogniteClient):
|
|
49
|
-
self.client = client
|
|
50
|
-
self.task_builder = WorkflowTaskBuilder(client, self)
|
|
51
|
-
self.workflow_registry = {}
|
|
52
|
-
self.load_workflows_from_storage()
|
|
53
|
-
|
|
54
|
-
def get_list_of_workflows(self):
|
|
55
|
-
return list(self.workflow_registry.keys())
|
|
56
|
-
|
|
57
|
-
def get_steps_registry(self):
|
|
58
|
-
return self.steps_registry
|
|
59
|
-
|
|
60
|
-
def get_workflow(self, name: str) -> BaseWorkflow | None:
|
|
61
|
-
try:
|
|
62
|
-
return self.workflow_registry[name]
|
|
63
|
-
except KeyError:
|
|
64
|
-
return None
|
|
65
|
-
|
|
66
|
-
def full_reset(self):
|
|
67
|
-
self.workflow_registry = {}
|
|
68
|
-
self.ephemeral_instance_registry = {}
|
|
69
|
-
|
|
70
|
-
def start_workflow(self, name: str, sync=False, **kwargs):
|
|
71
|
-
workflow = self.get_workflow(name)
|
|
72
|
-
if workflow is None:
|
|
73
|
-
raise ValueError(f"Workflow {name} not found")
|
|
74
|
-
workflow.start(sync, kwargs=kwargs)
|
|
75
|
-
return workflow
|
|
76
|
-
|
|
77
|
-
def delete_workflow(self, name: str):
|
|
78
|
-
workflow = self.get_workflow(name)
|
|
79
|
-
if workflow is not None:
|
|
80
|
-
workflow.cleanup_workflow_context()
|
|
81
|
-
del self.workflow_registry[name]
|
|
82
|
-
full_path = self.workflows_storage_path / name
|
|
83
|
-
shutil.rmtree(full_path)
|
|
84
|
-
|
|
85
|
-
def update_workflow(self, name: str, workflow: WorkflowDefinition):
|
|
86
|
-
self.workflow_registry[name].workflow_steps = workflow.steps
|
|
87
|
-
self.workflow_registry[name].configs = workflow.configs
|
|
88
|
-
self.workflow_registry[name].workflow_system_components = workflow.system_components
|
|
89
|
-
|
|
90
|
-
def save_workflow_to_storage(self, name: str, custom_implementation_module: str | None = None):
|
|
91
|
-
"""Save workflow from memory to storage"""
|
|
92
|
-
if self.workflows_storage_type == "file":
|
|
93
|
-
full_path = self.workflows_storage_path / name / "workflow.yaml"
|
|
94
|
-
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
95
|
-
wf = self.workflow_registry[name]
|
|
96
|
-
full_path.write_text(
|
|
97
|
-
wf.serialize_workflow(output_format="yaml", custom_implementation_module=custom_implementation_module)
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
def create_new_workflow(self, name: str, description=None, mode: str = "manifest") -> WorkflowDefinition:
|
|
101
|
-
"""Create new workflow in memory"""
|
|
102
|
-
workflow_definition = WorkflowDefinition(
|
|
103
|
-
name=name, description=description, steps=[], system_components=[], configs=[]
|
|
104
|
-
)
|
|
105
|
-
self.register_workflow(BaseWorkflow, name, workflow_definition)
|
|
106
|
-
self.save_workflow_to_storage(name)
|
|
107
|
-
return workflow_definition
|
|
108
|
-
|
|
109
|
-
def load_single_workflow_from_storage(self, workflow_name: str):
|
|
110
|
-
"""Load single workflow from storage into memory"""
|
|
111
|
-
|
|
112
|
-
# check if workflow is already loaded. If so, perform context cleanup first
|
|
113
|
-
if workflow_name in self.workflow_registry:
|
|
114
|
-
self.workflow_registry[workflow_name].cleanup_workflow_context()
|
|
115
|
-
del self.workflow_registry[workflow_name]
|
|
116
|
-
|
|
117
|
-
workflow_path = self.workflows_storage_path / workflow_name
|
|
118
|
-
# THIS IS WHERE WE LOAD THE WORKFLOW MODULES
|
|
119
|
-
if workflow_path.is_dir():
|
|
120
|
-
logging.info(f"Loading workflow {workflow_name} from {workflow_path}")
|
|
121
|
-
try:
|
|
122
|
-
if (workflow_definition_path := workflow_path / "workflow.yaml").exists():
|
|
123
|
-
with workflow_definition_path.open() as workflow_definition_file:
|
|
124
|
-
workflow_definition: WorkflowDefinition = BaseWorkflow.deserialize_definition(
|
|
125
|
-
workflow_definition_file.read(), output_format="yaml"
|
|
126
|
-
)
|
|
127
|
-
else:
|
|
128
|
-
logging.info(f"Definition file {workflow_definition_path} not found, skipping")
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
# This is convinience feature when user wants to share self contained workflow
|
|
132
|
-
# folder as single zip file.
|
|
133
|
-
if (workflow_path / "rules").exists():
|
|
134
|
-
logging.info(f"Copying rules from {workflow_path / 'rules'} to {self.rules_storage_path} ")
|
|
135
|
-
for rule_file in os.listdir(workflow_path / "rules"):
|
|
136
|
-
shutil.copy(workflow_path / "rules" / rule_file, self.rules_storage_path)
|
|
137
|
-
|
|
138
|
-
self.steps_registry.load_custom_step_classes(workflow_path, workflow_name)
|
|
139
|
-
self.register_workflow(BaseWorkflow, workflow_name, workflow_definition)
|
|
140
|
-
|
|
141
|
-
except Exception as e:
|
|
142
|
-
trace = traceback.format_exc()
|
|
143
|
-
logging.error(f"Error loading workflow {workflow_name}: error: {e} trace : {trace}")
|
|
144
|
-
|
|
145
|
-
def load_workflows_from_storage(self):
|
|
146
|
-
"""Loads workflows from disk/storage into memory, initializes and register them in the workflow registry"""
|
|
147
|
-
|
|
148
|
-
# set workflow storage path
|
|
149
|
-
workflows_storage_path = self.workflows_storage_path
|
|
150
|
-
|
|
151
|
-
# set system path to be used when importing individual workflows as python modules
|
|
152
|
-
# via importlib.import_module(...)
|
|
153
|
-
sys.path.append(str(workflows_storage_path))
|
|
154
|
-
|
|
155
|
-
for workflow_name in os.listdir(workflows_storage_path):
|
|
156
|
-
self.load_single_workflow_from_storage(workflow_name)
|
|
157
|
-
|
|
158
|
-
def register_workflow(self, obj, workflow_name, workflow_definition):
|
|
159
|
-
"""Register workflow in the workflow registry
|
|
160
|
-
|
|
161
|
-
Parameters
|
|
162
|
-
----------
|
|
163
|
-
obj : BaseWorkflow
|
|
164
|
-
Class of the workflow to be registered
|
|
165
|
-
workflow_name : _type_
|
|
166
|
-
Name of the workflow to be registered
|
|
167
|
-
workflow_definition : _type_
|
|
168
|
-
Definition of the workflow to be registered originating from workflow.yaml
|
|
169
|
-
"""
|
|
170
|
-
self.workflow_registry[workflow_name] = obj(workflow_name, self.client, steps_registry=self.steps_registry)
|
|
171
|
-
self.workflow_registry[workflow_name].set_definition(workflow_definition)
|
|
172
|
-
# Comment: Not entirely sure what is task_builder meant for
|
|
173
|
-
# as at first glance it looks like circular import ???
|
|
174
|
-
self.workflow_registry[workflow_name].set_task_builder(self.task_builder)
|
|
175
|
-
self.workflow_registry[workflow_name].set_default_dataset_id(self.data_set_id)
|
|
176
|
-
self.workflow_registry[workflow_name].set_storage_path("transformation_rules", self.rules_storage_path)
|
|
177
|
-
self.workflow_registry[workflow_name].set_storage_path("data_store", self.data_store_path)
|
|
178
|
-
|
|
179
|
-
def create_workflow_instance(self, template_name: str, add_to_registry: bool = True) -> BaseWorkflow:
|
|
180
|
-
new_instance = self.workflow_registry[template_name].copy()
|
|
181
|
-
if add_to_registry:
|
|
182
|
-
self.ephemeral_instance_registry[new_instance.instance_id] = new_instance
|
|
183
|
-
live_workflow_instances.labels(itype="ephemeral").set(len(self.ephemeral_instance_registry))
|
|
184
|
-
return new_instance
|
|
185
|
-
|
|
186
|
-
def get_workflow_instance(self, instance_id: str) -> BaseWorkflow:
|
|
187
|
-
return self.ephemeral_instance_registry[instance_id]
|
|
188
|
-
|
|
189
|
-
def delete_workflow_instance(self, instance_id: str):
|
|
190
|
-
del self.ephemeral_instance_registry[instance_id]
|
|
191
|
-
live_workflow_instances.labels(itype="ephemeral").set(len(self.ephemeral_instance_registry))
|
|
192
|
-
return
|
|
193
|
-
|
|
194
|
-
def start_workflow_instance(
|
|
195
|
-
self, workflow_name: str, step_id: str = "", flow_msg: FlowMessage | None = None, sync: bool | None = None
|
|
196
|
-
) -> WorkflowStartStatus:
|
|
197
|
-
retrieved = self.get_workflow(workflow_name)
|
|
198
|
-
|
|
199
|
-
if retrieved is None:
|
|
200
|
-
return WorkflowStartStatus(
|
|
201
|
-
workflow_instance=None, is_success=False, status_text="Workflow not found in registry"
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
if self._is_workflow_made_of_mixed_steps(retrieved.workflow_steps):
|
|
205
|
-
retrieved.state = WorkflowState.FAILED
|
|
206
|
-
return WorkflowStartStatus(
|
|
207
|
-
workflow_instance=None,
|
|
208
|
-
is_success=False,
|
|
209
|
-
status_text="Workflow consists of both legacy and current steps. "
|
|
210
|
-
"Please update the workflow to use only current steps.",
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
workflow = retrieved
|
|
214
|
-
retrieved_step = workflow.get_trigger_step(step_id)
|
|
215
|
-
if retrieved_step is None:
|
|
216
|
-
return WorkflowStartStatus(
|
|
217
|
-
workflow_instance=None, is_success=False, status_text="Step not found in workflow"
|
|
218
|
-
)
|
|
219
|
-
trigger_step = retrieved_step
|
|
220
|
-
if not trigger_step.trigger:
|
|
221
|
-
logging.info(f"Step {step_id} is not a trigger step")
|
|
222
|
-
return WorkflowStartStatus(
|
|
223
|
-
workflow_instance=None, is_success=False, status_text="Step is not a trigger step"
|
|
224
|
-
)
|
|
225
|
-
if sync is None:
|
|
226
|
-
sync = trigger_step.params.get("sync", "true").lower() == "true"
|
|
227
|
-
|
|
228
|
-
max_wait_time = int(trigger_step.params.get("max_wait_time", "30"))
|
|
229
|
-
instance_start_method = trigger_step.params.get(
|
|
230
|
-
"workflow_start_method", InstanceStartMethod.PERSISTENT_INSTANCE_BLOCKING
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
logging.info(
|
|
234
|
-
f"""----- Starting workflow {workflow_name} , step_id = {step_id} , sync = {sync},
|
|
235
|
-
max_wait_time = {max_wait_time}, instance_start_method = {instance_start_method} -----"""
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
if instance_start_method == InstanceStartMethod.PERSISTENT_INSTANCE_BLOCKING:
|
|
239
|
-
live_workflow_instances.labels(itype="persistent").set(len(self.workflow_registry))
|
|
240
|
-
# wait until workflow transition to RUNNING state and then start , set max wait time to 30 seconds
|
|
241
|
-
start_time = time.perf_counter()
|
|
242
|
-
# wait until workflow transition to RUNNING state and then start , set max wait time to 30 seconds.
|
|
243
|
-
# The operation is executed in callers thread
|
|
244
|
-
while workflow.state == WorkflowState.RUNNING:
|
|
245
|
-
logging.info("Existing workflow instance already running , waiting for RUNNING state")
|
|
246
|
-
elapsed_time = time.perf_counter() - start_time
|
|
247
|
-
if elapsed_time > max_wait_time:
|
|
248
|
-
logging.info(
|
|
249
|
-
f"Workflow {workflow_name} wait time exceeded . "
|
|
250
|
-
f"elapsed time = {elapsed_time}, max wait time = {max_wait_time}"
|
|
251
|
-
)
|
|
252
|
-
return WorkflowStartStatus(
|
|
253
|
-
workflow_instance=None,
|
|
254
|
-
is_success=False,
|
|
255
|
-
status_text="Workflow instance already running.Wait time exceeded",
|
|
256
|
-
)
|
|
257
|
-
time.sleep(0.5)
|
|
258
|
-
workflow.start(sync=sync, flow_message=flow_msg, start_step_id=step_id)
|
|
259
|
-
return WorkflowStartStatus(workflow_instance=workflow, is_success=True, status_text="")
|
|
260
|
-
|
|
261
|
-
elif instance_start_method == InstanceStartMethod.PERSISTENT_INSTANCE_NON_BLOCKING:
|
|
262
|
-
live_workflow_instances.labels(itype="persistent").set(len(self.workflow_registry))
|
|
263
|
-
# start workflow if not already running, skip if already running
|
|
264
|
-
if workflow.state == WorkflowState.RUNNING:
|
|
265
|
-
return WorkflowStartStatus(
|
|
266
|
-
workflow_instance=None, is_success=False, status_text="Workflow instance already running"
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
workflow.start(sync=sync, flow_message=flow_msg, start_step_id=step_id)
|
|
270
|
-
return WorkflowStartStatus(workflow_instance=workflow, is_success=True, status_text="")
|
|
271
|
-
|
|
272
|
-
elif instance_start_method == InstanceStartMethod.EPHEMERAL_INSTANCE:
|
|
273
|
-
# start new workflow instance in new thread
|
|
274
|
-
workflow = self.create_workflow_instance(workflow_name, add_to_registry=True)
|
|
275
|
-
workflow.start(sync=sync, delete_after_completion=True, flow_message=flow_msg, start_step_id=step_id)
|
|
276
|
-
if sync:
|
|
277
|
-
self.delete_workflow_instance(workflow.instance_id)
|
|
278
|
-
return WorkflowStartStatus(workflow_instance=workflow, is_success=True, status_text="")
|
|
279
|
-
|
|
280
|
-
return WorkflowStartStatus(
|
|
281
|
-
workflow_instance=None, is_success=False, status_text="Unsupported workflow start method"
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
def _is_workflow_made_of_mixed_steps(self, steps: list[WorkflowStepDefinition]):
|
|
285
|
-
legacy_steps = 0
|
|
286
|
-
current_steps = 0
|
|
287
|
-
for step in steps:
|
|
288
|
-
if step.method in self.steps_registry.categorized_steps["legacy"]:
|
|
289
|
-
legacy_steps += 1
|
|
290
|
-
if step.method in self.steps_registry.categorized_steps["current"]:
|
|
291
|
-
current_steps += 1
|
|
292
|
-
return legacy_steps > 0 and current_steps > 0
|
cognite/neat/_workflows/model.py
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
if sys.version_info >= (3, 11):
|
|
5
|
-
from enum import StrEnum
|
|
6
|
-
else:
|
|
7
|
-
from backports.strenum import StrEnum
|
|
8
|
-
|
|
9
|
-
from pydantic import BaseModel, Field, field_validator
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class WorkflowState(StrEnum):
|
|
13
|
-
CREATED = "CREATED"
|
|
14
|
-
RUNNING = "RUNNING"
|
|
15
|
-
RUNNING_WAITING = "RUNNING_WAITING"
|
|
16
|
-
COMPLETED = "COMPLETED"
|
|
17
|
-
FAILED = "FAILED"
|
|
18
|
-
EXPIRED = "EXPIRED"
|
|
19
|
-
UNKNOWN = "UNKNOWN"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class StepExecutionStatus(StrEnum):
|
|
23
|
-
SUCCESS = "COMPLETED"
|
|
24
|
-
FAILED = "FAILED"
|
|
25
|
-
SKIPPED = "SKIPPED"
|
|
26
|
-
STARTED = "STARTED"
|
|
27
|
-
UNKNOWN = "UNKNOWN"
|
|
28
|
-
ABORT_AND_FAIL = "ABORT_AND_FAIL"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class InstanceStartMethod(StrEnum):
|
|
32
|
-
PERSISTENT_INSTANCE_NON_BLOCKING = "persistent_non_blocking"
|
|
33
|
-
PERSISTENT_INSTANCE_BLOCKING = "persistent_blocking"
|
|
34
|
-
EPHEMERAL_INSTANCE = "ephemeral_instance"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class WorkflowStartException(Exception):
|
|
38
|
-
pass
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class FlowMessage(BaseModel):
|
|
42
|
-
"""A message that can be sent between steps in a workflow.It's the only parameter step takes as input."""
|
|
43
|
-
|
|
44
|
-
payload: Any = None # The payload of the message
|
|
45
|
-
headers: dict[str, str] | None = None # The headers of the message
|
|
46
|
-
output_text: str | None = None # The output text of the step that is captured in the execution log
|
|
47
|
-
error_text: str | None = None # The error text of the step that is captured in the execution log
|
|
48
|
-
next_step_ids: list[str] | None = (
|
|
49
|
-
None # If set, the workflow will skip default route and go to the next step in the list
|
|
50
|
-
)
|
|
51
|
-
step_execution_status: StepExecutionStatus = StepExecutionStatus.UNKNOWN # The status of the step execution
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class StepType(StrEnum):
|
|
55
|
-
PYSTEP = "pystep"
|
|
56
|
-
STD_STEP = "stdstep"
|
|
57
|
-
START_WORKFLOW_TASK_STEP = "start_workflow_task_step" # Call a workflow from another workflow
|
|
58
|
-
CONTAINER_TASK_STEP = "container_step" # TODO: implement
|
|
59
|
-
CLI_TASK_STEP = "cli_step" # TODO: implement
|
|
60
|
-
HTTP_TRIGGER = "http_trigger"
|
|
61
|
-
TIME_TRIGGER = "time_trigger"
|
|
62
|
-
WAIT_FOR_EVENT = "wait_for_event" # TODO: implement
|
|
63
|
-
EVENT_TRIGGER = "event_trigger" # TODO: implement
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class UIConfig(BaseModel):
|
|
67
|
-
pos_x: int = 0
|
|
68
|
-
pos_y: int = 0
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class WorkflowConfigItem(BaseModel):
|
|
72
|
-
name: str
|
|
73
|
-
value: str | None = None
|
|
74
|
-
label: str | None = None
|
|
75
|
-
type: str | None = None
|
|
76
|
-
required: bool = False
|
|
77
|
-
options: list[str] | None = None
|
|
78
|
-
group: str | None = None
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class WorkflowStepDefinition(BaseModel):
|
|
82
|
-
id: str
|
|
83
|
-
label: str | None = None
|
|
84
|
-
stype: str = StepType.PYSTEP
|
|
85
|
-
description: str | None = None
|
|
86
|
-
method: str | None = None
|
|
87
|
-
enabled: bool = True
|
|
88
|
-
system_component_id: str | None = None
|
|
89
|
-
trigger: bool = False
|
|
90
|
-
transition_to: list[str] | None = None
|
|
91
|
-
ui_config: UIConfig = UIConfig()
|
|
92
|
-
params: dict[str, str] = Field(default_factory=dict) # System parameters
|
|
93
|
-
configs: dict[str, Any] = Field(default_factory=dict) # Step configurations
|
|
94
|
-
complex_configs: dict[str, Any] = Field(default_factory=dict) # Complex step configurations
|
|
95
|
-
max_retries: int = 0
|
|
96
|
-
retry_delay: int = 3
|
|
97
|
-
auto_workflow_cleanup: bool = False
|
|
98
|
-
|
|
99
|
-
@field_validator("configs", "params", mode="before")
|
|
100
|
-
def none_as_empty_dict(cls, value):
|
|
101
|
-
if value is None:
|
|
102
|
-
return {}
|
|
103
|
-
return value
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
class WorkflowSystemComponent(BaseModel):
|
|
107
|
-
# Container for steps
|
|
108
|
-
id: str
|
|
109
|
-
label: str
|
|
110
|
-
transition_to: list[str] | None = None
|
|
111
|
-
description: str | None = None
|
|
112
|
-
ui_config: UIConfig = UIConfig()
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class WorkflowDefinition(BaseModel):
|
|
116
|
-
"""Workflow definition"""
|
|
117
|
-
|
|
118
|
-
name: str
|
|
119
|
-
description: str | None = None
|
|
120
|
-
implementation_module: str | None = None
|
|
121
|
-
steps: list[WorkflowStepDefinition] = Field(default_factory=list)
|
|
122
|
-
system_components: list[WorkflowSystemComponent] = Field(default_factory=list)
|
|
123
|
-
configs: list[WorkflowConfigItem] = Field(default_factory=list)
|
|
124
|
-
|
|
125
|
-
def get_config_item(self, name: str) -> WorkflowConfigItem | None:
|
|
126
|
-
for config in self.configs:
|
|
127
|
-
if config.name == name:
|
|
128
|
-
return config
|
|
129
|
-
return None
|
|
130
|
-
|
|
131
|
-
def upsert_config_item(self, config_item: WorkflowConfigItem) -> None:
|
|
132
|
-
for config in self.configs:
|
|
133
|
-
if config.name == config_item.name:
|
|
134
|
-
config.value = config_item.value
|
|
135
|
-
config.label = config_item.label
|
|
136
|
-
config.type = config_item.type
|
|
137
|
-
config.required = config_item.required
|
|
138
|
-
config.options = config_item.options
|
|
139
|
-
config.group = config_item.group
|
|
140
|
-
return
|
|
141
|
-
self.configs.append(config_item)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
class WorkflowStepEvent(BaseModel):
|
|
145
|
-
"""Workflow step event represent a single step execution"""
|
|
146
|
-
|
|
147
|
-
id: str
|
|
148
|
-
system_component_id: str | None = None
|
|
149
|
-
state: StepExecutionStatus
|
|
150
|
-
elapsed_time: float
|
|
151
|
-
timestamp: str
|
|
152
|
-
error: str | None = None
|
|
153
|
-
output_text: str | None = None
|
|
154
|
-
data: Any = None
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
class WorkflowFullStateReport(BaseModel):
|
|
158
|
-
"""Workflow state report is complete log of workflow execution"""
|
|
159
|
-
|
|
160
|
-
workflow_name: str | None = None
|
|
161
|
-
workflow_version: str | None = None
|
|
162
|
-
run_id: str | None = None
|
|
163
|
-
state: WorkflowState
|
|
164
|
-
start_time: int | None = None
|
|
165
|
-
end_time: int | None = None
|
|
166
|
-
elapsed_time: float = 0
|
|
167
|
-
last_error: str | None = None
|
|
168
|
-
execution_log: list[WorkflowStepEvent]
|
|
169
|
-
last_updated_time: int | None = None
|
|
170
|
-
|
|
171
|
-
@field_validator("start_time", "end_time", mode="before")
|
|
172
|
-
def float_to_int(cls, value):
|
|
173
|
-
if isinstance(value, float):
|
|
174
|
-
return int(value)
|
|
175
|
-
return value
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
class WorkflowConfigs(BaseModel):
|
|
179
|
-
"""Workflow configs"""
|
|
180
|
-
|
|
181
|
-
configs: list[WorkflowConfigItem] = []
|
|
182
|
-
|
|
183
|
-
def get_config_item(self, config_name: str) -> WorkflowConfigItem | None:
|
|
184
|
-
return next((item for item in self.configs if item.name == config_name), None)
|
|
185
|
-
|
|
186
|
-
def set_config_item(self, config_item: WorkflowConfigItem):
|
|
187
|
-
for item in self.configs:
|
|
188
|
-
if item.name == config_item.name:
|
|
189
|
-
item.value = config_item.value
|
|
190
|
-
return
|
|
191
|
-
self.configs.append(config_item)
|
|
192
|
-
|
|
193
|
-
def get_config_group_values_by_name(
|
|
194
|
-
self, group_name: str, remove_group_prefix: bool = True
|
|
195
|
-
) -> dict[str, str | None]:
|
|
196
|
-
return {
|
|
197
|
-
(item.name.removeprefix(item.group) if remove_group_prefix else item.name): item.value
|
|
198
|
-
for item in self.configs
|
|
199
|
-
if item.group == group_name
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
def get_config_item_value(self, config_name: str, default_value=None) -> str | None:
|
|
203
|
-
return config.value if (config := self.get_config_item(config_name)) else default_value
|
|
File without changes
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
from cognite.client import CogniteClient
|
|
4
|
-
from cognite.client.data_classes import (
|
|
5
|
-
Asset,
|
|
6
|
-
AssetUpdate,
|
|
7
|
-
Relationship,
|
|
8
|
-
RelationshipUpdate,
|
|
9
|
-
)
|
|
10
|
-
from cognite.client.data_classes.data_modeling import EdgeApply, NodeApply
|
|
11
|
-
|
|
12
|
-
from cognite.neat._rules.models import (
|
|
13
|
-
DMSRules,
|
|
14
|
-
InformationRules,
|
|
15
|
-
)
|
|
16
|
-
from cognite.neat._store import NeatGraphStore
|
|
17
|
-
from cognite.neat._workflows.steps.step_model import DataContract
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class MultiRuleData(DataContract):
|
|
21
|
-
information: InformationRules | None = None
|
|
22
|
-
dms: DMSRules | None = None
|
|
23
|
-
|
|
24
|
-
@classmethod
|
|
25
|
-
def from_rules(cls, rules: InformationRules | DMSRules):
|
|
26
|
-
if isinstance(rules, InformationRules):
|
|
27
|
-
return cls(information=rules)
|
|
28
|
-
elif isinstance(rules, DMSRules):
|
|
29
|
-
return cls(dms=rules)
|
|
30
|
-
else:
|
|
31
|
-
raise ValueError(f"Unsupported rules type {type(rules)}")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class PathData(DataContract):
|
|
35
|
-
"""
|
|
36
|
-
This represents the path to the excel file.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
excel_file_path: The path to the excel file.
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
excel_file_path: Path
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class NeatGraph(DataContract):
|
|
46
|
-
"""
|
|
47
|
-
This represents the neat graph.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
graph: The neat graph store.
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
graph: NeatGraphStore
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class ClientData(DataContract):
|
|
57
|
-
"""
|
|
58
|
-
This represents an instantiated CogniteClient object.
|
|
59
|
-
|
|
60
|
-
Args:
|
|
61
|
-
client: The CogniteClient object.
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
client: CogniteClient
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class CategorizedAssets(DataContract):
|
|
68
|
-
""" "
|
|
69
|
-
This represents the categorized assets.
|
|
70
|
-
|
|
71
|
-
Args:
|
|
72
|
-
assets: The categorized assets.
|
|
73
|
-
"""
|
|
74
|
-
|
|
75
|
-
assets: dict[str, list[Asset | AssetUpdate]]
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
class CategorizedRelationships(DataContract):
|
|
79
|
-
"""
|
|
80
|
-
This represents the categorized relationships.
|
|
81
|
-
|
|
82
|
-
Args:
|
|
83
|
-
relationships: The categorized relationships.
|
|
84
|
-
|
|
85
|
-
"""
|
|
86
|
-
|
|
87
|
-
relationships: dict[str, list[Relationship] | list[RelationshipUpdate]]
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
class Nodes(DataContract):
|
|
91
|
-
"""
|
|
92
|
-
This represents nodes.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
nodes: list of nodes.
|
|
96
|
-
"""
|
|
97
|
-
|
|
98
|
-
nodes: list[NodeApply]
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class Edges(DataContract):
|
|
102
|
-
"""
|
|
103
|
-
This represents edges.
|
|
104
|
-
|
|
105
|
-
Args:
|
|
106
|
-
edges: list of edges.
|
|
107
|
-
"""
|
|
108
|
-
|
|
109
|
-
edges: list[EdgeApply]
|
|
File without changes
|