ddeutil-workflow 0.0.73__py3-none-any.whl → 0.0.75__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.
- ddeutil/workflow/__about__.py +1 -1
- ddeutil/workflow/__cron.py +20 -12
- ddeutil/workflow/__init__.py +119 -10
- ddeutil/workflow/__types.py +53 -41
- ddeutil/workflow/api/__init__.py +74 -3
- ddeutil/workflow/api/routes/job.py +15 -29
- ddeutil/workflow/api/routes/logs.py +9 -9
- ddeutil/workflow/api/routes/workflows.py +3 -3
- ddeutil/workflow/audits.py +70 -55
- ddeutil/workflow/cli.py +1 -15
- ddeutil/workflow/conf.py +71 -26
- ddeutil/workflow/errors.py +86 -19
- ddeutil/workflow/event.py +268 -169
- ddeutil/workflow/job.py +331 -192
- ddeutil/workflow/params.py +43 -11
- ddeutil/workflow/result.py +96 -70
- ddeutil/workflow/reusables.py +56 -6
- ddeutil/workflow/stages.py +1059 -572
- ddeutil/workflow/traces.py +205 -124
- ddeutil/workflow/utils.py +58 -19
- ddeutil/workflow/workflow.py +435 -296
- {ddeutil_workflow-0.0.73.dist-info → ddeutil_workflow-0.0.75.dist-info}/METADATA +27 -17
- ddeutil_workflow-0.0.75.dist-info/RECORD +30 -0
- ddeutil_workflow-0.0.73.dist-info/RECORD +0 -30
- {ddeutil_workflow-0.0.73.dist-info → ddeutil_workflow-0.0.75.dist-info}/WHEEL +0 -0
- {ddeutil_workflow-0.0.73.dist-info → ddeutil_workflow-0.0.75.dist-info}/entry_points.txt +0 -0
- {ddeutil_workflow-0.0.73.dist-info → ddeutil_workflow-0.0.75.dist-info}/licenses/LICENSE +0 -0
- {ddeutil_workflow-0.0.73.dist-info → ddeutil_workflow-0.0.75.dist-info}/top_level.txt +0 -0
ddeutil/workflow/__about__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__: str = "0.0.
|
1
|
+
__version__: str = "0.0.75"
|
ddeutil/workflow/__cron.py
CHANGED
@@ -38,8 +38,11 @@ class YearReachLimit(Exception):
|
|
38
38
|
def str2cron(value: str) -> str: # pragma: no cov
|
39
39
|
"""Convert Special String with the @ prefix to Crontab value.
|
40
40
|
|
41
|
-
:
|
42
|
-
|
41
|
+
Args:
|
42
|
+
value: A string value that want to convert to cron value.
|
43
|
+
|
44
|
+
Returns:
|
45
|
+
str: The converted cron expression.
|
43
46
|
|
44
47
|
Table:
|
45
48
|
|
@@ -165,9 +168,10 @@ CRON_UNITS_YEAR: Units = CRON_UNITS + (
|
|
165
168
|
class CronPart:
|
166
169
|
"""Part of Cron object that represent a collection of positive integers.
|
167
170
|
|
168
|
-
:
|
169
|
-
|
170
|
-
|
171
|
+
Args:
|
172
|
+
unit: A Unit dataclass object.
|
173
|
+
values: A crontab values that want to validate
|
174
|
+
options: A Options dataclass object.
|
171
175
|
"""
|
172
176
|
|
173
177
|
__slots__: tuple[str, ...] = (
|
@@ -288,7 +292,11 @@ class CronPart:
|
|
288
292
|
"""Parses a string as a range of positive integers. The string should
|
289
293
|
include only `-` and `,` special strings.
|
290
294
|
|
291
|
-
:
|
295
|
+
Args:
|
296
|
+
value: A string value that want to parse
|
297
|
+
|
298
|
+
Returns:
|
299
|
+
tuple[int, ...]: Parsed range of integers.
|
292
300
|
|
293
301
|
TODO: support for `L`, `W`, and `#`
|
294
302
|
---
|
@@ -334,8 +342,6 @@ class CronPart:
|
|
334
342
|
- 15 10 ? * 6L 2002-2005
|
335
343
|
Run at 10:15am UTC on the last Friday of each month during the
|
336
344
|
years 2002 to 2005
|
337
|
-
|
338
|
-
:rtype: tuple[int, ...]
|
339
345
|
"""
|
340
346
|
interval_list: list[list[int]] = []
|
341
347
|
# NOTE: Start replace alternative like JAN to FEB or MON to SUN.
|
@@ -787,10 +793,11 @@ class CronRunner:
|
|
787
793
|
"Invalid type of `tz` parameter, it should be str or "
|
788
794
|
"ZoneInfo instance."
|
789
795
|
)
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
796
|
+
else:
|
797
|
+
try:
|
798
|
+
self.tz = ZoneInfo(tz)
|
799
|
+
except ZoneInfoNotFoundError as err:
|
800
|
+
raise ValueError(f"Invalid timezone: {tz}") from err
|
794
801
|
|
795
802
|
# NOTE: Prepare date
|
796
803
|
if date:
|
@@ -801,6 +808,7 @@ class CronRunner:
|
|
801
808
|
if tz is not None:
|
802
809
|
self.date: datetime = date.astimezone(self.tz)
|
803
810
|
else:
|
811
|
+
self.tz = date.tzinfo
|
804
812
|
self.date: datetime = date
|
805
813
|
else:
|
806
814
|
self.date: datetime = datetime.now(tz=self.tz)
|
ddeutil/workflow/__init__.py
CHANGED
@@ -3,19 +3,108 @@
|
|
3
3
|
# Licensed under the MIT License. See LICENSE in the project root for
|
4
4
|
# license information.
|
5
5
|
# ------------------------------------------------------------------------------
|
6
|
-
|
6
|
+
"""DDE Workflow - Lightweight Workflow Orchestration Package.
|
7
|
+
|
8
|
+
This package provides a comprehensive workflow orchestration system with YAML template
|
9
|
+
support. It enables developers to create, manage, and execute complex workflows with
|
10
|
+
minimal configuration.
|
11
|
+
|
12
|
+
Key Features:
|
13
|
+
- YAML-based workflow configuration
|
14
|
+
- Job and stage execution management
|
15
|
+
- Scheduling with cron-like syntax
|
16
|
+
- Parallel and sequential execution support
|
17
|
+
- Comprehensive error handling and logging
|
18
|
+
- Extensible stage types (Bash, Python, Docker, etc.)
|
19
|
+
- Matrix strategy for parameterized workflows
|
20
|
+
- Audit and tracing capabilities
|
21
|
+
|
22
|
+
Main Classes:
|
23
|
+
Workflow: Core workflow orchestration class
|
24
|
+
Job: Execution unit containing stages
|
25
|
+
Stage: Individual task execution unit
|
26
|
+
CronJob: Scheduled workflow execution
|
27
|
+
Audit: Execution tracking and logging
|
28
|
+
Result: Execution status and output management
|
29
|
+
|
30
|
+
Example:
|
31
|
+
Basic workflow usage:
|
32
|
+
|
33
|
+
```python
|
34
|
+
from ddeutil.workflow import Workflow
|
35
|
+
|
36
|
+
# Load workflow from configuration
|
37
|
+
workflow = Workflow.from_conf('my-workflow')
|
38
|
+
|
39
|
+
# Execute with parameters
|
40
|
+
result = workflow.execute({'param1': 'value1'})
|
41
|
+
|
42
|
+
if result.status == 'SUCCESS':
|
43
|
+
print("Workflow completed successfully")
|
44
|
+
```
|
45
|
+
|
46
|
+
Note:
|
47
|
+
This package requires Python 3.9+ and supports both synchronous and
|
48
|
+
asynchronous execution patterns.
|
49
|
+
"""
|
50
|
+
from .__cron import CronRunner
|
7
51
|
from .__types import DictData, DictStr, Matrix, Re, TupleStr
|
8
52
|
from .audits import (
|
9
53
|
Audit,
|
10
|
-
AuditModel,
|
11
54
|
FileAudit,
|
12
|
-
|
55
|
+
get_audit_model,
|
13
56
|
)
|
14
57
|
from .conf import *
|
15
|
-
from .errors import
|
16
|
-
|
17
|
-
|
18
|
-
|
58
|
+
from .errors import (
|
59
|
+
BaseError,
|
60
|
+
JobCancelError,
|
61
|
+
JobError,
|
62
|
+
JobSkipError,
|
63
|
+
ResultError,
|
64
|
+
StageCancelError,
|
65
|
+
StageError,
|
66
|
+
StageSkipError,
|
67
|
+
UtilError,
|
68
|
+
WorkflowCancelError,
|
69
|
+
WorkflowError,
|
70
|
+
WorkflowTimeoutError,
|
71
|
+
to_dict,
|
72
|
+
)
|
73
|
+
from .event import (
|
74
|
+
Cron,
|
75
|
+
CronJob,
|
76
|
+
CronJobYear,
|
77
|
+
Crontab,
|
78
|
+
CrontabValue,
|
79
|
+
CrontabYear,
|
80
|
+
Event,
|
81
|
+
Interval,
|
82
|
+
)
|
83
|
+
from .job import (
|
84
|
+
Job,
|
85
|
+
OnAzBatch,
|
86
|
+
OnDocker,
|
87
|
+
OnLocal,
|
88
|
+
OnSelfHosted,
|
89
|
+
Rule,
|
90
|
+
RunsOnModel,
|
91
|
+
Strategy,
|
92
|
+
docker_execution,
|
93
|
+
local_execute,
|
94
|
+
local_execute_strategy,
|
95
|
+
self_hosted_execute,
|
96
|
+
)
|
97
|
+
from .params import (
|
98
|
+
ArrayParam,
|
99
|
+
DateParam,
|
100
|
+
DatetimeParam,
|
101
|
+
DecimalParam,
|
102
|
+
FloatParam,
|
103
|
+
IntParam,
|
104
|
+
MapParam,
|
105
|
+
Param,
|
106
|
+
StrParam,
|
107
|
+
)
|
19
108
|
from .result import (
|
20
109
|
CANCEL,
|
21
110
|
FAILED,
|
@@ -26,15 +115,35 @@ from .result import (
|
|
26
115
|
Status,
|
27
116
|
)
|
28
117
|
from .reusables import *
|
29
|
-
from .stages import
|
118
|
+
from .stages import (
|
119
|
+
BashStage,
|
120
|
+
CallStage,
|
121
|
+
CaseStage,
|
122
|
+
DockerStage,
|
123
|
+
EmptyStage,
|
124
|
+
ForEachStage,
|
125
|
+
ParallelStage,
|
126
|
+
PyStage,
|
127
|
+
RaiseStage,
|
128
|
+
Stage,
|
129
|
+
TriggerStage,
|
130
|
+
UntilStage,
|
131
|
+
VirtualPyStage,
|
132
|
+
)
|
30
133
|
from .traces import (
|
31
134
|
ConsoleTrace,
|
32
135
|
FileTrace,
|
33
136
|
Trace,
|
34
137
|
TraceData,
|
35
138
|
TraceMeta,
|
36
|
-
TraceModel,
|
37
139
|
get_trace,
|
38
140
|
)
|
39
141
|
from .utils import *
|
40
|
-
from .workflow import
|
142
|
+
from .workflow import (
|
143
|
+
EVENT,
|
144
|
+
FORCE,
|
145
|
+
NORMAL,
|
146
|
+
RERUN,
|
147
|
+
ReleaseType,
|
148
|
+
Workflow,
|
149
|
+
)
|
ddeutil/workflow/__types.py
CHANGED
@@ -27,6 +27,47 @@ DictData = dict[str, Any]
|
|
27
27
|
DictStr = dict[str, str]
|
28
28
|
Matrix = dict[str, Union[list[str], list[int]]]
|
29
29
|
|
30
|
+
# Pre-compile regex patterns for better performance
|
31
|
+
_RE_CALLER_PATTERN = r"""
|
32
|
+
\$ # start with $
|
33
|
+
{{ # value open with {{
|
34
|
+
\s* # whitespace or not
|
35
|
+
(?P<caller>
|
36
|
+
(?P<caller_prefix>(?:[a-zA-Z_-]+\??\.)*)
|
37
|
+
(?P<caller_last>[a-zA-Z0-9_\-.'\"(\)[\]{}]+\??)
|
38
|
+
)
|
39
|
+
\s* # whitespace or not
|
40
|
+
(?P<post_filters>
|
41
|
+
(?:
|
42
|
+
\|\s*
|
43
|
+
(?:
|
44
|
+
[a-zA-Z0-9_]{3,}
|
45
|
+
[a-zA-Z0-9_.,-\\%\s'\"[\]()\{}]*
|
46
|
+
)\s*
|
47
|
+
)*
|
48
|
+
)
|
49
|
+
}} # value close with }}
|
50
|
+
"""
|
51
|
+
|
52
|
+
_RE_TASK_FMT_PATTERN = r"""
|
53
|
+
^ # start task format
|
54
|
+
(?P<path>[^/@]+)
|
55
|
+
/ # start get function with /
|
56
|
+
(?P<func>[^@]+)
|
57
|
+
@ # start tag with @
|
58
|
+
(?P<tag>.+)
|
59
|
+
$ # end task format
|
60
|
+
"""
|
61
|
+
|
62
|
+
# Compile patterns at module level for better performance
|
63
|
+
RE_CALLER: Pattern = re.compile(
|
64
|
+
_RE_CALLER_PATTERN, MULTILINE | IGNORECASE | UNICODE | VERBOSE
|
65
|
+
)
|
66
|
+
|
67
|
+
RE_TASK_FMT: Pattern = re.compile(
|
68
|
+
_RE_TASK_FMT_PATTERN, MULTILINE | IGNORECASE | UNICODE | VERBOSE
|
69
|
+
)
|
70
|
+
|
30
71
|
|
31
72
|
class Context(TypedDict):
|
32
73
|
"""TypeDict support the Context."""
|
@@ -51,10 +92,12 @@ class CallerRe:
|
|
51
92
|
def from_regex(cls, match: Match[str]) -> Self:
|
52
93
|
"""Class construct from matching result.
|
53
94
|
|
54
|
-
:
|
55
|
-
|
95
|
+
Args:
|
96
|
+
match: A match string object for contract this Caller regex data
|
97
|
+
class.
|
56
98
|
|
57
|
-
:
|
99
|
+
Returns:
|
100
|
+
Self: The constructed CallerRe instance from regex match.
|
58
101
|
"""
|
59
102
|
return cls(full=match.group(0), **match.groupdict())
|
60
103
|
|
@@ -79,29 +122,7 @@ class Re:
|
|
79
122
|
# - ${{ params.datetime | fmt('%Y-%m-%d') }}
|
80
123
|
# - ${{ params.source?.schema }}
|
81
124
|
#
|
82
|
-
|
83
|
-
\$ # start with $
|
84
|
-
{{ # value open with {{
|
85
|
-
\s* # whitespace or not
|
86
|
-
(?P<caller>
|
87
|
-
(?P<caller_prefix>(?:[a-zA-Z_-]+\??\.)*)
|
88
|
-
(?P<caller_last>[a-zA-Z0-9_\-.'\"(\)[\]{}]+\??)
|
89
|
-
)
|
90
|
-
\s* # whitespace or not
|
91
|
-
(?P<post_filters>
|
92
|
-
(?:
|
93
|
-
\|\s*
|
94
|
-
(?:
|
95
|
-
[a-zA-Z0-9_]{3,}
|
96
|
-
[a-zA-Z0-9_.,-\\%\s'\"[\]()\{}]*
|
97
|
-
)\s*
|
98
|
-
)*
|
99
|
-
)
|
100
|
-
}} # value close with }}
|
101
|
-
"""
|
102
|
-
RE_CALLER: Pattern = re.compile(
|
103
|
-
__re_caller, MULTILINE | IGNORECASE | UNICODE | VERBOSE
|
104
|
-
)
|
125
|
+
RE_CALLER: Pattern = RE_CALLER
|
105
126
|
|
106
127
|
# NOTE:
|
107
128
|
# Regular expression:
|
@@ -111,28 +132,19 @@ class Re:
|
|
111
132
|
# Examples:
|
112
133
|
# - tasks/function@dummy
|
113
134
|
#
|
114
|
-
|
115
|
-
^ # start task format
|
116
|
-
(?P<path>[^/@]+)
|
117
|
-
/ # start get function with /
|
118
|
-
(?P<func>[^@]+)
|
119
|
-
@ # start tag with @
|
120
|
-
(?P<tag>.+)
|
121
|
-
$ # end task format
|
122
|
-
"""
|
123
|
-
RE_TASK_FMT: Pattern = re.compile(
|
124
|
-
__re_task_fmt, MULTILINE | IGNORECASE | UNICODE | VERBOSE
|
125
|
-
)
|
135
|
+
RE_TASK_FMT: Pattern = RE_TASK_FMT
|
126
136
|
|
127
137
|
@classmethod
|
128
138
|
def finditer_caller(cls, value: str) -> Iterator[CallerRe]:
|
129
139
|
"""Generate CallerRe object that create from matching object that
|
130
140
|
extract with re.finditer function.
|
131
141
|
|
132
|
-
:
|
133
|
-
|
142
|
+
Args:
|
143
|
+
value: A string value that want to finditer with the caller
|
144
|
+
regular expression.
|
134
145
|
|
135
|
-
:
|
146
|
+
Yields:
|
147
|
+
CallerRe: CallerRe objects created from regex matches.
|
136
148
|
"""
|
137
149
|
for found in cls.RE_CALLER.finditer(value):
|
138
150
|
yield CallerRe.from_regex(found)
|
ddeutil/workflow/api/__init__.py
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
+
"""FastAPI Web Application for Workflow Management.
|
2
|
+
|
3
|
+
This module provides a RESTful API interface for workflow orchestration using
|
4
|
+
FastAPI. It enables remote workflow management, execution monitoring, and
|
5
|
+
provides endpoints for workflow operations.
|
6
|
+
|
7
|
+
The API supports:
|
8
|
+
- Workflow execution and management
|
9
|
+
- Job status monitoring
|
10
|
+
- Log streaming and access
|
11
|
+
- Result retrieval and analysis
|
12
|
+
|
13
|
+
Example:
|
14
|
+
```python
|
15
|
+
from ddeutil.workflow.api import app
|
16
|
+
|
17
|
+
# Run the API server
|
18
|
+
import uvicorn
|
19
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
20
|
+
```
|
21
|
+
|
22
|
+
Routes:
|
23
|
+
- /workflows: Workflow management endpoints
|
24
|
+
- /jobs: Job execution and monitoring
|
25
|
+
- /logs: Log access and streaming
|
26
|
+
"""
|
27
|
+
|
1
28
|
# ------------------------------------------------------------------------------
|
2
29
|
# Copyright (c) 2022 Korawich Anuttra. All rights reserved.
|
3
30
|
# Licensed under the MIT License. See LICENSE in the project root for
|
@@ -28,7 +55,17 @@ logger = logging.getLogger("uvicorn.error")
|
|
28
55
|
|
29
56
|
@contextlib.asynccontextmanager
|
30
57
|
async def lifespan(_: FastAPI) -> AsyncIterator[dict[str, list]]:
|
31
|
-
"""
|
58
|
+
"""FastAPI application lifespan management.
|
59
|
+
|
60
|
+
Manages the startup and shutdown lifecycle of the FastAPI application.
|
61
|
+
Currently yields an empty dictionary for future extension.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
_: FastAPI application instance (unused)
|
65
|
+
|
66
|
+
Yields:
|
67
|
+
dict: Empty dictionary for future lifespan data
|
68
|
+
"""
|
32
69
|
yield {}
|
33
70
|
|
34
71
|
|
@@ -59,7 +96,20 @@ app.add_middleware(
|
|
59
96
|
|
60
97
|
@app.get(path="/", response_class=UJSONResponse)
|
61
98
|
async def health() -> UJSONResponse:
|
62
|
-
"""
|
99
|
+
"""Health check endpoint for API status monitoring.
|
100
|
+
|
101
|
+
Provides a simple health check endpoint to verify the API is running
|
102
|
+
and responding correctly. Returns a JSON response with health status.
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
UJSONResponse: JSON response confirming healthy API status
|
106
|
+
|
107
|
+
Example:
|
108
|
+
```bash
|
109
|
+
curl http://localhost:8000/
|
110
|
+
# Returns: {"message": "Workflow already start up with healthy status."}
|
111
|
+
```
|
112
|
+
"""
|
63
113
|
logger.info("[API]: Workflow API Application already running ...")
|
64
114
|
return UJSONResponse(
|
65
115
|
content={"message": "Workflow already start up with healthy status."},
|
@@ -78,7 +128,28 @@ async def validation_exception_handler(
|
|
78
128
|
request: Request,
|
79
129
|
exc: RequestValidationError,
|
80
130
|
) -> UJSONResponse:
|
81
|
-
"""
|
131
|
+
"""Handle request validation errors from Pydantic models.
|
132
|
+
|
133
|
+
Provides standardized error responses for request validation failures,
|
134
|
+
including detailed error information for debugging and client feedback.
|
135
|
+
|
136
|
+
Args:
|
137
|
+
request: The FastAPI request object (unused)
|
138
|
+
exc: The validation exception containing error details
|
139
|
+
|
140
|
+
Returns:
|
141
|
+
UJSONResponse: Standardized error response with validation details
|
142
|
+
|
143
|
+
Example:
|
144
|
+
When a request fails validation:
|
145
|
+
```json
|
146
|
+
{
|
147
|
+
"message": "Body does not parsing with model.",
|
148
|
+
"detail": [...],
|
149
|
+
"body": {...}
|
150
|
+
}
|
151
|
+
```
|
152
|
+
"""
|
82
153
|
_ = request
|
83
154
|
return UJSONResponse(
|
84
155
|
status_code=st.HTTP_422_UNPROCESSABLE_ENTITY,
|
@@ -8,72 +8,58 @@ from __future__ import annotations
|
|
8
8
|
import logging
|
9
9
|
from typing import Any, Optional
|
10
10
|
|
11
|
-
from fastapi import APIRouter
|
11
|
+
from fastapi import APIRouter, Body
|
12
12
|
from fastapi import status as st
|
13
13
|
from fastapi.responses import UJSONResponse
|
14
|
-
from pydantic import BaseModel, Field
|
15
14
|
|
16
15
|
from ...__types import DictData
|
17
16
|
from ...errors import JobError
|
18
17
|
from ...job import Job
|
19
|
-
from ...
|
18
|
+
from ...traces import Trace, get_trace
|
19
|
+
from ...utils import gen_id
|
20
20
|
|
21
21
|
logger = logging.getLogger("uvicorn.error")
|
22
22
|
router = APIRouter(prefix="/job", tags=["job"])
|
23
23
|
|
24
24
|
|
25
|
-
class ResultCreate(BaseModel):
|
26
|
-
"""Create Result model for receive running IDs to create the Result
|
27
|
-
dataclass.
|
28
|
-
"""
|
29
|
-
|
30
|
-
run_id: str = Field(description="A running ID.")
|
31
|
-
parent_run_id: Optional[str] = Field(
|
32
|
-
default=None, description="A parent running ID."
|
33
|
-
)
|
34
|
-
|
35
|
-
|
36
25
|
@router.post(
|
37
26
|
path="/execute/",
|
38
27
|
response_class=UJSONResponse,
|
39
28
|
status_code=st.HTTP_200_OK,
|
40
29
|
)
|
41
30
|
async def job_execute(
|
42
|
-
result: ResultCreate,
|
43
31
|
job: Job,
|
44
32
|
params: dict[str, Any],
|
45
|
-
|
33
|
+
run_id: str = Body(...),
|
34
|
+
extras: Optional[dict[str, Any]] = Body(default=None),
|
46
35
|
) -> UJSONResponse:
|
47
36
|
"""Execute job via RestAPI with execute route path."""
|
48
37
|
logger.info("[API]: Start execute job ...")
|
49
|
-
|
50
|
-
|
51
|
-
parent_run_id=result.parent_run_id,
|
52
|
-
extras=extras or {},
|
53
|
-
)
|
38
|
+
parent_run_id: str = run_id
|
39
|
+
run_id = gen_id(job.id, unique=True)
|
54
40
|
|
55
41
|
if extras:
|
56
42
|
job.extras = extras
|
57
43
|
|
44
|
+
trace: Trace = get_trace(
|
45
|
+
run_id, parent_run_id=parent_run_id, extras=job.extras
|
46
|
+
)
|
47
|
+
|
58
48
|
context: DictData = {}
|
59
49
|
try:
|
60
50
|
job.set_outputs(
|
61
51
|
job.execute(
|
62
52
|
params=params,
|
63
|
-
run_id=
|
64
|
-
parent_run_id=rs.parent_run_id,
|
53
|
+
run_id=parent_run_id,
|
65
54
|
).context,
|
66
55
|
to=context,
|
67
56
|
)
|
68
57
|
except JobError as err:
|
69
|
-
|
58
|
+
trace.error(f"[JOB]: {err.__class__.__name__}: {err}")
|
70
59
|
return UJSONResponse(
|
71
60
|
content={
|
72
61
|
"message": str(err),
|
73
|
-
"
|
74
|
-
"run_id": rs.run_id,
|
75
|
-
"parent_run_id": rs.parent_run_id,
|
76
|
-
},
|
62
|
+
"run_id": parent_run_id,
|
77
63
|
"job": job.model_dump(
|
78
64
|
by_alias=True,
|
79
65
|
exclude_none=False,
|
@@ -88,7 +74,7 @@ async def job_execute(
|
|
88
74
|
return UJSONResponse(
|
89
75
|
content={
|
90
76
|
"message": "Execute job via RestAPI successful.",
|
91
|
-
"
|
77
|
+
"run_id": parent_run_id,
|
92
78
|
"job": job.model_dump(
|
93
79
|
by_alias=True,
|
94
80
|
exclude_none=False,
|
@@ -10,7 +10,7 @@ from fastapi import APIRouter, Path, Query
|
|
10
10
|
from fastapi import status as st
|
11
11
|
from fastapi.responses import UJSONResponse
|
12
12
|
|
13
|
-
from ...audits import
|
13
|
+
from ...audits import get_audit_model
|
14
14
|
from ...result import Result
|
15
15
|
|
16
16
|
router = APIRouter(
|
@@ -86,11 +86,11 @@ async def get_trace_with_id(run_id: str):
|
|
86
86
|
)
|
87
87
|
async def get_audits():
|
88
88
|
"""Return all audit logs from the current audit log path that config with
|
89
|
-
`
|
89
|
+
`WORKFLOW_AUDIT_URL` environment variable name.
|
90
90
|
"""
|
91
91
|
return {
|
92
92
|
"message": "Getting audit logs",
|
93
|
-
"audits": list(
|
93
|
+
"audits": list(get_audit_model().find_audits(name="demo")),
|
94
94
|
}
|
95
95
|
|
96
96
|
|
@@ -103,13 +103,13 @@ async def get_audits():
|
|
103
103
|
)
|
104
104
|
async def get_audit_with_workflow(workflow: str):
|
105
105
|
"""Return all audit logs with specific workflow name from the current audit
|
106
|
-
log path that config with `
|
106
|
+
log path that config with `WORKFLOW_AUDIT_URL` environment variable name.
|
107
107
|
|
108
108
|
- **workflow**: A specific workflow name that want to find audit logs.
|
109
109
|
"""
|
110
110
|
return {
|
111
111
|
"message": f"Getting audit logs with workflow name {workflow}",
|
112
|
-
"audits": list(
|
112
|
+
"audits": list(get_audit_model().find_audits(name="demo")),
|
113
113
|
}
|
114
114
|
|
115
115
|
|
@@ -125,7 +125,7 @@ async def get_audit_with_workflow_release(
|
|
125
125
|
release: str = Path(...),
|
126
126
|
):
|
127
127
|
"""Return all audit logs with specific workflow name and release date from
|
128
|
-
the current audit log path that config with `
|
128
|
+
the current audit log path that config with `WORKFLOW_AUDIT_URL`
|
129
129
|
environment variable name.
|
130
130
|
|
131
131
|
- **workflow**: A specific workflow name that want to find audit logs.
|
@@ -136,7 +136,7 @@ async def get_audit_with_workflow_release(
|
|
136
136
|
f"Getting audit logs with workflow name {workflow} and release "
|
137
137
|
f"{release}"
|
138
138
|
),
|
139
|
-
"audits": list(
|
139
|
+
"audits": list(get_audit_model().find_audits(name="demo")),
|
140
140
|
}
|
141
141
|
|
142
142
|
|
@@ -154,7 +154,7 @@ async def get_audit_with_workflow_release_run_id(
|
|
154
154
|
workflow: str, release: str, run_id: str
|
155
155
|
):
|
156
156
|
"""Return all audit logs with specific workflow name and release date from
|
157
|
-
the current audit log path that config with `
|
157
|
+
the current audit log path that config with `WORKFLOW_AUDIT_URL`
|
158
158
|
environment variable name.
|
159
159
|
|
160
160
|
- **workflow**: A specific workflow name that want to find audit logs.
|
@@ -167,5 +167,5 @@ async def get_audit_with_workflow_release_run_id(
|
|
167
167
|
f"Getting audit logs with workflow name {workflow}, release "
|
168
168
|
f"{release}, and running ID {run_id}"
|
169
169
|
),
|
170
|
-
"audits": list(
|
170
|
+
"audits": list(get_audit_model().find_audits(name="demo")),
|
171
171
|
}
|
@@ -16,7 +16,7 @@ from fastapi.responses import UJSONResponse
|
|
16
16
|
from pydantic import BaseModel
|
17
17
|
|
18
18
|
from ...__types import DictData
|
19
|
-
from ...audits import
|
19
|
+
from ...audits import Audit, get_audit_model
|
20
20
|
from ...conf import YamlParser
|
21
21
|
from ...result import Result
|
22
22
|
from ...workflow import Workflow
|
@@ -100,7 +100,7 @@ async def get_workflow_audits(name: str):
|
|
100
100
|
exclude_none=False,
|
101
101
|
exclude_unset=True,
|
102
102
|
)
|
103
|
-
for audit in
|
103
|
+
for audit in get_audit_model().find_audits(name=name)
|
104
104
|
],
|
105
105
|
}
|
106
106
|
except FileNotFoundError:
|
@@ -114,7 +114,7 @@ async def get_workflow_audits(name: str):
|
|
114
114
|
async def get_workflow_release_audit(name: str, release: str):
|
115
115
|
"""Get Workflow audit log with an input release value."""
|
116
116
|
try:
|
117
|
-
audit:
|
117
|
+
audit: Audit = get_audit_model().find_audit_with_release(
|
118
118
|
name=name,
|
119
119
|
release=datetime.strptime(release, "%Y%m%d%H%M%S"),
|
120
120
|
)
|