ddeutil-workflow 0.0.23__py3-none-any.whl → 0.0.25__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.
@@ -43,19 +43,18 @@ from typing_extensions import Self
43
43
  from .__cron import CronJob, CronRunner
44
44
  from .__types import DictData, TupleStr
45
45
  from .conf import FileLog, Loader, Log, config, get_logger
46
+ from .cron import On
46
47
  from .exceptions import JobException, WorkflowException
47
48
  from .job import Job
48
- from .on import On
49
49
  from .params import Param
50
50
  from .result import Result
51
51
  from .utils import (
52
52
  cut_id,
53
- delay,
54
53
  gen_id,
55
- get_diff_sec,
56
54
  get_dt_now,
57
55
  has_template,
58
56
  param2template,
57
+ wait_a_minute,
59
58
  )
60
59
 
61
60
  logger = get_logger("ddeutil.workflow")
@@ -64,7 +63,7 @@ __all__: TupleStr = (
64
63
  "Workflow",
65
64
  "WorkflowRelease",
66
65
  "WorkflowQueue",
67
- "WorkflowTaskData",
66
+ "WorkflowTask",
68
67
  )
69
68
 
70
69
 
@@ -168,7 +167,23 @@ class WorkflowQueue:
168
167
  """
169
168
  return len(self.queue) > 0
170
169
 
171
- def check_queue(self, value: WorkflowRelease) -> bool:
170
+ @property
171
+ def first_queue(self) -> WorkflowRelease:
172
+ """Check an input WorkflowRelease object is the first value of the
173
+ waiting queue.
174
+
175
+ :rtype: bool
176
+ """
177
+ # NOTE: Old logic to peeking the first release from waiting queue.
178
+ #
179
+ # first_value: WorkflowRelease = heappop(self.queue)
180
+ # heappush(self.queue, first_value)
181
+ #
182
+ # return first_value
183
+ #
184
+ return self.queue[0]
185
+
186
+ def check_queue(self, value: WorkflowRelease | datetime) -> bool:
172
187
  """Check a WorkflowRelease value already exists in list of tracking
173
188
  queues.
174
189
 
@@ -177,27 +192,37 @@ class WorkflowQueue:
177
192
 
178
193
  :rtype: bool
179
194
  """
195
+ if isinstance(value, datetime):
196
+ value = WorkflowRelease.from_dt(value)
197
+
180
198
  return (
181
199
  (value in self.queue)
182
200
  or (value in self.running)
183
201
  or (value in self.complete)
184
202
  )
185
203
 
186
- def push_queue(self, value: WorkflowRelease) -> Self:
187
- """Push data to the queue."""
188
- heappush(self.queue, value)
189
- return self
190
-
191
- def push_running(self, value: WorkflowRelease) -> Self:
192
- """Push data to the running."""
193
- heappush(self.running, value)
194
- return self
195
-
196
204
  def remove_running(self, value: WorkflowRelease) -> Self:
197
- """Remove data on the running if it exists."""
205
+ """Remove WorkflowRelease in the running queue if it exists."""
198
206
  if value in self.running:
199
207
  self.running.remove(value)
200
208
 
209
+ def mark_complete(self, value: WorkflowRelease) -> Self:
210
+ """Push WorkflowRelease to the complete queue."""
211
+ heappush(self.complete, value)
212
+
213
+ # NOTE: Remove complete queue on workflow that keep more than the
214
+ # maximum config.
215
+ num_complete_delete: int = (
216
+ len(self.complete) - config.max_queue_complete_hist
217
+ )
218
+
219
+ if num_complete_delete > 0:
220
+ print(num_complete_delete)
221
+ for _ in range(num_complete_delete):
222
+ heappop(self.complete)
223
+
224
+ return self
225
+
201
226
 
202
227
  class Workflow(BaseModel):
203
228
  """Workflow Pydantic model.
@@ -249,7 +274,7 @@ class Workflow(BaseModel):
249
274
  loader: Loader = Loader(name, externals=(externals or {}))
250
275
 
251
276
  # NOTE: Validate the config type match with current connection model
252
- if loader.type != cls:
277
+ if loader.type != cls.__name__:
253
278
  raise ValueError(f"Type {loader.type} does not match with {cls}")
254
279
 
255
280
  loader_data: DictData = copy.deepcopy(loader.data)
@@ -448,6 +473,7 @@ class Workflow(BaseModel):
448
473
  queue: (
449
474
  WorkflowQueue | list[datetime] | list[WorkflowRelease] | None
450
475
  ) = None,
476
+ override_log_name: str | None = None,
451
477
  ) -> Result:
452
478
  """Release the workflow execution with overriding parameter with the
453
479
  release templating that include logical date (release date), execution
@@ -456,18 +482,30 @@ class Workflow(BaseModel):
456
482
  This method allow workflow use log object to save the execution
457
483
  result to log destination like file log to the local `/logs` directory.
458
484
 
485
+ :Steps:
486
+ - Initialize WorkflowQueue and WorkflowRelease if they do not pass.
487
+ - Create release data for pass to parameter templating function.
488
+ - Execute this workflow with mapping release data to its parameters.
489
+ - Writing log
490
+ - Remove this release on the running queue
491
+ - Push this release to complete queue
492
+
459
493
  :param release: A release datetime or WorkflowRelease object.
460
494
  :param params: A workflow parameter that pass to execute method.
461
495
  :param queue: A list of release time that already queue.
462
496
  :param run_id: A workflow running ID for this release.
463
497
  :param log: A log class that want to save the execution result.
464
498
  :param queue: A WorkflowQueue object.
499
+ :param override_log_name: An override logging name that use instead
500
+ the workflow name.
465
501
 
466
502
  :rtype: Result
467
503
  """
468
504
  log: type[Log] = log or FileLog
469
- run_id: str = run_id or gen_id(self.name, unique=True)
505
+ name: str = override_log_name or self.name
506
+ run_id: str = run_id or gen_id(name, unique=True)
470
507
  rs_release: Result = Result(run_id=run_id)
508
+ rs_release_type: str = "release"
471
509
 
472
510
  # VALIDATE: Change queue value to WorkflowQueue object.
473
511
  if queue is None or isinstance(queue, list):
@@ -475,10 +513,11 @@ class Workflow(BaseModel):
475
513
 
476
514
  # VALIDATE: Change release value to WorkflowRelease object.
477
515
  if isinstance(release, datetime):
516
+ rs_release_type: str = "datetime"
478
517
  release: WorkflowRelease = WorkflowRelease.from_dt(release)
479
518
 
480
519
  logger.debug(
481
- f"({cut_id(run_id)}) [RELEASE]: {self.name!r} : Start release - "
520
+ f"({cut_id(run_id)}) [RELEASE]: Start release - {name!r} : "
482
521
  f"{release.date:%Y-%m-%d %H:%M:%S}"
483
522
  )
484
523
 
@@ -499,14 +538,14 @@ class Workflow(BaseModel):
499
538
  run_id=run_id,
500
539
  )
501
540
  logger.debug(
502
- f"({cut_id(run_id)}) [RELEASE]: {self.name!r} : End release - "
541
+ f"({cut_id(run_id)}) [RELEASE]: End release - {name!r} : "
503
542
  f"{release.date:%Y-%m-%d %H:%M:%S}"
504
543
  )
505
544
 
506
545
  rs.set_parent_run_id(run_id)
507
546
  rs_log: Log = log.model_validate(
508
547
  {
509
- "name": self.name,
548
+ "name": name,
510
549
  "release": release.date,
511
550
  "type": release.type,
512
551
  "context": rs.context,
@@ -516,12 +555,14 @@ class Workflow(BaseModel):
516
555
  )
517
556
 
518
557
  # NOTE: Saving execution result to destination of the input log object.
558
+ logger.debug(f"({cut_id(run_id)}) [LOG]: Writing log: {name!r}.")
519
559
  rs_log.save(excluded=None)
520
560
 
521
561
  # NOTE: Remove this release from running.
522
562
  queue.remove_running(release)
523
- heappush(queue.complete, release)
563
+ queue.mark_complete(release)
524
564
 
565
+ # NOTE: Remove the params key from the result context for deduplicate.
525
566
  context: dict[str, Any] = rs.context
526
567
  context.pop("params")
527
568
 
@@ -529,12 +570,17 @@ class Workflow(BaseModel):
529
570
  status=0,
530
571
  context={
531
572
  "params": params,
532
- "release": {"status": "success", "logical_date": release.date},
573
+ "release": {
574
+ "status": "success",
575
+ "type": rs_release_type,
576
+ "logical_date": release.date,
577
+ "release": release,
578
+ },
533
579
  "outputs": context,
534
580
  },
535
581
  )
536
582
 
537
- def queue_poking(
583
+ def queue(
538
584
  self,
539
585
  offset: float,
540
586
  end_date: datetime,
@@ -589,7 +635,7 @@ class Workflow(BaseModel):
589
635
  continue
590
636
 
591
637
  # NOTE: Push the WorkflowRelease object to queue.
592
- queue.push_queue(workflow_release)
638
+ heappush(queue.queue, workflow_release)
593
639
 
594
640
  return queue
595
641
 
@@ -667,7 +713,7 @@ class Workflow(BaseModel):
667
713
  wf_queue: WorkflowQueue = WorkflowQueue()
668
714
 
669
715
  # NOTE: Make queue to the workflow queue object.
670
- self.queue_poking(
716
+ self.queue(
671
717
  offset,
672
718
  end_date=end_date,
673
719
  queue=wf_queue,
@@ -703,11 +749,11 @@ class Workflow(BaseModel):
703
749
  f"release has diff time more than 60 seconds ..."
704
750
  )
705
751
  heappush(wf_queue.queue, release)
706
- delay(60)
752
+ wait_a_minute(get_dt_now(tz=config.tz, offset=offset))
707
753
 
708
754
  # WARNING: I already call queue poking again because issue
709
755
  # about the every minute crontab.
710
- self.queue_poking(
756
+ self.queue(
711
757
  offset,
712
758
  end_date,
713
759
  queue=wf_queue,
@@ -717,7 +763,7 @@ class Workflow(BaseModel):
717
763
  continue
718
764
 
719
765
  # NOTE: Push the latest WorkflowRelease to the running queue.
720
- wf_queue.push_running(release)
766
+ heappush(wf_queue.running, release)
721
767
 
722
768
  futures.append(
723
769
  executor.submit(
@@ -729,7 +775,7 @@ class Workflow(BaseModel):
729
775
  )
730
776
  )
731
777
 
732
- self.queue_poking(
778
+ self.queue(
733
779
  offset,
734
780
  end_date,
735
781
  queue=wf_queue,
@@ -1074,7 +1120,7 @@ class Workflow(BaseModel):
1074
1120
 
1075
1121
 
1076
1122
  @dataclass(config=ConfigDict(arbitrary_types_allowed=True))
1077
- class WorkflowTaskData:
1123
+ class WorkflowTask:
1078
1124
  """Workflow task Pydantic dataclass object that use to keep mapping data and
1079
1125
  workflow model for passing to the multithreading task.
1080
1126
 
@@ -1085,151 +1131,105 @@ class WorkflowTaskData:
1085
1131
  alias: str
1086
1132
  workflow: Workflow
1087
1133
  runner: CronRunner
1088
- params: DictData = field(default_factory=dict)
1134
+ values: DictData = field(default_factory=dict)
1089
1135
 
1090
1136
  def release(
1091
1137
  self,
1092
- queue: dict[str, list[datetime]],
1093
- log: Log | None = None,
1138
+ release: datetime | WorkflowRelease | None = None,
1094
1139
  run_id: str | None = None,
1095
- *,
1096
- waiting_sec: int = 60,
1097
- sleep_interval: int = 15,
1098
- ) -> Result: # pragma: no cov
1099
- """Release the workflow task data that use the same logic of
1100
- `workflow.release` method but use different the queue object for
1101
- tracking release datetime to run.
1102
-
1103
- :param queue: A mapping of alias name and list of release datetime.
1104
- :param log: A log object for saving result logging from workflow
1105
- execution process.
1140
+ log: type[Log] = None,
1141
+ queue: (
1142
+ WorkflowQueue | list[datetime] | list[WorkflowRelease] | None
1143
+ ) = None,
1144
+ ) -> Result:
1145
+ """Release the workflow task data.
1146
+
1147
+ :param release: A release datetime or WorkflowRelease object.
1106
1148
  :param run_id: A workflow running ID for this release.
1107
- :param waiting_sec: A second period value that allow workflow execute.
1108
- :param sleep_interval: A second value that want to waiting until time
1109
- to execute.
1149
+ :param log: A log class that want to save the execution result.
1150
+ :param queue: A WorkflowQueue object.
1110
1151
 
1111
1152
  :rtype: Result
1112
1153
  """
1113
1154
  log: type[Log] = log or FileLog
1114
- run_id: str = run_id or gen_id(self.alias, unique=True)
1115
- rs_release: Result = Result(run_id=run_id)
1116
- runner: CronRunner = self.runner
1117
1155
 
1118
- # NOTE: get next schedule time that generate from now.
1119
- next_time: datetime = runner.date
1156
+ if release is None:
1157
+ if queue.check_queue(self.runner.date):
1158
+ release = self.runner.next
1120
1159
 
1121
- # NOTE: get next utils it does not running.
1122
- while log.is_pointed(self.workflow.name, next_time) or (
1123
- next_time in queue[self.alias]
1124
- ):
1125
- next_time: datetime = runner.next
1160
+ while queue.check_queue(release):
1161
+ release = self.runner.next
1162
+ else:
1163
+ release = self.runner.date
1126
1164
 
1127
- logger.debug(
1128
- f"({cut_id(run_id)}) [CORE]: {self.workflow.name!r} : "
1129
- f"{runner.cron} : {next_time:%Y-%m-%d %H:%M:%S}"
1165
+ return self.workflow.release(
1166
+ release=release,
1167
+ params=self.values,
1168
+ run_id=run_id,
1169
+ log=log,
1170
+ queue=queue,
1171
+ override_log_name=self.alias,
1130
1172
  )
1131
- heappush(queue[self.alias], next_time)
1132
- start_sec: float = time.monotonic()
1133
-
1134
- if get_diff_sec(next_time, tz=runner.tz) > waiting_sec:
1135
- logger.debug(
1136
- f"({cut_id(run_id)}) [WORKFLOW]: {self.workflow.name!r} : "
1137
- f"{runner.cron} "
1138
- f": Does not closely >> {next_time:%Y-%m-%d %H:%M:%S}"
1139
- )
1140
1173
 
1141
- # NOTE: Add this next running datetime that not in period to queue
1142
- # and remove it to running.
1143
- queue[self.alias].remove(next_time)
1174
+ def queue(
1175
+ self,
1176
+ end_date: datetime,
1177
+ queue: WorkflowQueue,
1178
+ log: type[Log],
1179
+ *,
1180
+ force_run: bool = False,
1181
+ ):
1182
+ """Generate WorkflowRelease to WorkflowQueue object.
1183
+
1184
+ :param end_date: An end datetime object.
1185
+ :param queue: A workflow queue object.
1186
+ :param log: A log class that want to making log object.
1187
+ :param force_run: A flag that allow to release workflow if the log with
1188
+ that release was pointed.
1144
1189
 
1145
- time.sleep(0.2)
1146
- return rs_release.catch(status=0, context={})
1190
+ :rtype: WorkflowQueue
1191
+ """
1192
+ if self.runner.date > end_date:
1193
+ return queue
1147
1194
 
1148
- logger.debug(
1149
- f"({cut_id(run_id)}) [CORE]: {self.workflow.name!r} : "
1150
- f"{runner.cron} : Closely to run >> {next_time:%Y-%m-%d %H:%M:%S}"
1195
+ workflow_release = WorkflowRelease(
1196
+ date=self.runner.date,
1197
+ offset=0,
1198
+ end_date=end_date,
1199
+ runner=self.runner,
1200
+ type="task",
1151
1201
  )
1152
1202
 
1153
- # NOTE: Release when the time is nearly to schedule time.
1154
- while (duration := get_diff_sec(next_time, tz=config.tz)) > (
1155
- sleep_interval + 5
1203
+ while queue.check_queue(workflow_release) or (
1204
+ log.is_pointed(name=self.alias, release=workflow_release.date)
1205
+ and not force_run
1156
1206
  ):
1157
- logger.debug(
1158
- f"({cut_id(run_id)}) [CORE]: {self.workflow.name!r} : "
1159
- f"{runner.cron} : Sleep until: {duration}"
1207
+ workflow_release = WorkflowRelease(
1208
+ date=self.runner.next,
1209
+ offset=0,
1210
+ end_date=end_date,
1211
+ runner=self.runner,
1212
+ type="task",
1160
1213
  )
1161
- time.sleep(15)
1162
1214
 
1163
- time.sleep(0.5)
1215
+ if self.runner.date > end_date:
1216
+ return queue
1164
1217
 
1165
- # NOTE: Release parameter that use to change if params has
1166
- # templating.
1167
- release_params: DictData = {
1168
- "release": {
1169
- "logical_date": next_time,
1170
- "execute_date": datetime.now(tz=config.tz),
1171
- "run_id": run_id,
1172
- "timezone": runner.tz,
1173
- },
1174
- }
1175
-
1176
- # WARNING: Re-create workflow object that use new running workflow ID.
1177
- rs: Result = self.workflow.execute(
1178
- params=param2template(self.params, release_params),
1179
- )
1180
- logger.debug(
1181
- f"({cut_id(run_id)}) [CORE]: {self.workflow.name!r} : "
1182
- f"{runner.cron} : End release - {next_time:%Y-%m-%d %H:%M:%S}"
1183
- )
1184
-
1185
- # NOTE: Set parent ID on this result.
1186
- rs.set_parent_run_id(run_id)
1218
+ # NOTE: Push the WorkflowRelease object to queue.
1219
+ heappush(queue.queue, workflow_release)
1187
1220
 
1188
- # NOTE: Save result to log object saving.
1189
- rs_log: Log = log.model_validate(
1190
- {
1191
- "name": self.workflow.name,
1192
- "type": "schedule",
1193
- "release": next_time,
1194
- "context": rs.context,
1195
- "parent_run_id": rs.run_id,
1196
- "run_id": rs.run_id,
1197
- }
1198
- )
1199
- rs_log.save(excluded=None)
1200
-
1201
- # NOTE: Remove the current release date from the running.
1202
- queue[self.alias].remove(next_time)
1203
- total_sec: float = time.monotonic() - start_sec
1204
-
1205
- # IMPORTANT:
1206
- # Add the next running datetime to workflow task queue.
1207
- future_running_time: datetime = runner.next
1208
-
1209
- while (
1210
- future_running_time in queue[self.alias]
1211
- or (future_running_time - next_time).total_seconds() < total_sec
1212
- ): # pragma: no cov
1213
- future_running_time: datetime = runner.next
1214
-
1215
- # NOTE: Queue next release date.
1216
- logger.debug(f"[CORE]: {'-' * 100}")
1217
-
1218
- context: dict[str, Any] = rs.context
1219
- context.pop("params")
1221
+ return queue
1220
1222
 
1221
- return rs_release.catch(
1222
- status=0,
1223
- context={
1224
- "params": self.params,
1225
- "release": {"status": "success", "logical_date": next_time},
1226
- "outputs": context,
1227
- },
1223
+ def __repr__(self) -> str:
1224
+ return (
1225
+ f"{self.__class__.__name__}(alias={self.alias!r}, "
1226
+ f"workflow={self.workflow.name!r}, runner={self.runner!r}, "
1227
+ f"values={self.values})"
1228
1228
  )
1229
1229
 
1230
- def __eq__(self, other: WorkflowTaskData) -> bool:
1230
+ def __eq__(self, other: WorkflowTask) -> bool:
1231
1231
  """Override equal property that will compare only the same type."""
1232
- if isinstance(other, WorkflowTaskData):
1232
+ if isinstance(other, WorkflowTask):
1233
1233
  return (
1234
1234
  self.workflow.name == other.workflow.name
1235
1235
  and self.runner.cron == other.runner.cron
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ddeutil-workflow
3
- Version: 0.0.23
3
+ Version: 0.0.25
4
4
  Summary: Lightweight workflow orchestration with less dependencies
5
5
  Author-email: ddeutils <korawich.anu@gmail.com>
6
6
  License: MIT
@@ -24,7 +24,7 @@ Description-Content-Type: text/markdown
24
24
  License-File: LICENSE
25
25
  Requires-Dist: ddeutil>=0.4.3
26
26
  Requires-Dist: ddeutil-io[toml,yaml]>=0.2.3
27
- Requires-Dist: pydantic==2.10.2
27
+ Requires-Dist: pydantic==2.10.4
28
28
  Requires-Dist: python-dotenv==1.0.1
29
29
  Requires-Dist: typer==0.15.1
30
30
  Requires-Dist: schedule<2.0.0,==1.2.2
@@ -68,8 +68,8 @@ configuration. It called **Metadata Driven Data Workflow**.
68
68
  > with `.yml` files and all of config file from several data orchestration framework
69
69
  > tools from my experience on Data Engineer. :grimacing:
70
70
  >
71
- > Other workflow that I interest on them and pick some interested feature to this
72
- > package:
71
+ > Other workflow tools that I interest on them and pick some interested feature
72
+ > implement to this package:
73
73
  >
74
74
  > - [Google **Workflows**](https://cloud.google.com/workflows)
75
75
  > - [AWS **Step Functions**](https://aws.amazon.com/step-functions/)
@@ -85,9 +85,6 @@ If you want to install this package with application add-ons, you should add
85
85
  | Python & CLI | `pip install ddeutil-workflow` | :heavy_check_mark: |
86
86
  | FastAPI Server | `pip install ddeutil-workflow[api]` | :heavy_check_mark: |
87
87
 
88
-
89
- > I added this feature to the main milestone.
90
- >
91
88
  > :egg: **Docker Images** supported:
92
89
  >
93
90
  > | Docker Image | Python Version | Support |
@@ -113,7 +110,7 @@ use-case.
113
110
  run-py-local:
114
111
 
115
112
  # Validate model that use to parsing exists for template file
116
- type: ddeutil.workflow.Workflow
113
+ type: ddeutil.workflow.workflow.Workflow
117
114
  on:
118
115
  # If workflow deploy to schedule, it will running every 5 minutes
119
116
  # with Asia/Bangkok timezone.
@@ -182,34 +179,35 @@ The main configuration that use to dynamic changing with your propose of this
182
179
  application. If any configuration values do not set yet, it will use default value
183
180
  and do not raise any error to you.
184
181
 
185
- | Environment | Component | Default | Description | Remark |
186
- |:----------------------------------------|:----------|:---------------------------------|--------------------------------------------------------------------------------------------------------------------|--------|
187
- | `WORKFLOW_ROOT_PATH` | Core | . | The root path of the workflow application. | |
188
- | `WORKFLOW_CORE_REGISTRY` | Core | src.ddeutil.workflow,tests.utils | List of importable string for the hook stage. | |
189
- | `WORKFLOW_CORE_REGISTRY_FILTER` | Core | ddeutil.workflow.utils | List of importable string for the filter template. | |
190
- | `WORKFLOW_CORE_PATH_CONF` | Core | conf | The config path that keep all template `.yaml` files. | |
191
- | `WORKFLOW_CORE_TIMEZONE` | Core | Asia/Bangkok | A Timezone string value that will pass to `ZoneInfo` object. | |
192
- | `WORKFLOW_CORE_STAGE_DEFAULT_ID` | Core | true | A flag that enable default stage ID that use for catch an execution output. | |
193
- | `WORKFLOW_CORE_STAGE_RAISE_ERROR` | Core | false | A flag that all stage raise StageException from stage execution. | |
194
- | `WORKFLOW_CORE_JOB_DEFAULT_ID` | Core | false | A flag that enable default job ID that use for catch an execution output. The ID that use will be sequence number. | |
195
- | `WORKFLOW_CORE_JOB_RAISE_ERROR` | Core | true | A flag that all job raise JobException from job strategy execution. | |
196
- | `WORKFLOW_CORE_MAX_NUM_POKING` | Core | 4 | . | |
197
- | `WORKFLOW_CORE_MAX_JOB_PARALLEL` | Core | 2 | The maximum job number that able to run parallel in workflow executor. | |
198
- | `WORKFLOW_CORE_MAX_JOB_EXEC_TIMEOUT` | Core | 600 | | |
199
- | `WORKFLOW_CORE_MAX_ON_PER_WORKFLOW` | Core | 5 | | |
200
- | `WORKFLOW_CORE_GENERATE_ID_SIMPLE_MODE` | Core | true | A flog that enable generating ID with `md5` algorithm. | |
201
- | `WORKFLOW_LOG_DEBUG_MODE` | Log | true | A flag that enable logging with debug level mode. | |
202
- | `WORKFLOW_LOG_ENABLE_WRITE` | Log | true | A flag that enable logging object saving log to its destination. | |
203
- | `WORKFLOW_APP_MAX_PROCESS` | Schedule | 2 | The maximum process worker number that run in scheduler app module. | |
204
- | `WORKFLOW_APP_MAX_SCHEDULE_PER_PROCESS` | Schedule | 100 | A schedule per process that run parallel. | |
205
- | `WORKFLOW_APP_STOP_BOUNDARY_DELTA` | Schedule | '{"minutes": 5, "seconds": 20}' | A time delta value that use to stop scheduler app in json string format. | |
182
+ | Environment | Component | Default | Description | Remark |
183
+ |:----------------------------------------|:----------:|:---------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|--------|
184
+ | `WORKFLOW_ROOT_PATH` | Core | . | The root path of the workflow application. | |
185
+ | `WORKFLOW_CORE_REGISTRY` | Core | src,src.ddeutil.workflow,tests,tests.utils | List of importable string for the hook stage. | |
186
+ | `WORKFLOW_CORE_REGISTRY_FILTER` | Core | src.ddeutil.workflow.utils,ddeutil.workflow.utils | List of importable string for the filter template. | |
187
+ | `WORKFLOW_CORE_PATH_CONF` | Core | conf | The config path that keep all template `.yaml` files. | |
188
+ | `WORKFLOW_CORE_TIMEZONE` | Core | Asia/Bangkok | A Timezone string value that will pass to `ZoneInfo` object. | |
189
+ | `WORKFLOW_CORE_STAGE_DEFAULT_ID` | Core | true | A flag that enable default stage ID that use for catch an execution output. | |
190
+ | `WORKFLOW_CORE_STAGE_RAISE_ERROR` | Core | false | A flag that all stage raise StageException from stage execution. | |
191
+ | `WORKFLOW_CORE_JOB_DEFAULT_ID` | Core | false | A flag that enable default job ID that use for catch an execution output. The ID that use will be sequence number. | |
192
+ | `WORKFLOW_CORE_JOB_RAISE_ERROR` | Core | true | A flag that all job raise JobException from job strategy execution. | |
193
+ | `WORKFLOW_CORE_MAX_NUM_POKING` | Core | 4 | . | |
194
+ | `WORKFLOW_CORE_MAX_JOB_PARALLEL` | Core | 2 | The maximum job number that able to run parallel in workflow executor. | |
195
+ | `WORKFLOW_CORE_MAX_JOB_EXEC_TIMEOUT` | Core | 600 | | |
196
+ | `WORKFLOW_CORE_MAX_CRON_PER_WORKFLOW` | Core | 5 | | |
197
+ | `WORKFLOW_CORE_MAX_QUEUE_COMPLETE_HIST` | Core | 16 | | |
198
+ | `WORKFLOW_CORE_GENERATE_ID_SIMPLE_MODE` | Core | true | A flog that enable generating ID with `md5` algorithm. | |
199
+ | `WORKFLOW_LOG_DEBUG_MODE` | Log | true | A flag that enable logging with debug level mode. | |
200
+ | `WORKFLOW_LOG_ENABLE_WRITE` | Log | true | A flag that enable logging object saving log to its destination. | |
201
+ | `WORKFLOW_APP_MAX_PROCESS` | Schedule | 2 | The maximum process worker number that run in scheduler app module. | |
202
+ | `WORKFLOW_APP_MAX_SCHEDULE_PER_PROCESS` | Schedule | 100 | A schedule per process that run parallel. | |
203
+ | `WORKFLOW_APP_STOP_BOUNDARY_DELTA` | Schedule | '{"minutes": 5, "seconds": 20}' | A time delta value that use to stop scheduler app in json string format. | |
206
204
 
207
205
  **API Application**:
208
206
 
209
- | Environment | Component | Default | Description | Remark |
210
- |:--------------------------------------|-----------|---------|------------------------------------------------------------------------------------|--------|
211
- | `WORKFLOW_API_ENABLE_ROUTE_WORKFLOW` | API | true | A flag that enable workflow route to manage execute manually and workflow logging. | |
212
- | `WORKFLOW_API_ENABLE_ROUTE_SCHEDULE` | API | true | A flag that enable run scheduler. | |
207
+ | Environment | Component | Default | Description | Remark |
208
+ |:--------------------------------------|:-----------:|---------|------------------------------------------------------------------------------------|--------|
209
+ | `WORKFLOW_API_ENABLE_ROUTE_WORKFLOW` | API | true | A flag that enable workflow route to manage execute manually and workflow logging. | |
210
+ | `WORKFLOW_API_ENABLE_ROUTE_SCHEDULE` | API | true | A flag that enable run scheduler. | |
213
211
 
214
212
  ## :rocket: Deployment
215
213
 
@@ -217,7 +215,7 @@ This package able to run as a application service for receive manual trigger
217
215
  from the master node via RestAPI or use to be Scheduler background service
218
216
  like crontab job but via Python API.
219
217
 
220
- ### Schedule App
218
+ ### CLI
221
219
 
222
220
  ```shell
223
221
  (venv) $ ddeutil-workflow schedule
@@ -230,7 +228,7 @@ like crontab job but via Python API.
230
228
  ```
231
229
 
232
230
  > [!NOTE]
233
- > If this package already deploy, it able to use
231
+ > If this package already deploy, it able to use multiprocess;
234
232
  > `uvicorn ddeutil.workflow.api:app --host 127.0.0.1 --port 80 --workers 4`
235
233
 
236
234
  ### Docker Container
@@ -0,0 +1,25 @@
1
+ ddeutil/workflow/__about__.py,sha256=t4IIhCiioD7q4m2ksb5i3WtaGFGCRBYpETJrirYC7s8,28
2
+ ddeutil/workflow/__cron.py,sha256=uA8XcbY_GwA9rJSHaHUaXaJyGDObJN0ZeYlJSinL8y8,26880
3
+ ddeutil/workflow/__init__.py,sha256=49eGrCuchPVZKMybRouAviNhbulK_F6VwCmLm76hIss,1478
4
+ ddeutil/workflow/__types.py,sha256=Ia7f38kvL3NibwmRKi0wQ1ud_45Z-SojYGhNJwIqcu8,3713
5
+ ddeutil/workflow/cli.py,sha256=Ik14rFFxE9u20uOgCa4Vx7fNuKXVsWJg7gZb15sGans,2878
6
+ ddeutil/workflow/conf.py,sha256=YY2zZ_qv9JkTDs_73bkyrF1n1cqBINuxzMxbBjzYw-8,15361
7
+ ddeutil/workflow/cron.py,sha256=75A0hqevvouziKoLALncLJspVAeki9qCH3zniAJaxzY,7513
8
+ ddeutil/workflow/exceptions.py,sha256=P56K7VD3etGm9y-k_GXrzEyqsTCaz9EJazTIshZDf9g,943
9
+ ddeutil/workflow/job.py,sha256=cvSLMdc1sMl1MeU7so7Oe2SdRYxQwt6hm55mLV1iP-Y,24219
10
+ ddeutil/workflow/params.py,sha256=uPGkZx18E-iZ8BteqQ2ONgg0frhF3ZmP5cOyfK2j59U,5280
11
+ ddeutil/workflow/result.py,sha256=WIC8MsnfLiWNpZomT6jS4YCdYhlbIVVBjtGGe2dkoKk,3404
12
+ ddeutil/workflow/scheduler.py,sha256=_V812UlqcwfVF2Sl_45nIatMklioBXcXfGZSFoAAjwo,20452
13
+ ddeutil/workflow/stage.py,sha256=a2sngzs9DkP6GU2pgAD3QvGoijyBQTR_pOhyJUIuWAo,26692
14
+ ddeutil/workflow/utils.py,sha256=pucRnCi9aLJDptXhzzReHZd5d-S0o5oZif5tr6H4iy8,18736
15
+ ddeutil/workflow/workflow.py,sha256=AD0rs1tRT2EpvUyNVAEr2bBPgF6-KOzGmLedR3o4y0Q,42177
16
+ ddeutil/workflow/api/__init__.py,sha256=F53NMBWtb9IKaDWkPU5KvybGGfKAcbehgn6TLBwHuuM,21
17
+ ddeutil/workflow/api/api.py,sha256=Md1cz3Edc7_uz63s_L_i-R3IE4mkO3aTADrX8GOGU-Y,5644
18
+ ddeutil/workflow/api/repeat.py,sha256=zyvsrXKk-3-_N8ZRZSki0Mueshugum2jtqctEOp9QSc,4927
19
+ ddeutil/workflow/api/route.py,sha256=MQXtkF5uM_ZL1SGDuXFzgkNkbT5cpAXVNRp6mvewupM,7447
20
+ ddeutil_workflow-0.0.25.dist-info/LICENSE,sha256=nGFZ1QEhhhWeMHf9n99_fdt4vQaXS29xWKxt-OcLywk,1085
21
+ ddeutil_workflow-0.0.25.dist-info/METADATA,sha256=Rt11UMGcXyYWwXcxb-p0LgelfUFi7DaM_lQwFVX1Mtw,14641
22
+ ddeutil_workflow-0.0.25.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
23
+ ddeutil_workflow-0.0.25.dist-info/entry_points.txt,sha256=0BVOgO3LdUdXVZ-CiHHDKxzEk2c8J30jEwHeKn2YCWI,62
24
+ ddeutil_workflow-0.0.25.dist-info/top_level.txt,sha256=m9M6XeSWDwt_yMsmH6gcOjHZVK5O0-vgtNBuncHjzW4,8
25
+ ddeutil_workflow-0.0.25.dist-info/RECORD,,