cognite-neat 0.103.1__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.

Files changed (175) hide show
  1. cognite/neat/_client/_api/data_modeling_loaders.py +83 -23
  2. cognite/neat/_client/_api/schema.py +2 -1
  3. cognite/neat/_client/data_classes/neat_sequence.py +261 -0
  4. cognite/neat/_client/data_classes/schema.py +5 -1
  5. cognite/neat/_client/testing.py +33 -0
  6. cognite/neat/_constants.py +56 -0
  7. cognite/neat/_graph/extractors/_classic_cdf/_base.py +6 -5
  8. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +225 -11
  9. cognite/neat/_graph/extractors/_mock_graph_generator.py +2 -2
  10. cognite/neat/_graph/loaders/_rdf2dms.py +13 -2
  11. cognite/neat/_graph/transformers/__init__.py +3 -1
  12. cognite/neat/_graph/transformers/_base.py +109 -1
  13. cognite/neat/_graph/transformers/_classic_cdf.py +6 -1
  14. cognite/neat/_graph/transformers/_prune_graph.py +103 -47
  15. cognite/neat/_graph/transformers/_rdfpath.py +41 -17
  16. cognite/neat/_graph/transformers/_value_type.py +188 -151
  17. cognite/neat/_issues/__init__.py +0 -2
  18. cognite/neat/_issues/_base.py +54 -43
  19. cognite/neat/_issues/warnings/__init__.py +4 -1
  20. cognite/neat/_issues/warnings/_general.py +7 -0
  21. cognite/neat/_issues/warnings/_resources.py +12 -1
  22. cognite/neat/_rules/_shared.py +18 -34
  23. cognite/neat/_rules/exporters/_base.py +28 -2
  24. cognite/neat/_rules/exporters/_rules2dms.py +39 -1
  25. cognite/neat/_rules/exporters/_rules2excel.py +13 -2
  26. cognite/neat/_rules/exporters/_rules2instance_template.py +4 -0
  27. cognite/neat/_rules/exporters/_rules2ontology.py +13 -1
  28. cognite/neat/_rules/exporters/_rules2yaml.py +4 -0
  29. cognite/neat/_rules/importers/_base.py +9 -0
  30. cognite/neat/_rules/importers/_dms2rules.py +80 -57
  31. cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +5 -2
  32. cognite/neat/_rules/importers/_rdf/_base.py +10 -8
  33. cognite/neat/_rules/importers/_rdf/_imf2rules.py +4 -0
  34. cognite/neat/_rules/importers/_rdf/_inference2rules.py +7 -0
  35. cognite/neat/_rules/importers/_rdf/_owl2rules.py +4 -0
  36. cognite/neat/_rules/importers/_spreadsheet2rules.py +17 -8
  37. cognite/neat/_rules/importers/_yaml2rules.py +21 -7
  38. cognite/neat/_rules/models/_base_input.py +1 -1
  39. cognite/neat/_rules/models/_base_rules.py +9 -1
  40. cognite/neat/_rules/models/dms/_rules.py +4 -0
  41. cognite/neat/_rules/models/dms/_rules_input.py +9 -0
  42. cognite/neat/_rules/models/entities/_wrapped.py +10 -5
  43. cognite/neat/_rules/models/information/_rules.py +4 -0
  44. cognite/neat/_rules/models/information/_rules_input.py +9 -0
  45. cognite/neat/_rules/models/mapping/_classic2core.py +2 -5
  46. cognite/neat/_rules/models/mapping/_classic2core.yaml +239 -38
  47. cognite/neat/_rules/transformers/__init__.py +13 -6
  48. cognite/neat/_rules/transformers/_base.py +41 -65
  49. cognite/neat/_rules/transformers/_converters.py +404 -234
  50. cognite/neat/_rules/transformers/_mapping.py +93 -72
  51. cognite/neat/_rules/transformers/_verification.py +50 -38
  52. cognite/neat/_session/_base.py +32 -121
  53. cognite/neat/_session/_inspect.py +5 -3
  54. cognite/neat/_session/_mapping.py +17 -105
  55. cognite/neat/_session/_prepare.py +138 -268
  56. cognite/neat/_session/_read.py +39 -195
  57. cognite/neat/_session/_set.py +6 -30
  58. cognite/neat/_session/_show.py +40 -21
  59. cognite/neat/_session/_state.py +49 -107
  60. cognite/neat/_session/_to.py +44 -33
  61. cognite/neat/_shared.py +23 -2
  62. cognite/neat/_store/_provenance.py +3 -82
  63. cognite/neat/_store/_rules_store.py +368 -10
  64. cognite/neat/_store/exceptions.py +23 -0
  65. cognite/neat/_utils/graph_transformations_report.py +36 -0
  66. cognite/neat/_utils/rdf_.py +8 -0
  67. cognite/neat/_utils/reader/_base.py +27 -0
  68. cognite/neat/_utils/spreadsheet.py +5 -4
  69. cognite/neat/_version.py +1 -1
  70. {cognite_neat-0.103.1.dist-info → cognite_neat-0.105.0.dist-info}/METADATA +3 -2
  71. cognite_neat-0.105.0.dist-info/RECORD +179 -0
  72. {cognite_neat-0.103.1.dist-info → cognite_neat-0.105.0.dist-info}/WHEEL +1 -1
  73. cognite/neat/_app/api/__init__.py +0 -0
  74. cognite/neat/_app/api/asgi/metrics.py +0 -4
  75. cognite/neat/_app/api/configuration.py +0 -98
  76. cognite/neat/_app/api/context_manager/__init__.py +0 -3
  77. cognite/neat/_app/api/context_manager/manager.py +0 -16
  78. cognite/neat/_app/api/data_classes/__init__.py +0 -0
  79. cognite/neat/_app/api/data_classes/rest.py +0 -59
  80. cognite/neat/_app/api/explorer.py +0 -66
  81. cognite/neat/_app/api/routers/configuration.py +0 -25
  82. cognite/neat/_app/api/routers/crud.py +0 -102
  83. cognite/neat/_app/api/routers/metrics.py +0 -10
  84. cognite/neat/_app/api/routers/workflows.py +0 -224
  85. cognite/neat/_app/api/utils/__init__.py +0 -0
  86. cognite/neat/_app/api/utils/data_mapping.py +0 -17
  87. cognite/neat/_app/api/utils/logging.py +0 -26
  88. cognite/neat/_app/api/utils/query_templates.py +0 -92
  89. cognite/neat/_app/main.py +0 -17
  90. cognite/neat/_app/monitoring/__init__.py +0 -0
  91. cognite/neat/_app/monitoring/metrics.py +0 -69
  92. cognite/neat/_app/ui/index.html +0 -1
  93. cognite/neat/_app/ui/neat-app/.gitignore +0 -23
  94. cognite/neat/_app/ui/neat-app/README.md +0 -70
  95. cognite/neat/_app/ui/neat-app/build/asset-manifest.json +0 -14
  96. cognite/neat/_app/ui/neat-app/build/favicon.ico +0 -0
  97. cognite/neat/_app/ui/neat-app/build/img/architect-icon.svg +0 -116
  98. cognite/neat/_app/ui/neat-app/build/img/developer-icon.svg +0 -112
  99. cognite/neat/_app/ui/neat-app/build/img/sme-icon.svg +0 -34
  100. cognite/neat/_app/ui/neat-app/build/index.html +0 -1
  101. cognite/neat/_app/ui/neat-app/build/logo192.png +0 -0
  102. cognite/neat/_app/ui/neat-app/build/manifest.json +0 -25
  103. cognite/neat/_app/ui/neat-app/build/robots.txt +0 -3
  104. cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css +0 -2
  105. cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css.map +0 -1
  106. cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js +0 -3
  107. cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.LICENSE.txt +0 -88
  108. cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.map +0 -1
  109. cognite/neat/_app/ui/neat-app/build/static/media/logo.8093b84df9ed36a174c629d6fe0b730d.svg +0 -1
  110. cognite/neat/_app/ui/neat-app/package-lock.json +0 -18306
  111. cognite/neat/_app/ui/neat-app/package.json +0 -62
  112. cognite/neat/_app/ui/neat-app/public/favicon.ico +0 -0
  113. cognite/neat/_app/ui/neat-app/public/img/architect-icon.svg +0 -116
  114. cognite/neat/_app/ui/neat-app/public/img/developer-icon.svg +0 -112
  115. cognite/neat/_app/ui/neat-app/public/img/sme-icon.svg +0 -34
  116. cognite/neat/_app/ui/neat-app/public/index.html +0 -43
  117. cognite/neat/_app/ui/neat-app/public/logo192.png +0 -0
  118. cognite/neat/_app/ui/neat-app/public/manifest.json +0 -25
  119. cognite/neat/_app/ui/neat-app/public/robots.txt +0 -3
  120. cognite/neat/_app/ui/neat-app/src/App.css +0 -38
  121. cognite/neat/_app/ui/neat-app/src/App.js +0 -17
  122. cognite/neat/_app/ui/neat-app/src/App.test.js +0 -8
  123. cognite/neat/_app/ui/neat-app/src/MainContainer.tsx +0 -70
  124. cognite/neat/_app/ui/neat-app/src/components/JsonViewer.tsx +0 -43
  125. cognite/neat/_app/ui/neat-app/src/components/LocalUploader.tsx +0 -124
  126. cognite/neat/_app/ui/neat-app/src/components/OverviewComponentEditorDialog.tsx +0 -63
  127. cognite/neat/_app/ui/neat-app/src/components/StepEditorDialog.tsx +0 -511
  128. cognite/neat/_app/ui/neat-app/src/components/TabPanel.tsx +0 -36
  129. cognite/neat/_app/ui/neat-app/src/components/Utils.tsx +0 -56
  130. cognite/neat/_app/ui/neat-app/src/components/WorkflowDeleteDialog.tsx +0 -60
  131. cognite/neat/_app/ui/neat-app/src/components/WorkflowExecutionReport.tsx +0 -112
  132. cognite/neat/_app/ui/neat-app/src/components/WorkflowImportExportDialog.tsx +0 -67
  133. cognite/neat/_app/ui/neat-app/src/components/WorkflowMetadataDialog.tsx +0 -79
  134. cognite/neat/_app/ui/neat-app/src/index.css +0 -13
  135. cognite/neat/_app/ui/neat-app/src/index.js +0 -13
  136. cognite/neat/_app/ui/neat-app/src/logo.svg +0 -1
  137. cognite/neat/_app/ui/neat-app/src/reportWebVitals.js +0 -13
  138. cognite/neat/_app/ui/neat-app/src/setupTests.js +0 -5
  139. cognite/neat/_app/ui/neat-app/src/types/WorkflowTypes.ts +0 -388
  140. cognite/neat/_app/ui/neat-app/src/views/AboutView.tsx +0 -61
  141. cognite/neat/_app/ui/neat-app/src/views/ConfigView.tsx +0 -184
  142. cognite/neat/_app/ui/neat-app/src/views/GlobalConfigView.tsx +0 -180
  143. cognite/neat/_app/ui/neat-app/src/views/WorkflowView.tsx +0 -570
  144. cognite/neat/_app/ui/neat-app/tsconfig.json +0 -27
  145. cognite/neat/_rules/transformers/_pipelines.py +0 -70
  146. cognite/neat/_workflows/__init__.py +0 -17
  147. cognite/neat/_workflows/base.py +0 -590
  148. cognite/neat/_workflows/cdf_store.py +0 -393
  149. cognite/neat/_workflows/examples/Export_DMS/workflow.yaml +0 -89
  150. cognite/neat/_workflows/examples/Export_Semantic_Data_Model/workflow.yaml +0 -66
  151. cognite/neat/_workflows/examples/Import_DMS/workflow.yaml +0 -65
  152. cognite/neat/_workflows/examples/Validate_Rules/workflow.yaml +0 -67
  153. cognite/neat/_workflows/examples/Validate_Solution_Model/workflow.yaml +0 -64
  154. cognite/neat/_workflows/manager.py +0 -292
  155. cognite/neat/_workflows/model.py +0 -203
  156. cognite/neat/_workflows/steps/__init__.py +0 -0
  157. cognite/neat/_workflows/steps/data_contracts.py +0 -109
  158. cognite/neat/_workflows/steps/lib/__init__.py +0 -0
  159. cognite/neat/_workflows/steps/lib/current/__init__.py +0 -6
  160. cognite/neat/_workflows/steps/lib/current/graph_extractor.py +0 -100
  161. cognite/neat/_workflows/steps/lib/current/graph_loader.py +0 -51
  162. cognite/neat/_workflows/steps/lib/current/graph_store.py +0 -48
  163. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +0 -537
  164. cognite/neat/_workflows/steps/lib/current/rules_importer.py +0 -398
  165. cognite/neat/_workflows/steps/lib/current/rules_validator.py +0 -106
  166. cognite/neat/_workflows/steps/lib/io/__init__.py +0 -1
  167. cognite/neat/_workflows/steps/lib/io/io_steps.py +0 -393
  168. cognite/neat/_workflows/steps/step_model.py +0 -79
  169. cognite/neat/_workflows/steps_registry.py +0 -218
  170. cognite/neat/_workflows/tasks.py +0 -18
  171. cognite/neat/_workflows/triggers.py +0 -169
  172. cognite/neat/_workflows/utils.py +0 -19
  173. cognite_neat-0.103.1.dist-info/RECORD +0 -275
  174. {cognite_neat-0.103.1.dist-info → cognite_neat-0.105.0.dist-info}/LICENSE +0 -0
  175. {cognite_neat-0.103.1.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
@@ -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()
@@ -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())