OpenOrchestrator 1.2.0__py3-none-any.whl → 1.3.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.
@@ -20,6 +20,7 @@ class QueueStatus(enum.Enum):
20
20
  IN_PROGRESS = 'In Progress'
21
21
  DONE = 'Done'
22
22
  FAILED = 'Failed'
23
+ ABANDONED = 'Abandoned'
23
24
 
24
25
 
25
26
  class Base(DeclarativeBase):
@@ -21,6 +21,7 @@ class TriggerStatus(enum.Enum):
21
21
  FAILED = "Failed"
22
22
  DONE = "Done"
23
23
  PAUSED = "Paused"
24
+ PAUSING = "Pausing"
24
25
 
25
26
 
26
27
  class TriggerType(enum.Enum):
@@ -0,0 +1,18 @@
1
+ """Module for truncating a string by removing the middle part."""
2
+
3
+ import math
4
+
5
+
6
+ def truncate_message(message: str, max_length: int = 8000) -> str:
7
+ """Truncate a message to max_length characters
8
+
9
+ Args:
10
+ message: The string to truncate
11
+ max_length: Maximum allowed length, defaults to 8000
12
+
13
+ Returns:
14
+ A string with length set to max_length
15
+ """
16
+ if len(message) <= max_length:
17
+ return message
18
+ return message[:math.ceil(max_length/2)] + message[-math.floor(max_length/2):]
@@ -19,8 +19,9 @@ class DatetimeInput(ui.input):
19
19
  on_change: A callable to execute on change. Defaults to None.
20
20
  allow_empty: Whether to allow an empty input on validation. Defaults to False.
21
21
  """
22
- super().__init__(label, value=None, on_change=on_change)
22
+ super().__init__(label, on_change=lambda: self._on_change(on_change))
23
23
  self.props("clearable")
24
+ self.complete = False
24
25
 
25
26
  # Define dialog
26
27
  with ui.dialog() as self._dialog, ui.card():
@@ -38,6 +39,8 @@ class DatetimeInput(ui.input):
38
39
 
39
40
  self._define_validation(allow_empty)
40
41
 
42
+ self.complete = True
43
+
41
44
  def _define_validation(self, allow_empty: bool):
42
45
  if not allow_empty:
43
46
  self.validation = {
@@ -47,7 +50,7 @@ class DatetimeInput(ui.input):
47
50
 
48
51
  else:
49
52
  def validate(value: str):
50
- if value is None:
53
+ if not value:
51
54
  return True
52
55
 
53
56
  return self.get_datetime() is not None
@@ -73,3 +76,14 @@ class DatetimeInput(ui.input):
73
76
  value: The new datetime value.
74
77
  """
75
78
  self.value = value.strftime(self.PY_FORMAT)
79
+
80
+ def _on_change(self, func: Optional[Callable[..., Any]]) -> None:
81
+ """Wrapper for the input's on_change function.
82
+ This avoids that the on_change function is called
83
+ before the element is fully initialized.
84
+
85
+ Args:
86
+ func: The on_change function of the element.
87
+ """
88
+ if self.complete and func:
89
+ func()
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
16
16
  # pylint: disable-next=too-few-public-methods
17
17
  class ConstantPopup():
18
18
  """A popup for creating/updating queue triggers."""
19
- def __init__(self, constant_tab: ConstantTab, constant: Constant = None):
19
+ def __init__(self, constant_tab: ConstantTab, constant: Constant | None = None):
20
20
  """Create a new popup.
21
21
  If a constant is given it will be updated instead of creating a new constant.
22
22
 
@@ -41,9 +41,7 @@ class ConstantPopup():
41
41
  ui.button("Delete", color='red', on_click=self._delete_constant)
42
42
 
43
43
  self._define_validation()
44
-
45
- if constant:
46
- self._pre_populate()
44
+ self._pre_populate()
47
45
 
48
46
  def _define_validation(self):
49
47
  """Define validation rules for input elements."""
@@ -52,9 +50,10 @@ class ConstantPopup():
52
50
 
53
51
  def _pre_populate(self):
54
52
  """Pre populate the inputs with an existing constant."""
55
- self.name_input.value = self.constant.name
56
- self.name_input.disable()
57
- self.value_input.value = self.constant.value
53
+ if self.constant:
54
+ self.name_input.value = self.constant.name
55
+ self.name_input.disable()
56
+ self.value_input.value = self.constant.value
58
57
 
59
58
  def _create_constant(self):
60
59
  """Creates a new constant in the database using the data from the
@@ -89,6 +88,8 @@ class ConstantPopup():
89
88
  self.constant_tab.update()
90
89
 
91
90
  async def _delete_constant(self):
91
+ if not self.constant:
92
+ return
92
93
  if await question_popup(f"Delete constant '{self.constant.name}?", "Delete", "Cancel", color1='red'):
93
94
  db_util.delete_constant(self.constant.name)
94
95
  self.dialog.close()
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
16
16
  # pylint: disable-next=too-few-public-methods
17
17
  class CredentialPopup():
18
18
  """A popup for creating/updating queue triggers."""
19
- def __init__(self, constant_tab: ConstantTab, credential: Credential = None):
19
+ def __init__(self, constant_tab: ConstantTab, credential: Credential | None = None):
20
20
  """Create a new popup.
21
21
  If a credential is given it will be updated instead of creating a new credential.
22
22
 
@@ -43,9 +43,7 @@ class CredentialPopup():
43
43
  ui.button("Delete", color='red', on_click=self._delete_credential)
44
44
 
45
45
  self._define_validation()
46
-
47
- if credential:
48
- self._pre_populate()
46
+ self._pre_populate()
49
47
 
50
48
  def _define_validation(self):
51
49
  """Define validation functions for ui elements."""
@@ -55,9 +53,10 @@ class CredentialPopup():
55
53
 
56
54
  def _pre_populate(self):
57
55
  """Pre populate the inputs with an existing credential."""
58
- self.name_input.value = self.credential.name
59
- self.name_input.disable()
60
- self.username_input.value = self.credential.username
56
+ if self.credential:
57
+ self.name_input.value = self.credential.name
58
+ self.name_input.disable()
59
+ self.username_input.value = self.credential.username
61
60
 
62
61
  def _save_credential(self):
63
62
  """Create or update a credential in the database using the data from the UI."""
@@ -93,6 +92,8 @@ class CredentialPopup():
93
92
 
94
93
  async def _delete_credential(self):
95
94
  """Delete the selected credential."""
95
+ if not self.credential:
96
+ return
96
97
  if await question_popup(f"Delete credential '{self.credential.name}'?", "Delete", "Cancel", color1='red'):
97
98
  db_util.delete_credential(self.credential.name)
98
99
  self.dialog.close()
@@ -5,11 +5,11 @@ from typing import TYPE_CHECKING
5
5
  from datetime import datetime
6
6
 
7
7
  from nicegui import ui
8
- from croniter import croniter, CroniterBadCronError
8
+ from croniter import croniter, CroniterBadCronError # type: ignore
9
9
 
10
10
  from OpenOrchestrator.orchestrator.datetime_input import DatetimeInput
11
11
  from OpenOrchestrator.database import db_util
12
- from OpenOrchestrator.database.triggers import Trigger, TriggerStatus, TriggerType
12
+ from OpenOrchestrator.database.triggers import Trigger, TriggerStatus, TriggerType, ScheduledTrigger, SingleTrigger, QueueTrigger
13
13
  from OpenOrchestrator.orchestrator.popups import generic_popups
14
14
 
15
15
  if TYPE_CHECKING:
@@ -19,7 +19,7 @@ if TYPE_CHECKING:
19
19
  # pylint: disable-next=(too-many-instance-attributes, too-few-public-methods)
20
20
  class TriggerPopup():
21
21
  """A popup for creating/updating triggers."""
22
- def __init__(self, trigger_tab: TriggerTab, trigger_type: TriggerType, trigger: Trigger = None):
22
+ def __init__(self, trigger_tab: TriggerTab, trigger_type: TriggerType, trigger: Trigger | None = None):
23
23
  """Create a new popup.
24
24
  If a trigger is given it will be updated instead of creating a new trigger.
25
25
 
@@ -65,9 +65,7 @@ class TriggerPopup():
65
65
 
66
66
  self._disable_unused()
67
67
  self._define_validation()
68
-
69
- if trigger:
70
- self._pre_populate()
68
+ self._pre_populate()
71
69
 
72
70
  def _define_validation(self):
73
71
  self.trigger_input.validation = {"Please enter a trigger name": bool}
@@ -86,6 +84,9 @@ class TriggerPopup():
86
84
 
87
85
  def _pre_populate(self):
88
86
  """Populate the form with values from an existing trigger"""
87
+ if not self.trigger:
88
+ return
89
+
89
90
  self.trigger_input.value = self.trigger.trigger_name
90
91
  self.name_input.value = self.trigger.process_name
91
92
  self.path_input.value = self.trigger.process_path
@@ -93,13 +94,13 @@ class TriggerPopup():
93
94
  self.git_check.value = self.trigger.is_git_repo
94
95
  self.blocking_check.value = self.trigger.is_blocking
95
96
 
96
- if self.trigger_type == TriggerType.SCHEDULED:
97
+ if isinstance(self.trigger, ScheduledTrigger):
97
98
  self.cron_input.value = self.trigger.cron_expr
98
99
 
99
- if self.trigger_type in (TriggerType.SINGLE, TriggerType.SCHEDULED):
100
+ if isinstance(self.trigger, (SingleTrigger, ScheduledTrigger)):
100
101
  self.time_input.set_datetime(self.trigger.next_run)
101
102
 
102
- if self.trigger_type == TriggerType.QUEUE:
103
+ if isinstance(self.trigger, QueueTrigger):
103
104
  self.queue_input.value = self.trigger.queue_name
104
105
  self.batch_input.value = self.trigger.min_batch_size
105
106
 
@@ -154,7 +155,7 @@ class TriggerPopup():
154
155
 
155
156
  trigger_name = self.trigger_input.value
156
157
  process_name = self.name_input.value
157
- next_run = self.time_input.get_datetime()
158
+ next_run: datetime = self.time_input.get_datetime() # type: ignore
158
159
  cron_expr = self.cron_input.value
159
160
  queue_name = self.queue_input.value
160
161
  min_batch_size = self.batch_input.value
@@ -177,18 +178,17 @@ class TriggerPopup():
177
178
  # Update existing trigger
178
179
  self.trigger.trigger_name = trigger_name
179
180
  self.trigger.process_name = process_name
180
- self.trigger.next_run = next_run
181
181
  self.trigger.process_path = path
182
182
  self.trigger.process_args = args
183
183
  self.trigger.is_git_repo = is_git
184
184
  self.trigger.is_blocking = is_blocking
185
185
 
186
- if self.trigger_type == TriggerType.SINGLE:
186
+ if isinstance(self.trigger, SingleTrigger):
187
187
  self.trigger.next_run = next_run
188
- elif self.trigger_type == TriggerType.SCHEDULED:
188
+ elif isinstance(self.trigger, ScheduledTrigger):
189
189
  self.trigger.cron_expr = cron_expr
190
190
  self.trigger.next_run = next_run
191
- elif self.trigger_type == TriggerType.QUEUE:
191
+ elif isinstance(self.trigger, QueueTrigger):
192
192
  self.trigger.queue_name = queue_name
193
193
  self.trigger.min_batch_size = min_batch_size
194
194
 
@@ -199,6 +199,9 @@ class TriggerPopup():
199
199
  self.trigger_tab.update()
200
200
 
201
201
  async def _delete_trigger(self):
202
+ if not self.trigger:
203
+ return
204
+
202
205
  if await generic_popups.question_popup(f"Delete trigger '{self.trigger.trigger_name}'?", "Delete", "Cancel", color1='red'):
203
206
  db_util.delete_trigger(self.trigger.id)
204
207
  ui.notify("Trigger deleted", type='positive')
@@ -206,11 +209,20 @@ class TriggerPopup():
206
209
  self.trigger_tab.update()
207
210
 
208
211
  def _disable_trigger(self):
209
- db_util.set_trigger_status(self.trigger.id, TriggerStatus.PAUSED)
212
+ if not self.trigger:
213
+ return
214
+
215
+ if self.trigger.process_status == TriggerStatus.RUNNING:
216
+ db_util.set_trigger_status(self.trigger.id, TriggerStatus.PAUSING)
217
+ else:
218
+ db_util.set_trigger_status(self.trigger.id, TriggerStatus.PAUSED)
210
219
  ui.notify("Trigger status set to 'Paused'.", type='positive')
211
220
  self.trigger_tab.update()
212
221
 
213
222
  def _enable_trigger(self):
223
+ if not self.trigger:
224
+ return
225
+
214
226
  db_util.set_trigger_status(self.trigger.id, TriggerStatus.IDLE)
215
227
  ui.notify("Trigger status set to 'Idle'.", type='positive')
216
228
  self.trigger_tab.update()
@@ -38,7 +38,7 @@ class ConstantTab():
38
38
  """Callback for when a row is clicked in the table."""
39
39
  row = event.args[1]
40
40
  name = row['Credential Name']
41
- credential = db_util.get_credential(name)
41
+ credential = db_util.get_credential(name, False)
42
42
  CredentialPopup(self, credential)
43
43
 
44
44
  def update(self):
@@ -4,16 +4,17 @@ in Orchestrator."""
4
4
  from nicegui import ui
5
5
 
6
6
  from OpenOrchestrator.database import db_util
7
+ from OpenOrchestrator.database.logs import LogLevel
7
8
  from OpenOrchestrator.orchestrator.datetime_input import DatetimeInput
8
9
 
9
10
 
10
- COLUMNS = (
11
+ COLUMNS = [
11
12
  {'name': "Log Time", 'label': "Log Time", 'field': "Log Time", 'align': 'left', 'sortable': True},
12
13
  {'name': "Process Name", 'label': "Process Name", 'field': "Process Name", 'align': 'left'},
13
14
  {'name': "Level", 'label': "Level", 'field': "Level", 'align': 'left'},
14
15
  {'name': "Message", 'label': "Message", 'field': "Message", 'align': 'left', ':format': 'value => value.length < 100 ? value : value.substring(0, 100)+"..."'},
15
16
  {'name': "ID", 'label': "ID", 'field': "ID", 'headerClasses': 'hidden', 'classes': 'hidden'}
16
- )
17
+ ]
17
18
 
18
19
 
19
20
  # pylint: disable-next=too-few-public-methods
@@ -41,7 +42,7 @@ class LoggingTab():
41
42
  from_date = self.from_input.get_datetime()
42
43
  to_date = self.to_input.get_datetime()
43
44
  process_name = self.process_input.value if self.process_input.value != 'All' else None
44
- level = self.level_input.value if self.level_input.value != "All" else None
45
+ level = LogLevel(self.level_input.value) if self.level_input.value != "All" else None
45
46
  limit = self.limit_input.value
46
47
 
47
48
  logs = db_util.get_logs(0, limit=limit, from_date=from_date, to_date=to_date, log_level=level, process_name=process_name)
@@ -5,17 +5,19 @@ from nicegui import ui
5
5
 
6
6
  from OpenOrchestrator.database import db_util
7
7
  from OpenOrchestrator.database.queues import QueueStatus
8
+ from OpenOrchestrator.orchestrator.datetime_input import DatetimeInput
8
9
 
9
10
 
10
- QUEUE_COLUMNS = (
11
+ QUEUE_COLUMNS = [
11
12
  {'name': "Queue Name", 'label': "Queue Name", 'field': "Queue Name", 'align': 'left', 'sortable': True},
12
13
  {'name': "New", 'label': "New", 'field': "New", 'align': 'left', 'sortable': True},
13
14
  {'name': "In Progress", 'label': "In Progress", 'field': "In Progress", 'align': 'left', 'sortable': True},
14
15
  {'name': "Done", 'label': "Done", 'field': "Done", 'align': 'left', 'sortable': True},
15
- {'name': "Failed", 'label': "Failed", 'field': "Failed", 'align': 'left', 'sortable': True}
16
- )
16
+ {'name': "Failed", 'label': "Failed", 'field': "Failed", 'align': 'left', 'sortable': True},
17
+ {'name': "Abandoned", 'label': "Abandoned", 'field': "Abandoned", 'align': 'left', 'sortable': True}
18
+ ]
17
19
 
18
- ELEMENT_COLUMNS = (
20
+ ELEMENT_COLUMNS = [
19
21
  {'name': "Reference", 'label': "Reference", 'field': "Reference", 'align': 'left', 'sortable': True},
20
22
  {'name': "Status", 'label': "Status", 'field': "Status", 'align': 'left', 'sortable': True},
21
23
  {'name': "Data", 'label': "Data", 'field': "Data", 'align': 'left', 'sortable': True},
@@ -25,7 +27,7 @@ ELEMENT_COLUMNS = (
25
27
  {'name': "End Date", 'label': "End Date", 'field': "End Date", 'align': 'left', 'sortable': True},
26
28
  {'name': "Created By", 'label': "Created By", 'field': "Created By", 'align': 'left', 'sortable': True},
27
29
  {'name': "ID", 'label': "ID", 'field': "ID", 'align': 'left', 'sortable': True}
28
- )
30
+ ]
29
31
 
30
32
 
31
33
  # pylint: disable-next=too-few-public-methods
@@ -33,7 +35,7 @@ class QueueTab():
33
35
  """The 'Queues' tab object. It contains tables and buttons for dealing with queues."""
34
36
  def __init__(self, tab_name: str) -> None:
35
37
  with ui.tab_panel(tab_name):
36
- self.queue_table = ui.table(title="Queues", columns=QUEUE_COLUMNS, rows=[], row_key='Queue Name', pagination=50).classes("w-full")
38
+ self.queue_table = ui.table(title="Queues", columns=QUEUE_COLUMNS, rows=[], row_key='Queue Name', pagination={'rowsPerPage': 50, 'sortBy': 'Queue Name'}).classes("w-full")
37
39
  self.queue_table.on("rowClick", self._row_click)
38
40
 
39
41
  def update(self):
@@ -48,7 +50,8 @@ class QueueTab():
48
50
  "New": count.get(QueueStatus.NEW, 0),
49
51
  "In Progress": count.get(QueueStatus.IN_PROGRESS, 0),
50
52
  "Done": count.get(QueueStatus.DONE, 0),
51
- "Failed": count.get(QueueStatus.FAILED, 0)
53
+ "Failed": count.get(QueueStatus.FAILED, 0),
54
+ "Abandoned": count.get(QueueStatus.ABANDONED, 0),
52
55
  }
53
56
  rows.append(row)
54
57
 
@@ -68,7 +71,8 @@ class QueuePopup():
68
71
 
69
72
  with ui.dialog(value=True).props('full-width full-height') as dialog, ui.card():
70
73
  with ui.row().classes("w-full"):
71
- self._create_column_filter()
74
+ self.from_input = DatetimeInput("From Date", on_change=self._update, allow_empty=True).style('margin-left: 1rem')
75
+ self.to_input = DatetimeInput("To Date", on_change=self._update, allow_empty=True)
72
76
 
73
77
  self.limit_select = ui.select(
74
78
  options=[100, 200, 500, 1000, "All"],
@@ -76,9 +80,10 @@ class QueuePopup():
76
80
  value=100,
77
81
  on_change=self._update).classes("w-24")
78
82
 
79
- ui.switch("Dense", on_change=lambda e: self._dense_table(e.value))
80
-
81
83
  ui.space()
84
+
85
+ ui.switch("Dense", on_change=lambda e: self._dense_table(e.value))
86
+ self._create_column_filter()
82
87
  ui.button(icon='refresh', on_click=self._update)
83
88
  ui.button(icon="close", on_click=dialog.close)
84
89
  with ui.scroll_area().classes("h-full"):
@@ -111,6 +116,9 @@ class QueuePopup():
111
116
  if limit == 'All':
112
117
  limit = 1_000_000_000
113
118
 
114
- queue_elements = db_util.get_queue_elements(self.queue_name, limit=limit)
119
+ from_date = self.from_input.get_datetime()
120
+ to_date = self.to_input.get_datetime()
121
+
122
+ queue_elements = db_util.get_queue_elements(self.queue_name, limit=limit, from_date=from_date, to_date=to_date)
115
123
  rows = [element.to_row_dict() for element in queue_elements]
116
124
  self.table.update_rows(rows)
@@ -10,8 +10,8 @@ from OpenOrchestrator.common.connection_frame import ConnectionFrame
10
10
  # pylint: disable-next=too-few-public-methods
11
11
  class SettingsTab():
12
12
  """The settings tab object for Orchestrator."""
13
- def __init__(self, tab: ui.tab) -> None:
14
- with ui.tab_panel(tab):
13
+ def __init__(self, tab_name: str) -> None:
14
+ with ui.tab_panel(tab_name):
15
15
  conn_frame = ConnectionFrame()
16
16
  with ui.row().classes("w-full"):
17
17
  self.key_button = ui.button("Generate Key", on_click=conn_frame.new_key)
@@ -7,9 +7,7 @@ from OpenOrchestrator.database import db_util
7
7
  from OpenOrchestrator.database.triggers import SingleTrigger, ScheduledTrigger, QueueTrigger, TriggerType
8
8
  from OpenOrchestrator.orchestrator.popups.trigger_popup import TriggerPopup
9
9
 
10
- COLUMNS = ("Trigger Name", "Type", "Status", "Process Name", "Last Run", "Next Run", "ID")
11
-
12
- COLUMNS = (
10
+ COLUMNS = [
13
11
  {'name': "Trigger Name", 'label': "Trigger Name", 'field': "Trigger Name", 'align': 'left', 'sortable': True},
14
12
  {'name': "Type", 'label': "Type", 'field': "Type", 'align': 'left', 'sortable': True},
15
13
  {'name': "Status", 'label': "Status", 'field': "Status", 'align': 'left', 'sortable': True},
@@ -17,7 +15,7 @@ COLUMNS = (
17
15
  {'name': "Last Run", 'label': "Last Run", 'field': "Last Run", 'align': 'left', 'sortable': True},
18
16
  {'name': "Next_Run", 'label': "Next Run", 'field': "Next Run", 'align': 'left', 'sortable': True},
19
17
  {'name': "ID", 'label': "ID", 'field': "ID", 'align': 'left', 'sortable': True}
20
- )
18
+ ]
21
19
 
22
20
 
23
21
  # pylint disable-next=too-few-public-methods
@@ -30,9 +28,8 @@ class TriggerTab():
30
28
  ui.button("New Scheduled Trigger", icon="add", on_click=lambda e: TriggerPopup(self, TriggerType.SCHEDULED))
31
29
  ui.button("New Queue Trigger", icon="add", on_click=lambda e: TriggerPopup(self, TriggerType.QUEUE))
32
30
 
33
- self.trigger_table = ui.table(COLUMNS, [], title="Triggers", pagination=50, row_key='ID').classes("w-full")
31
+ self.trigger_table = ui.table(COLUMNS, [], title="Triggers", pagination={'rowsPerPage': 50, 'sortBy': 'Trigger Name'}, row_key='ID').classes("w-full")
34
32
  self.trigger_table.on('rowClick', self._row_click)
35
-
36
33
  self.add_column_colors()
37
34
 
38
35
  def _row_click(self, event):
@@ -61,7 +58,7 @@ class TriggerTab():
61
58
  "body-cell-Status",
62
59
  '''
63
60
  <q-td key="Status" :props="props">
64
- <q-badge v-if="{Running: 'green', Failed: 'red'}[props.value]" :color="{Running: 'green', Failed: 'red'}[props.value]">
61
+ <q-badge v-if="{Running: 'green', Pausing: 'orange', Failed: 'red'}[props.value]" :color="{Running: 'green', Pausing: 'orange', Failed: 'red'}[props.value]">
65
62
  {{props.value}}
66
63
  </q-badge>
67
64
  <p v-else>
@@ -3,6 +3,8 @@ The easiest way to create an OrchestratorConnection object is to call the
3
3
  class method create_connection_from_args."""
4
4
 
5
5
  import sys
6
+ from datetime import datetime
7
+
6
8
  from OpenOrchestrator.common import crypto_util
7
9
  from OpenOrchestrator.database import db_util
8
10
  from OpenOrchestrator.database.queues import QueueElement, QueueStatus
@@ -105,7 +107,7 @@ class OrchestratorConnection:
105
107
  """
106
108
  db_util.update_credential(credential_name, new_username, new_password)
107
109
 
108
- def create_queue_element(self, queue_name: str, reference: str = None, data: str = None, created_by: str = None) -> QueueElement:
110
+ def create_queue_element(self, queue_name: str, reference: str | None = None, data: str | None = None, created_by: str | None = None) -> QueueElement:
109
111
  """Adds a queue element to the given queue.
110
112
 
111
113
  Args:
@@ -119,8 +121,8 @@ class OrchestratorConnection:
119
121
  """
120
122
  return db_util.create_queue_element(queue_name, reference, data, created_by)
121
123
 
122
- def bulk_create_queue_elements(self, queue_name: str, references: tuple[str], data: tuple[str],
123
- created_by: str = None) -> None:
124
+ def bulk_create_queue_elements(self, queue_name: str, references: tuple[str | None, ...], data: tuple[str | None, ...],
125
+ created_by: str | None = None) -> None:
124
126
  """Insert multiple queue elements into a queue in an optimized manner.
125
127
  The lengths of both 'references' and 'data' must be equal to the number of elements to insert.
126
128
 
@@ -135,7 +137,7 @@ class OrchestratorConnection:
135
137
  """
136
138
  db_util.bulk_create_queue_elements(queue_name, references, data, created_by)
137
139
 
138
- def get_next_queue_element(self, queue_name: str, reference: str = None,
140
+ def get_next_queue_element(self, queue_name: str, reference: str | None = None,
139
141
  set_status: bool = True) -> QueueElement | None:
140
142
  """Gets the next queue element from the given queue that has the status 'new'.
141
143
 
@@ -150,8 +152,8 @@ class OrchestratorConnection:
150
152
  """
151
153
  return db_util.get_next_queue_element(queue_name, reference, set_status)
152
154
 
153
- def get_queue_elements(self, queue_name: str, reference: str = None, status: QueueStatus = None,
154
- offset: int = 0, limit: int = 100) -> tuple[QueueElement]:
155
+ def get_queue_elements(self, queue_name: str, reference: str | None = None, status: QueueStatus | None = None,
156
+ offset: int = 0, limit: int = 100, from_date: datetime | None = None, to_date: datetime | None = None) -> tuple[QueueElement, ...]:
155
157
  """Get multiple queue elements from a queue. The elements are ordered by created_date.
156
158
 
157
159
  Args:
@@ -164,12 +166,12 @@ class OrchestratorConnection:
164
166
  Returns:
165
167
  tuple[QueueElement]: A tuple of queue elements.
166
168
  """
167
- return db_util.get_queue_elements(queue_name, reference, status, offset, limit)
169
+ return db_util.get_queue_elements(queue_name, reference, status, from_date, to_date, offset, limit)
168
170
 
169
- def set_queue_element_status(self, element_id: str, status: QueueStatus, message: str = None) -> None:
171
+ def set_queue_element_status(self, element_id: str, status: QueueStatus, message: str | None = None) -> None:
170
172
  """Set the status of a queue element.
171
173
  If the new status is 'in progress' the start date is noted.
172
- If the new status is 'Done' or 'Failed' the end date is noted.
174
+ If the new status is 'Done', 'Failed' or 'Abandoned' the end date is noted.
173
175
 
174
176
  Args:
175
177
  element_id: The id of the queue element to change status on.
@@ -91,6 +91,6 @@ class ConnectionFrame(ttk.Frame):
91
91
  """Creates a new encryption key and inserts it
92
92
  into the key entry.
93
93
  """
94
- key = crypto_util.generate_key()
94
+ key = crypto_util.generate_key().decode()
95
95
  self.key_entry.delete(0, 'end')
96
96
  self.key_entry.insert(0, key)
@@ -16,7 +16,7 @@ class Job():
16
16
  """An object that holds information about a running job."""
17
17
  process: subprocess.Popen
18
18
  trigger: Trigger
19
- process_folder: str
19
+ process_folder: str | None
20
20
 
21
21
 
22
22
  def poll_triggers(app) -> Job | None:
@@ -180,7 +180,7 @@ def end_job(job: Job) -> None:
180
180
  """Mark a job as ended in the triggers table
181
181
  in the database.
182
182
  If it's a single trigger it's marked as 'Done'
183
- else it's marked as 'Idle'.
183
+ else it's marked as 'Idle' or 'Paused'.
184
184
 
185
185
  Args:
186
186
  job: The job whose trigger to mark as ended.
@@ -188,11 +188,12 @@ def end_job(job: Job) -> None:
188
188
  if isinstance(job.trigger, SingleTrigger):
189
189
  db_util.set_trigger_status(job.trigger.id, TriggerStatus.DONE)
190
190
 
191
- elif isinstance(job.trigger, ScheduledTrigger):
192
- db_util.set_trigger_status(job.trigger.id, TriggerStatus.IDLE)
193
-
194
- elif isinstance(job.trigger, QueueTrigger):
195
- db_util.set_trigger_status(job.trigger.id, TriggerStatus.IDLE)
191
+ elif isinstance(job.trigger, (ScheduledTrigger, QueueTrigger)):
192
+ current_status = db_util.get_trigger(job.trigger.id).process_status
193
+ if current_status == TriggerStatus.PAUSING:
194
+ db_util.set_trigger_status(job.trigger.id, TriggerStatus.PAUSED)
195
+ elif current_status == TriggerStatus.RUNNING:
196
+ db_util.set_trigger_status(job.trigger.id, TriggerStatus.IDLE)
196
197
 
197
198
  if job.process_folder:
198
199
  clear_folder(job.process_folder)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: OpenOrchestrator
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: A package containing OpenOrchestrator and OpenOrchestrator Scheduler
5
5
  Author-email: ITK Development <itk-rpa@mkb.aarhus.dk>
6
6
  Project-URL: Homepage, https://github.com/itk-dev-rpa/OpenOrchestrator
@@ -0,0 +1,39 @@
1
+ OpenOrchestrator/__init__.py,sha256=i4Ir68mBu7rrqhlEE6Qh_uyble599jaEWCEMf8g58tI,179
2
+ OpenOrchestrator/__main__.py,sha256=Oe3SbWqw3zUhpQpspUaS6mMddXxVvvV-pJbuBU_1GgA,518
3
+ OpenOrchestrator/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ OpenOrchestrator/common/connection_frame.py,sha256=NuRwbEZviOExszhspbETvHcMi7pS4cgOnwQBvAqAZc4,2838
5
+ OpenOrchestrator/common/crypto_util.py,sha256=VJ7fgxyjrW-bGQmsGVKXLUk98pcZGXG4faHtwxCWDDk,2440
6
+ OpenOrchestrator/common/datetime_util.py,sha256=4I70tz6WZGZ3xdxodJhdHJID14Y9myJTShFArOWCSpA,534
7
+ OpenOrchestrator/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ OpenOrchestrator/database/constants.py,sha256=ik6lY6IR8EOyKEg-N70X-0U654i3nFb3u-XWhqbUbJU,2404
9
+ OpenOrchestrator/database/db_util.py,sha256=2M2j47LLj6OymS1J3Zw4q6b7xfgUzEhlcoVQbTpFuiE,28768
10
+ OpenOrchestrator/database/logs.py,sha256=h2BztjmDRhj8TACYN3LK5c9hIqn5xCs9m_LDUdYYHEk,1623
11
+ OpenOrchestrator/database/queues.py,sha256=GYrTktg-RiHef-nFAXGAR9Ne0MIiRipxkAXv-CGc0_Q,2292
12
+ OpenOrchestrator/database/triggers.py,sha256=cO3-dq5-Ib4P_F6p5EgU02XgaRaiIgs6CKqsY0SnDBk,4028
13
+ OpenOrchestrator/database/truncated_string.py,sha256=v5TvMn3Sof9sG3RzHZx5_wRGBZ5IcJTsiyIO20MjehA,521
14
+ OpenOrchestrator/orchestrator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ OpenOrchestrator/orchestrator/application.py,sha256=Sb0rMrlFsvwkXuTLvBLqTUNqFSYjvdrAz3keiQYiUvY,3224
16
+ OpenOrchestrator/orchestrator/datetime_input.py,sha256=GmQ7kVe_QgOsFF7okJJshjaTQ7P-Epe3lxOJ1u21Z0k,3011
17
+ OpenOrchestrator/orchestrator/popups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ OpenOrchestrator/orchestrator/popups/constant_popup.py,sha256=58Yhyz7dW0I_1Vly0U54NEpwWHhEmwLr34z2nUpQAgs,3435
19
+ OpenOrchestrator/orchestrator/popups/credential_popup.py,sha256=4uY4mhD-EGOnOCu9GN4JPecoCbKj067kdSs1kMutpv8,3909
20
+ OpenOrchestrator/orchestrator/popups/generic_popups.py,sha256=wV5isJrwFGUBpDhDpuOzzURwU4NTp4l5mzMDg-KebKw,1061
21
+ OpenOrchestrator/orchestrator/popups/trigger_popup.py,sha256=adCKg-nnUYq_1WWdJK0SI6mZhekYuUD-0VaaQZMgK1E,10063
22
+ OpenOrchestrator/orchestrator/tabs/constants_tab.py,sha256=dSnfWbZeCndsd8vzVEyRJFEyEUPnP-RAY2YwCCi7Omw,2485
23
+ OpenOrchestrator/orchestrator/tabs/logging_tab.py,sha256=kLl2QplqGEkvWwmXlr3wfZEFf-XXqctOeQN8hcpMBQw,3589
24
+ OpenOrchestrator/orchestrator/tabs/queue_tab.py,sha256=ZjN_rWUZ99alunztxEMMI-OYIYvzMlulwmzuMoO2wlU,5820
25
+ OpenOrchestrator/orchestrator/tabs/settings_tab.py,sha256=9_B5K__GtUVWFxBD4XQ1KiGOx5a7VxiMz4WjJHoWuVE,852
26
+ OpenOrchestrator/orchestrator/tabs/trigger_tab.py,sha256=bBIyQSbfL6zBEiA8ZlhzVR3u6AdsAOlp-L5MVW4rz08,4035
27
+ OpenOrchestrator/orchestrator_connection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ OpenOrchestrator/orchestrator_connection/connection.py,sha256=whw3pbCiTwVoy4bmgdkaxBjPOpsHhGmpz7dHGlsYZjY,8923
29
+ OpenOrchestrator/scheduler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ OpenOrchestrator/scheduler/application.py,sha256=vjDzW8x0MwS9giFI6UOlhtFqUPHKbsRnrb6-HY8jWOI,1620
31
+ OpenOrchestrator/scheduler/connection_frame.py,sha256=Ufltek3jyX0Fm20A5JxSGQPuftqtY5BvodfLFOkmm8U,3378
32
+ OpenOrchestrator/scheduler/run_tab.py,sha256=QP-hoZEJAI5A-T7kV1lFCftpTm_e_R3T8I2Fg9BHxnA,5779
33
+ OpenOrchestrator/scheduler/runner.py,sha256=5F9eGlo8AsUx8WgeGxCe4k33NBPUzpsqTnVRS7_NaGQ,8348
34
+ OpenOrchestrator/scheduler/settings_tab.py,sha256=AQxt5HxPn4sLfj-6GwyAQ8ffJ35D0PHLBVoi8W3tu2A,613
35
+ OpenOrchestrator-1.3.0.dist-info/LICENSE,sha256=4-Kjm-gkbiOLCBYMzsVJZEepdsm2vk8QesNOASvi9mg,1068
36
+ OpenOrchestrator-1.3.0.dist-info/METADATA,sha256=esgDD3fPQSnTXFRZMfDlg1kq18WpFr5ihCJtjVG-bhY,1938
37
+ OpenOrchestrator-1.3.0.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
38
+ OpenOrchestrator-1.3.0.dist-info/top_level.txt,sha256=2btKMQESHuRC_ICbCjHTHH_-us2G7CyeskeaSTTL07Y,17
39
+ OpenOrchestrator-1.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (70.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5