ddeutil-workflow 0.0.67__py3-none-any.whl → 0.0.69__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.
@@ -1 +1 @@
1
- __version__: str = "0.0.67"
1
+ __version__: str = "0.0.69"
@@ -5,23 +5,16 @@
5
5
  # ------------------------------------------------------------------------------
6
6
  from .__cron import CronJob, CronRunner
7
7
  from .__types import DictData, DictStr, Matrix, Re, TupleStr
8
- from .conf import *
9
- from .errors import *
10
- from .event import *
11
- from .job import *
12
- from .logs import (
8
+ from .audits import (
13
9
  Audit,
14
10
  AuditModel,
15
11
  FileAudit,
16
- FileTrace,
17
- Trace,
18
- TraceData,
19
- TraceMeta,
20
- TraceModel,
21
12
  get_audit,
22
- get_dt_tznow,
23
- get_trace,
24
13
  )
14
+ from .conf import *
15
+ from .errors import *
16
+ from .event import *
17
+ from .job import *
25
18
  from .params import *
26
19
  from .result import (
27
20
  CANCEL,
@@ -34,5 +27,14 @@ from .result import (
34
27
  )
35
28
  from .reusables import *
36
29
  from .stages import *
30
+ from .traces import (
31
+ ConsoleTrace,
32
+ FileTrace,
33
+ Trace,
34
+ TraceData,
35
+ TraceMeta,
36
+ TraceModel,
37
+ get_trace,
38
+ )
37
39
  from .utils import *
38
40
  from .workflow import *
@@ -6,6 +6,7 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  import contextlib
9
+ import logging
9
10
  from collections.abc import AsyncIterator
10
11
 
11
12
  from dotenv import load_dotenv
@@ -19,11 +20,10 @@ from fastapi.responses import UJSONResponse
19
20
 
20
21
  from ..__about__ import __version__
21
22
  from ..conf import api_config
22
- from ..logs import get_logger
23
23
  from .routes import job, log, workflow
24
24
 
25
25
  load_dotenv()
26
- logger = get_logger("uvicorn.error")
26
+ logger = logging.getLogger("uvicorn.error")
27
27
 
28
28
 
29
29
  @contextlib.asynccontextmanager
@@ -58,12 +58,16 @@ app.add_middleware(
58
58
 
59
59
 
60
60
  @app.get(path="/", response_class=UJSONResponse)
61
- async def health():
61
+ async def health() -> UJSONResponse:
62
62
  """Index view that not return any template without json status."""
63
- return {"message": "Workflow already start up with healthy status."}
63
+ logger.info("[API]: Workflow API Application already running ...")
64
+ return UJSONResponse(
65
+ content={"message": "Workflow already start up with healthy status."},
66
+ status_code=st.HTTP_200_OK,
67
+ )
64
68
 
65
69
 
66
- # NOTE Add the jobs and logs routes by default.
70
+ # NOTE: Add the jobs and logs routes by default.
67
71
  app.include_router(job, prefix=api_config.prefix_path)
68
72
  app.include_router(log, prefix=api_config.prefix_path)
69
73
  app.include_router(workflow, prefix=api_config.prefix_path)
@@ -71,21 +75,18 @@ app.include_router(workflow, prefix=api_config.prefix_path)
71
75
 
72
76
  @app.exception_handler(RequestValidationError)
73
77
  async def validation_exception_handler(
74
- request: Request, exc: RequestValidationError
75
- ):
78
+ request: Request,
79
+ exc: RequestValidationError,
80
+ ) -> UJSONResponse:
81
+ """Error Handler for model validate does not valid."""
76
82
  _ = request
77
83
  return UJSONResponse(
78
84
  status_code=st.HTTP_422_UNPROCESSABLE_ENTITY,
79
- content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
80
- )
81
-
82
-
83
- if __name__ == "__main__":
84
- import uvicorn
85
-
86
- uvicorn.run(
87
- app,
88
- host="0.0.0.0",
89
- port=80,
90
- log_level="DEBUG",
85
+ content=jsonable_encoder(
86
+ {
87
+ "message": "Body does not parsing with model.",
88
+ "detail": exc.errors(),
89
+ "body": exc.body,
90
+ }
91
+ ),
91
92
  )
@@ -0,0 +1,59 @@
1
+ from typing import Any
2
+
3
+ from uvicorn.config import LOGGING_CONFIG as LOGGING_CONFIG_UVICORN
4
+
5
+ from ..conf import config
6
+
7
+ LOGGING_CONFIG: dict[str, Any] = { # pragma: no cov
8
+ "version": 1,
9
+ "disable_existing_loggers": False,
10
+ "formatters": {
11
+ "default": LOGGING_CONFIG_UVICORN["formatters"]["default"],
12
+ "access": LOGGING_CONFIG_UVICORN["formatters"]["access"],
13
+ "custom": {
14
+ "format": config.log_format,
15
+ "datefmt": config.log_datetime_format,
16
+ },
17
+ },
18
+ "root": {
19
+ "level": "DEBUG" if config.debug else "INFO",
20
+ },
21
+ "handlers": {
22
+ "default": LOGGING_CONFIG_UVICORN["handlers"]["default"],
23
+ "access": LOGGING_CONFIG_UVICORN["handlers"]["access"],
24
+ "stream_custom": {
25
+ "formatter": "custom",
26
+ "class": "logging.StreamHandler",
27
+ "stream": "ext://sys.stdout",
28
+ },
29
+ # "file_handler": {
30
+ # "formatter": "custom_formatter",
31
+ # "class": "logging.handlers.RotatingFileHandler",
32
+ # "filename": "logs/app.log",
33
+ # "maxBytes": 1024 * 1024 * 1,
34
+ # "backupCount": 3,
35
+ # },
36
+ },
37
+ "loggers": {
38
+ "uvicorn": {
39
+ "handlers": ["default"],
40
+ "level": "DEBUG" if config.debug else "INFO",
41
+ "propagate": False,
42
+ },
43
+ "uvicorn.access": {
44
+ "handlers": ["access"],
45
+ "level": "DEBUG" if config.debug else "INFO",
46
+ "propagate": False,
47
+ },
48
+ "uvicorn.error": {
49
+ "handlers": ["default"],
50
+ "level": "DEBUG" if config.debug else "INFO",
51
+ },
52
+ "ddeutil.workflow": {
53
+ "handlers": ["stream_custom"],
54
+ "level": "INFO",
55
+ # "propagate": False,
56
+ "propagate": True,
57
+ },
58
+ },
59
+ }
@@ -3,6 +3,6 @@
3
3
  # Licensed under the MIT License. See LICENSE in the project root for
4
4
  # license information.
5
5
  # ------------------------------------------------------------------------------
6
- from .job import job_route as job
7
- from .logs import log_route as log
8
- from .workflows import workflow_route as workflow
6
+ from .job import router as job
7
+ from .logs import router as log
8
+ from .workflows import router as workflow
@@ -5,20 +5,21 @@
5
5
  # ------------------------------------------------------------------------------
6
6
  from __future__ import annotations
7
7
 
8
+ import logging
8
9
  from typing import Any, Optional
9
10
 
10
11
  from fastapi import APIRouter
12
+ from fastapi import status as st
11
13
  from fastapi.responses import UJSONResponse
12
14
  from pydantic import BaseModel, Field
13
15
 
14
16
  from ...__types import DictData
15
17
  from ...errors import JobError
16
18
  from ...job import Job
17
- from ...logs import get_logger
18
19
  from ...result import Result
19
20
 
20
- logger = get_logger("uvicorn.error")
21
- job_route = APIRouter(prefix="/job", tags=["job"])
21
+ logger = logging.getLogger("uvicorn.error")
22
+ router = APIRouter(prefix="/job", tags=["job"])
22
23
 
23
24
 
24
25
  class ResultCreate(BaseModel):
@@ -32,14 +33,19 @@ class ResultCreate(BaseModel):
32
33
  )
33
34
 
34
35
 
35
- @job_route.post(path="/execute/", response_class=UJSONResponse)
36
+ @router.post(
37
+ path="/execute/",
38
+ response_class=UJSONResponse,
39
+ status_code=st.HTTP_200_OK,
40
+ )
36
41
  async def job_execute(
37
42
  result: ResultCreate,
38
43
  job: Job,
39
44
  params: dict[str, Any],
40
45
  extras: Optional[dict[str, Any]] = None,
41
- ):
46
+ ) -> UJSONResponse:
42
47
  """Execute job via RestAPI with execute route path."""
48
+ logger.info("[API]: Start execute job ...")
43
49
  rs: Result = Result(
44
50
  run_id=result.run_id,
45
51
  parent_run_id=result.parent_run_id,
@@ -61,15 +67,35 @@ async def job_execute(
61
67
  )
62
68
  except JobError as err:
63
69
  rs.trace.error(f"[JOB]: {err.__class__.__name__}: {err}")
70
+ return UJSONResponse(
71
+ content={
72
+ "message": str(err),
73
+ "result": {
74
+ "run_id": rs.run_id,
75
+ "parent_run_id": rs.parent_run_id,
76
+ },
77
+ "job": job.model_dump(
78
+ by_alias=True,
79
+ exclude_none=False,
80
+ exclude_unset=True,
81
+ ),
82
+ "params": params,
83
+ "context": context,
84
+ },
85
+ status_code=st.HTTP_500_INTERNAL_SERVER_ERROR,
86
+ )
64
87
 
65
- return {
66
- "message": "Execute job via RestAPI.",
67
- "result": {"run_id": rs.run_id, "parent_run_id": rs.parent_run_id},
68
- "job": job.model_dump(
69
- by_alias=True,
70
- exclude_none=False,
71
- exclude_unset=True,
72
- ),
73
- "params": params,
74
- "context": context,
75
- }
88
+ return UJSONResponse(
89
+ content={
90
+ "message": "Execute job via RestAPI successful.",
91
+ "result": {"run_id": rs.run_id, "parent_run_id": rs.parent_run_id},
92
+ "job": job.model_dump(
93
+ by_alias=True,
94
+ exclude_none=False,
95
+ exclude_unset=True,
96
+ ),
97
+ "params": params,
98
+ "context": context,
99
+ },
100
+ status_code=st.HTTP_200_OK,
101
+ )
@@ -10,17 +10,17 @@ 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 ...logs import get_audit
13
+ from ...audits import get_audit
14
14
  from ...result import Result
15
15
 
16
- log_route = APIRouter(
16
+ router = APIRouter(
17
17
  prefix="/logs",
18
18
  tags=["logs"],
19
19
  default_response_class=UJSONResponse,
20
20
  )
21
21
 
22
22
 
23
- @log_route.get(
23
+ @router.get(
24
24
  path="/traces/",
25
25
  response_class=UJSONResponse,
26
26
  status_code=st.HTTP_200_OK,
@@ -50,7 +50,7 @@ async def get_traces(
50
50
  }
51
51
 
52
52
 
53
- @log_route.get(
53
+ @router.get(
54
54
  path="/traces/{run_id}",
55
55
  response_class=UJSONResponse,
56
56
  status_code=st.HTTP_200_OK,
@@ -77,7 +77,7 @@ async def get_trace_with_id(run_id: str):
77
77
  }
78
78
 
79
79
 
80
- @log_route.get(
80
+ @router.get(
81
81
  path="/audits/",
82
82
  response_class=UJSONResponse,
83
83
  status_code=st.HTTP_200_OK,
@@ -94,7 +94,7 @@ async def get_audits():
94
94
  }
95
95
 
96
96
 
97
- @log_route.get(
97
+ @router.get(
98
98
  path="/audits/{workflow}/",
99
99
  response_class=UJSONResponse,
100
100
  status_code=st.HTTP_200_OK,
@@ -113,7 +113,7 @@ async def get_audit_with_workflow(workflow: str):
113
113
  }
114
114
 
115
115
 
116
- @log_route.get(
116
+ @router.get(
117
117
  path="/audits/{workflow}/{release}",
118
118
  response_class=UJSONResponse,
119
119
  status_code=st.HTTP_200_OK,
@@ -140,7 +140,7 @@ async def get_audit_with_workflow_release(
140
140
  }
141
141
 
142
142
 
143
- @log_route.get(
143
+ @router.get(
144
144
  path="/audits/{workflow}/{release}/{run_id}",
145
145
  response_class=UJSONResponse,
146
146
  status_code=st.HTTP_200_OK,
@@ -5,6 +5,7 @@
5
5
  # ------------------------------------------------------------------------------
6
6
  from __future__ import annotations
7
7
 
8
+ import logging
8
9
  from dataclasses import asdict
9
10
  from datetime import datetime
10
11
  from typing import Any
@@ -15,23 +16,23 @@ from fastapi.responses import UJSONResponse
15
16
  from pydantic import BaseModel
16
17
 
17
18
  from ...__types import DictData
18
- from ...conf import Loader
19
- from ...logs import Audit, get_audit, get_logger
19
+ from ...audits import AuditModel, get_audit
20
+ from ...conf import YamlParser
20
21
  from ...result import Result
21
22
  from ...workflow import Workflow
22
23
 
23
- logger = get_logger("uvicorn.error")
24
- workflow_route = APIRouter(
24
+ logger = logging.getLogger("uvicorn.error")
25
+ router = APIRouter(
25
26
  prefix="/workflows",
26
27
  tags=["workflows"],
27
28
  default_response_class=UJSONResponse,
28
29
  )
29
30
 
30
31
 
31
- @workflow_route.get(path="/", status_code=st.HTTP_200_OK)
32
+ @router.get(path="/", status_code=st.HTTP_200_OK)
32
33
  async def get_workflows() -> DictData:
33
34
  """Return all workflow workflows that exists in config path."""
34
- workflows: DictData = dict(Loader.finds(Workflow))
35
+ workflows: DictData = dict(YamlParser.finds(Workflow))
35
36
  return {
36
37
  "message": f"Getting all workflows: {len(workflows)}",
37
38
  "count": len(workflows),
@@ -39,7 +40,7 @@ async def get_workflows() -> DictData:
39
40
  }
40
41
 
41
42
 
42
- @workflow_route.get(path="/{name}", status_code=st.HTTP_200_OK)
43
+ @router.get(path="/{name}", status_code=st.HTTP_200_OK)
43
44
  async def get_workflow_by_name(name: str) -> DictData:
44
45
  """Return model of workflow that passing an input workflow name."""
45
46
  try:
@@ -63,7 +64,7 @@ class ExecutePayload(BaseModel):
63
64
  params: dict[str, Any]
64
65
 
65
66
 
66
- @workflow_route.post(path="/{name}/execute", status_code=st.HTTP_202_ACCEPTED)
67
+ @router.post(path="/{name}/execute", status_code=st.HTTP_202_ACCEPTED)
67
68
  async def workflow_execute(name: str, payload: ExecutePayload) -> DictData:
68
69
  """Return model of workflow that passing an input workflow name."""
69
70
  try:
@@ -88,7 +89,7 @@ async def workflow_execute(name: str, payload: ExecutePayload) -> DictData:
88
89
  return asdict(result)
89
90
 
90
91
 
91
- @workflow_route.get(path="/{name}/audits", status_code=st.HTTP_200_OK)
92
+ @router.get(path="/{name}/audits", status_code=st.HTTP_200_OK)
92
93
  async def get_workflow_audits(name: str):
93
94
  try:
94
95
  return {
@@ -109,11 +110,11 @@ async def get_workflow_audits(name: str):
109
110
  ) from None
110
111
 
111
112
 
112
- @workflow_route.get(path="/{name}/audits/{release}", status_code=st.HTTP_200_OK)
113
+ @router.get(path="/{name}/audits/{release}", status_code=st.HTTP_200_OK)
113
114
  async def get_workflow_release_audit(name: str, release: str):
114
115
  """Get Workflow audit log with an input release value."""
115
116
  try:
116
- audit: Audit = get_audit().find_audit_with_release(
117
+ audit: AuditModel = get_audit().find_audit_with_release(
117
118
  name=name,
118
119
  release=datetime.strptime(release, "%Y%m%d%H%M%S"),
119
120
  )