cognite-neat 0.104.0__py3-none-any.whl → 0.105.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +56 -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 +13 -2
- cognite/neat/_graph/transformers/__init__.py +3 -1
- cognite/neat/_graph/transformers/_classic_cdf.py +2 -1
- 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 +4 -1
- cognite/neat/_issues/warnings/_general.py +7 -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.0.dist-info}/METADATA +3 -2
- cognite_neat-0.105.0.dist-info/RECORD +179 -0
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.0.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.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import importlib
|
|
2
|
-
import inspect
|
|
3
|
-
import logging
|
|
4
|
-
import os
|
|
5
|
-
import sys
|
|
6
|
-
import types
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Any
|
|
9
|
-
|
|
10
|
-
from pydantic import BaseModel
|
|
11
|
-
|
|
12
|
-
# steps
|
|
13
|
-
import cognite.neat._workflows.steps.lib.current
|
|
14
|
-
import cognite.neat._workflows.steps.lib.io
|
|
15
|
-
from cognite.neat._app.monitoring.metrics import NeatMetricsCollector
|
|
16
|
-
from cognite.neat._config import Config
|
|
17
|
-
from cognite.neat._issues.errors import WorkflowConfigurationNotSetError, WorkFlowMissingDataError
|
|
18
|
-
from cognite.neat._workflows.model import FlowMessage, WorkflowConfigs
|
|
19
|
-
from cognite.neat._workflows.steps.step_model import Configurable, DataContract, Step
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class StepMetadata(BaseModel):
|
|
23
|
-
name: str
|
|
24
|
-
description: str = ""
|
|
25
|
-
category: str = "default" # defines the category the step belongs to (e.g. "data", "model", "test")
|
|
26
|
-
scope: str = "global" # defines the scope of the step (e.g. "global", local to a specific workflow)
|
|
27
|
-
input: list[str]
|
|
28
|
-
output: list[str]
|
|
29
|
-
configurables: list[Configurable] = []
|
|
30
|
-
version: str = "1.0.0" # version of the step. All alpha versions considered as experimental
|
|
31
|
-
docs_url: str = "" # url to the extended documentation of the step
|
|
32
|
-
source: str = (
|
|
33
|
-
"cognite" # source of the step , can be source identifier or url , for instance github url for instance.
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class StepsRegistry:
|
|
38
|
-
def __init__(self, config: Config):
|
|
39
|
-
self.config = config
|
|
40
|
-
self._step_classes: list[type[Step]] = []
|
|
41
|
-
self.user_steps_path: Path = config.data_store_path / "steps"
|
|
42
|
-
self.data_store_path: str = str(config.data_store_path)
|
|
43
|
-
self.categorized_steps: dict[str, set] = {
|
|
44
|
-
"legacy": set(),
|
|
45
|
-
"current": set(),
|
|
46
|
-
"io": set(),
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
def load_step_classes(self):
|
|
50
|
-
if self._step_classes:
|
|
51
|
-
# classes already loaded - no need to reload
|
|
52
|
-
return
|
|
53
|
-
for name, step_cls in inspect.getmembers(cognite.neat._workflows.steps.lib.current):
|
|
54
|
-
if inspect.isclass(step_cls):
|
|
55
|
-
logging.info(f"Loading NEAT step {name}")
|
|
56
|
-
self._step_classes.append(step_cls)
|
|
57
|
-
self.categorized_steps["current"].add(name)
|
|
58
|
-
for name, step_cls in inspect.getmembers(cognite.neat._workflows.steps.lib.io):
|
|
59
|
-
if inspect.isclass(step_cls):
|
|
60
|
-
logging.info(f"Loading NEAT step {name}")
|
|
61
|
-
self._step_classes.append(step_cls)
|
|
62
|
-
self.categorized_steps["io"].add(name)
|
|
63
|
-
sys.path.append(str(Path(self.data_store_path) / "workflows"))
|
|
64
|
-
try:
|
|
65
|
-
if self.user_steps_path:
|
|
66
|
-
sys.path.append(str(self.user_steps_path))
|
|
67
|
-
self.load_custom_step_classes(self.user_steps_path, scope="user_global")
|
|
68
|
-
except Exception as e:
|
|
69
|
-
logging.info(f"No user defined modules provided in {self.user_steps_path}. Error: {e}")
|
|
70
|
-
|
|
71
|
-
def load_workflow_step_classes(self, workflow_name: str):
|
|
72
|
-
if not self.data_store_path:
|
|
73
|
-
raise WorkflowConfigurationNotSetError("data_store_path")
|
|
74
|
-
workflow_steps_path = Path(self.data_store_path) / "workflows" / workflow_name
|
|
75
|
-
if workflow_steps_path.exists():
|
|
76
|
-
self.load_custom_step_classes(workflow_steps_path, scope="workflow")
|
|
77
|
-
|
|
78
|
-
def load_custom_step_classes(self, custom_steps_path: Path, scope: str = "global"):
|
|
79
|
-
for step_module_name in os.listdir(custom_steps_path):
|
|
80
|
-
step_module_path = custom_steps_path / Path(step_module_name)
|
|
81
|
-
if step_module_name.startswith("__") or (
|
|
82
|
-
step_module_path.is_file() and not step_module_name.endswith(".py")
|
|
83
|
-
):
|
|
84
|
-
continue
|
|
85
|
-
if scope == "user_global":
|
|
86
|
-
full_module_name = step_module_name.replace(".py", "")
|
|
87
|
-
else:
|
|
88
|
-
full_module_name = custom_steps_path.name + "." + step_module_name.replace(".py", "")
|
|
89
|
-
logging.info(f"Loading user defined step module {full_module_name}")
|
|
90
|
-
if full_module_name in sys.modules:
|
|
91
|
-
logging.info(f"Reloading existing workflow module {full_module_name}")
|
|
92
|
-
steps_module = importlib.reload(sys.modules[full_module_name])
|
|
93
|
-
else:
|
|
94
|
-
steps_module = importlib.import_module(full_module_name)
|
|
95
|
-
logging.info(f"Loading user defined step from {steps_module}")
|
|
96
|
-
for name, step_cls in inspect.getmembers(steps_module):
|
|
97
|
-
base_class = getattr(step_cls, "__bases__", None)
|
|
98
|
-
if (
|
|
99
|
-
name.startswith("__")
|
|
100
|
-
or base_class is None
|
|
101
|
-
or len(base_class) == 0
|
|
102
|
-
or base_class[0].__name__ != "Step"
|
|
103
|
-
):
|
|
104
|
-
continue
|
|
105
|
-
logging.info(f"Loading user defined step {name} from {steps_module}")
|
|
106
|
-
if inspect.isclass(step_cls):
|
|
107
|
-
step_cls.scope = scope
|
|
108
|
-
is_new_class = True
|
|
109
|
-
for i, step in enumerate(self._step_classes):
|
|
110
|
-
if step.__name__ == step_cls.__name__:
|
|
111
|
-
logging.info(f"Reloading user defined step {name} class {step_cls.__name__}")
|
|
112
|
-
self._step_classes[i] = step_cls
|
|
113
|
-
is_new_class = False
|
|
114
|
-
if is_new_class:
|
|
115
|
-
logging.info(f"Loading NEW user defined step {name} class {step_cls.__name__}")
|
|
116
|
-
self._step_classes.append(step_cls)
|
|
117
|
-
|
|
118
|
-
def run_step(
|
|
119
|
-
self,
|
|
120
|
-
step_name: str,
|
|
121
|
-
flow_context: dict[str, DataContract],
|
|
122
|
-
step_configs: dict[str, Any],
|
|
123
|
-
metrics: NeatMetricsCollector | None = None,
|
|
124
|
-
workflow_configs: WorkflowConfigs | None = None,
|
|
125
|
-
workflow_id: str = "",
|
|
126
|
-
workflow_run_id: str = "",
|
|
127
|
-
step_complex_configs: dict[str, Any] | None = None,
|
|
128
|
-
) -> DataContract | tuple[FlowMessage, DataContract] | FlowMessage:
|
|
129
|
-
if step_complex_configs is None:
|
|
130
|
-
step_complex_configs = {}
|
|
131
|
-
for step_cls in self._step_classes:
|
|
132
|
-
if step_cls.__name__ == step_name:
|
|
133
|
-
step_obj: Step = step_cls(config=self.config)
|
|
134
|
-
step_obj.configure(step_configs, step_complex_configs)
|
|
135
|
-
step_obj.set_flow_context(flow_context)
|
|
136
|
-
step_obj.set_metrics(metrics)
|
|
137
|
-
step_obj.set_workflow_configs(workflow_configs)
|
|
138
|
-
step_obj.set_workflow_metadata(workflow_id, workflow_run_id)
|
|
139
|
-
signature = inspect.signature(step_obj.run)
|
|
140
|
-
parameters = signature.parameters
|
|
141
|
-
is_valid = True
|
|
142
|
-
input_data = []
|
|
143
|
-
missing_data = []
|
|
144
|
-
for parameter in parameters.values():
|
|
145
|
-
try:
|
|
146
|
-
if parameter.annotation is FlowMessage:
|
|
147
|
-
input_data.append(flow_context["FlowMessage"])
|
|
148
|
-
else:
|
|
149
|
-
if isinstance(parameter.annotation, types.UnionType):
|
|
150
|
-
for param in parameter.annotation.__args__:
|
|
151
|
-
if (
|
|
152
|
-
param.__name__ != "_empty"
|
|
153
|
-
and param.__name__ in flow_context
|
|
154
|
-
and flow_context[param.__name__] is not None
|
|
155
|
-
):
|
|
156
|
-
input_data.append(flow_context[param.__name__])
|
|
157
|
-
break # Only one variable can be used as input
|
|
158
|
-
else:
|
|
159
|
-
input_data.append(flow_context[parameter.annotation.__name__])
|
|
160
|
-
except KeyError:
|
|
161
|
-
is_valid = False
|
|
162
|
-
logging.error(f"Missing data for step {step_name} parameter {parameter.name}")
|
|
163
|
-
missing_data.append(parameter.annotation.__name__)
|
|
164
|
-
continue
|
|
165
|
-
if not is_valid:
|
|
166
|
-
raise WorkFlowMissingDataError(step_name, frozenset(missing_data))
|
|
167
|
-
return step_obj.run(*input_data)
|
|
168
|
-
raise WorkFlowMissingDataError(step_name, frozenset({}))
|
|
169
|
-
|
|
170
|
-
def get_list_of_steps(self) -> list[StepMetadata]:
|
|
171
|
-
steps: list[StepMetadata] = []
|
|
172
|
-
for step_cls in self._step_classes:
|
|
173
|
-
try:
|
|
174
|
-
signature = inspect.signature(step_cls.run)
|
|
175
|
-
parameters = signature.parameters
|
|
176
|
-
input_data = []
|
|
177
|
-
output_data = []
|
|
178
|
-
for parameter in parameters.values():
|
|
179
|
-
if isinstance(parameter.annotation, types.UnionType):
|
|
180
|
-
for param in parameter.annotation.__args__:
|
|
181
|
-
if param.__name__ != "_empty":
|
|
182
|
-
input_data.append(param.__name__)
|
|
183
|
-
elif parameter.annotation.__name__ != "_empty":
|
|
184
|
-
input_data.append(parameter.annotation.__name__)
|
|
185
|
-
return_annotation = signature.return_annotation
|
|
186
|
-
if return_annotation:
|
|
187
|
-
if isinstance(return_annotation, types.UnionType):
|
|
188
|
-
for annotation in return_annotation.__args__:
|
|
189
|
-
output_data.append(annotation.__name__)
|
|
190
|
-
elif isinstance(return_annotation, tuple):
|
|
191
|
-
for annotation in return_annotation:
|
|
192
|
-
if isinstance(annotation, types.UnionType):
|
|
193
|
-
for annotation_l2 in annotation.__args__:
|
|
194
|
-
output_data.append(annotation_l2.__name__)
|
|
195
|
-
else:
|
|
196
|
-
output_data.append(annotation.__name__)
|
|
197
|
-
else:
|
|
198
|
-
output_data.append(return_annotation.__name__)
|
|
199
|
-
steps.append(
|
|
200
|
-
StepMetadata(
|
|
201
|
-
name=step_cls.__name__,
|
|
202
|
-
scope=step_cls.scope,
|
|
203
|
-
input=input_data,
|
|
204
|
-
description=step_cls.description,
|
|
205
|
-
category=step_cls.category,
|
|
206
|
-
output=output_data,
|
|
207
|
-
configurables=step_cls.configurables,
|
|
208
|
-
version=step_cls.version,
|
|
209
|
-
source=step_cls.source,
|
|
210
|
-
docs_url=step_cls.docs_url,
|
|
211
|
-
)
|
|
212
|
-
)
|
|
213
|
-
except AttributeError as e:
|
|
214
|
-
logging.error(
|
|
215
|
-
f"Step {type(step_cls).__name__} does not have a run method or types can't be infered.Error: {e}"
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
return steps
|
cognite/neat/_workflows/tasks.py
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from cognite.client import CogniteClient
|
|
2
|
-
|
|
3
|
-
from cognite.neat._workflows.model import FlowMessage
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class WorkflowTaskBuilder:
|
|
7
|
-
"""Collection of all base tasks for workflows.All tasks must run in the context of a workflow including threads."""
|
|
8
|
-
|
|
9
|
-
def __init__(self, cdf_client: CogniteClient | None, workflow_manager):
|
|
10
|
-
# TODO : figure out circular import and set type to WorkflowManager
|
|
11
|
-
self.cdf_client = cdf_client
|
|
12
|
-
self.workflow_manager = workflow_manager
|
|
13
|
-
|
|
14
|
-
def start_workflow_task(self, workflow_name: str, sync: bool, flow_message: FlowMessage | None):
|
|
15
|
-
"""Call a workflow task from another workflow"""
|
|
16
|
-
return self.workflow_manager.start_workflow_instance(
|
|
17
|
-
workflow_name=workflow_name, flow_msg=flow_message, sync=sync
|
|
18
|
-
)
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import logging
|
|
3
|
-
import threading
|
|
4
|
-
import time
|
|
5
|
-
|
|
6
|
-
import schedule
|
|
7
|
-
from fastapi import Request
|
|
8
|
-
|
|
9
|
-
from cognite.neat._workflows.manager import WorkflowManager
|
|
10
|
-
from cognite.neat._workflows.model import FlowMessage, StepType, WorkflowState
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class TriggerManager:
|
|
14
|
-
"""Triggers are the way to start a workflow. They are defined in the workflow definition file."""
|
|
15
|
-
|
|
16
|
-
def __init__(self, workflow_manager: WorkflowManager):
|
|
17
|
-
self.workflow_manager = workflow_manager
|
|
18
|
-
self.is_running = False
|
|
19
|
-
self.thread = None
|
|
20
|
-
|
|
21
|
-
def start_workflow_from_http_request(self, workflow_name: str, step_id: str, request: Request, body: bytes):
|
|
22
|
-
logging.info(f"New HTTP trigger request for workflow {workflow_name} step {step_id}")
|
|
23
|
-
headers = dict(request.headers)
|
|
24
|
-
logging.debug(f"Request headers = {headers}")
|
|
25
|
-
json_payload = None
|
|
26
|
-
try:
|
|
27
|
-
json_payload = json.loads(body)
|
|
28
|
-
except Exception as e:
|
|
29
|
-
logging.info(f"Error parsing json body {e}")
|
|
30
|
-
logging.debug(f"Request object = {json_payload}")
|
|
31
|
-
|
|
32
|
-
flow_msg = FlowMessage(payload=json_payload, headers=dict(headers))
|
|
33
|
-
start_status = self.workflow_manager.start_workflow_instance(
|
|
34
|
-
workflow_name=workflow_name, step_id=step_id, flow_msg=flow_msg
|
|
35
|
-
)
|
|
36
|
-
if start_status.is_success and start_status.workflow_instance:
|
|
37
|
-
return start_status.workflow_instance.flow_message
|
|
38
|
-
|
|
39
|
-
def resume_workflow_from_http_request(
|
|
40
|
-
self, workflow_name: str, step_id: str, instance_id: str, request: Request, body: bytes
|
|
41
|
-
):
|
|
42
|
-
if instance_id != "default":
|
|
43
|
-
workflow = self.workflow_manager.get_workflow_instance(instance_id)
|
|
44
|
-
else:
|
|
45
|
-
returned = self.workflow_manager.get_workflow(workflow_name)
|
|
46
|
-
if returned is None:
|
|
47
|
-
return {"result": "Workflow instance not found"}
|
|
48
|
-
workflow = returned
|
|
49
|
-
|
|
50
|
-
json_payload = None
|
|
51
|
-
try:
|
|
52
|
-
json_payload = json.loads(body)
|
|
53
|
-
except ValueError as e:
|
|
54
|
-
logging.info(f"Error parsing json body {e}")
|
|
55
|
-
flow_msg = FlowMessage(payload=json_payload)
|
|
56
|
-
if workflow.state == WorkflowState.RUNNING_WAITING:
|
|
57
|
-
workflow.resume_workflow(flow_message=flow_msg, step_id=step_id)
|
|
58
|
-
return {"result": "Workflow instance resumed"}
|
|
59
|
-
|
|
60
|
-
return {"result": "Workflow instance not in RUNNING_WAITING state"}
|
|
61
|
-
|
|
62
|
-
def _start_scheduler_main_loop(self):
|
|
63
|
-
"""Starts a scheduler main loop for the workflows
|
|
64
|
-
|
|
65
|
-
Parameters
|
|
66
|
-
----------
|
|
67
|
-
|
|
68
|
-
"""
|
|
69
|
-
logging.info("Starting scheduler main loop")
|
|
70
|
-
self.is_running = True
|
|
71
|
-
|
|
72
|
-
def main_loop():
|
|
73
|
-
while self.is_running:
|
|
74
|
-
schedule.run_pending()
|
|
75
|
-
time.sleep(1)
|
|
76
|
-
logging.info("Scheduler main loop stopped")
|
|
77
|
-
|
|
78
|
-
self.thread = threading.Thread(target=main_loop)
|
|
79
|
-
self.thread.start()
|
|
80
|
-
|
|
81
|
-
def stop_scheduler_main_loop(self):
|
|
82
|
-
"""Stops a scheduler main loop for the workflows
|
|
83
|
-
|
|
84
|
-
Parameters
|
|
85
|
-
----------
|
|
86
|
-
|
|
87
|
-
"""
|
|
88
|
-
logging.info("Stopping scheduler main loop")
|
|
89
|
-
self.is_running = False
|
|
90
|
-
if self.thread:
|
|
91
|
-
self.thread.join()
|
|
92
|
-
logging.info("Scheduler main loop stopped")
|
|
93
|
-
|
|
94
|
-
def every_weekday_schedule(self, weekday_str: str):
|
|
95
|
-
return getattr(schedule.every(), weekday_str)
|
|
96
|
-
|
|
97
|
-
def start_time_schedulers(self):
|
|
98
|
-
"""Starts a time scheduler for the workflows
|
|
99
|
-
|
|
100
|
-
Parameters
|
|
101
|
-
----------
|
|
102
|
-
|
|
103
|
-
"""
|
|
104
|
-
logging.info("Starting time trigger scheduler")
|
|
105
|
-
weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
|
|
106
|
-
|
|
107
|
-
trigger_steps_count = 0
|
|
108
|
-
for workflow in self.workflow_manager.workflow_registry.values():
|
|
109
|
-
for step in workflow.workflow_steps:
|
|
110
|
-
if step.trigger and step.stype == StepType.TIME_TRIGGER and step.enabled:
|
|
111
|
-
# params
|
|
112
|
-
every_str = step.params["interval"]
|
|
113
|
-
logging.info(f"Starting time trigger scheduler for {workflow.name} {step.id} {every_str}")
|
|
114
|
-
every_str = every_str.replace("every", "").strip()
|
|
115
|
-
|
|
116
|
-
if "at" in every_str: # "day at 10:30:00"
|
|
117
|
-
every = every_str.split("at ")
|
|
118
|
-
if len(every) == 2:
|
|
119
|
-
interval_unit = every[0]
|
|
120
|
-
interval_value = every[1]
|
|
121
|
-
if "day" in interval_unit:
|
|
122
|
-
if interval_unit.lower() in weekdays:
|
|
123
|
-
trigger_steps_count += 1
|
|
124
|
-
self.every_weekday_schedule(interval_unit.lower()).at(interval_value).do(
|
|
125
|
-
workflow.start, start_step_id=step.id
|
|
126
|
-
)
|
|
127
|
-
else:
|
|
128
|
-
trigger_steps_count += 1
|
|
129
|
-
schedule.every().day.at(interval_value).do(workflow.start, start_step_id=step.id)
|
|
130
|
-
else:
|
|
131
|
-
logging.error(f"Invalid time trigger interval {every_str}")
|
|
132
|
-
|
|
133
|
-
else: # "5 minutes"
|
|
134
|
-
every = every_str.split(" ")
|
|
135
|
-
if len(every) == 2:
|
|
136
|
-
interval_unit = every[1]
|
|
137
|
-
interval_value = every[0]
|
|
138
|
-
trigger_steps_count += 1
|
|
139
|
-
if "minutes" in interval_unit: # "day at 10:30:00" , "5 minutes"
|
|
140
|
-
schedule.every(int(interval_value)).minutes.do(workflow.start, start_step_id=step.id)
|
|
141
|
-
elif "hours" in interval_unit:
|
|
142
|
-
schedule.every(int(interval_value)).hours.do(workflow.start, start_step_id=step.id)
|
|
143
|
-
elif "days" in interval_unit:
|
|
144
|
-
schedule.every(int(interval_value)).days.do(workflow.start, start_step_id=step.id)
|
|
145
|
-
elif "seconds" in interval_unit:
|
|
146
|
-
schedule.every(int(interval_value)).seconds.do(workflow.start, start_step_id=step.id)
|
|
147
|
-
else:
|
|
148
|
-
logging.error(f"Invalid time trigger interval {every_str}")
|
|
149
|
-
trigger_steps_count -= 1
|
|
150
|
-
else:
|
|
151
|
-
logging.error(f"Invalid time trigger interval {every_str}")
|
|
152
|
-
|
|
153
|
-
if trigger_steps_count > 0 and not self.is_running:
|
|
154
|
-
self._start_scheduler_main_loop()
|
|
155
|
-
logging.info("ALl Time trigger scheduler started")
|
|
156
|
-
else:
|
|
157
|
-
logging.info("No Time trigger scheduler started")
|
|
158
|
-
|
|
159
|
-
def reload_all_triggers(self):
|
|
160
|
-
"""Reloads all triggers
|
|
161
|
-
|
|
162
|
-
Parameters
|
|
163
|
-
----------
|
|
164
|
-
"""
|
|
165
|
-
logging.info("Reloading all triggers")
|
|
166
|
-
schedule.clear()
|
|
167
|
-
self.is_running = False
|
|
168
|
-
time.sleep(1)
|
|
169
|
-
self.start_time_schedulers()
|
cognite/neat/_workflows/utils.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import hashlib
|
|
2
|
-
import uuid
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def get_iso8601_timestamp_now_unaware():
|
|
8
|
-
return datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def get_file_hash(file_path: Path) -> str:
|
|
12
|
-
if isinstance(file_path, str):
|
|
13
|
-
file_path = Path(file_path)
|
|
14
|
-
return hashlib.md5(file_path.read_bytes()).hexdigest()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def generate_run_id() -> str:
|
|
18
|
-
# Generate a random run guid
|
|
19
|
-
return str(uuid.uuid4())
|