OpenOrchestrator 1.0.2__py3-none-any.whl → 1.2.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.
- OpenOrchestrator/__init__.py +7 -0
- OpenOrchestrator/__main__.py +2 -0
- OpenOrchestrator/common/connection_frame.py +35 -49
- OpenOrchestrator/common/crypto_util.py +4 -4
- OpenOrchestrator/common/datetime_util.py +20 -0
- OpenOrchestrator/database/constants.py +25 -19
- OpenOrchestrator/database/db_util.py +77 -30
- OpenOrchestrator/database/logs.py +13 -0
- OpenOrchestrator/database/queues.py +17 -0
- OpenOrchestrator/database/triggers.py +25 -56
- OpenOrchestrator/orchestrator/application.py +87 -34
- OpenOrchestrator/orchestrator/datetime_input.py +75 -0
- OpenOrchestrator/orchestrator/popups/constant_popup.py +87 -69
- OpenOrchestrator/orchestrator/popups/credential_popup.py +92 -82
- OpenOrchestrator/orchestrator/popups/generic_popups.py +27 -0
- OpenOrchestrator/orchestrator/popups/trigger_popup.py +216 -0
- OpenOrchestrator/orchestrator/tabs/constants_tab.py +52 -0
- OpenOrchestrator/orchestrator/tabs/logging_tab.py +70 -0
- OpenOrchestrator/orchestrator/tabs/queue_tab.py +116 -0
- OpenOrchestrator/orchestrator/tabs/settings_tab.py +22 -0
- OpenOrchestrator/orchestrator/tabs/trigger_tab.py +87 -0
- OpenOrchestrator/scheduler/application.py +3 -3
- OpenOrchestrator/scheduler/connection_frame.py +96 -0
- OpenOrchestrator/scheduler/run_tab.py +87 -80
- OpenOrchestrator/scheduler/runner.py +33 -25
- OpenOrchestrator/scheduler/settings_tab.py +2 -1
- {OpenOrchestrator-1.0.2.dist-info → OpenOrchestrator-1.2.0.dist-info}/METADATA +2 -2
- OpenOrchestrator-1.2.0.dist-info/RECORD +38 -0
- OpenOrchestrator/orchestrator/constants_tab.py +0 -169
- OpenOrchestrator/orchestrator/logging_tab.py +0 -221
- OpenOrchestrator/orchestrator/popups/queue_trigger_popup.py +0 -129
- OpenOrchestrator/orchestrator/popups/scheduled_trigger_popup.py +0 -129
- OpenOrchestrator/orchestrator/popups/single_trigger_popup.py +0 -134
- OpenOrchestrator/orchestrator/settings_tab.py +0 -31
- OpenOrchestrator/orchestrator/table_util.py +0 -76
- OpenOrchestrator/orchestrator/trigger_tab.py +0 -231
- OpenOrchestrator-1.0.2.dist-info/RECORD +0 -36
- {OpenOrchestrator-1.0.2.dist-info → OpenOrchestrator-1.2.0.dist-info}/LICENSE +0 -0
- {OpenOrchestrator-1.0.2.dist-info → OpenOrchestrator-1.2.0.dist-info}/WHEEL +0 -0
- {OpenOrchestrator-1.0.2.dist-info → OpenOrchestrator-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
"""This module is responsible for the layout and functionality of the Constants tab
|
|
2
|
-
in Orchestrator."""
|
|
3
|
-
|
|
4
|
-
import tkinter
|
|
5
|
-
from tkinter import ttk, messagebox
|
|
6
|
-
|
|
7
|
-
from OpenOrchestrator.database import db_util
|
|
8
|
-
from OpenOrchestrator.orchestrator import table_util
|
|
9
|
-
from OpenOrchestrator.orchestrator.popups import constant_popup, credential_popup
|
|
10
|
-
|
|
11
|
-
def create_tab(parent: ttk.Notebook) -> ttk.Frame:
|
|
12
|
-
"""Create a new Constants tab object.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
parent: The ttk.Notebook object that this tab is a child of.
|
|
16
|
-
|
|
17
|
-
Returns:
|
|
18
|
-
ttk.Frame: The created tab object as a ttk.Frame.
|
|
19
|
-
"""
|
|
20
|
-
tab = ttk.Frame(parent)
|
|
21
|
-
tab.pack(fill='both', expand=True)
|
|
22
|
-
|
|
23
|
-
tab.columnconfigure(0, weight=1)
|
|
24
|
-
tab.rowconfigure((0,2), weight=1, uniform='a')
|
|
25
|
-
tab.rowconfigure((1,3), weight=6, uniform='a')
|
|
26
|
-
tab.rowconfigure((4, 5), weight=1, uniform='a')
|
|
27
|
-
|
|
28
|
-
#Constants table
|
|
29
|
-
ttk.Label(tab, text="Constants").grid(row=0, column=0)
|
|
30
|
-
const_table_frame = ttk.Frame(tab)
|
|
31
|
-
const_table_frame.grid(row=1, column=0, sticky='nsew')
|
|
32
|
-
const_table = table_util.create_table(const_table_frame, ('Name', 'Value', 'Changed at'))
|
|
33
|
-
|
|
34
|
-
#Credentials table
|
|
35
|
-
ttk.Label(tab, text="Credentials").grid(row=2, column=0)
|
|
36
|
-
cred_table_frame = ttk.Frame(tab)
|
|
37
|
-
cred_table_frame.grid(row=3, column=0, sticky='nsew')
|
|
38
|
-
cred_table = table_util.create_table(cred_table_frame, ('Name', 'Username', 'Password', 'Changed at'))
|
|
39
|
-
|
|
40
|
-
# Controls 1
|
|
41
|
-
controls_frame = ttk.Frame(tab)
|
|
42
|
-
controls_frame.grid(row=4, column=0)
|
|
43
|
-
def update_command():
|
|
44
|
-
update_tables(const_table, cred_table)
|
|
45
|
-
|
|
46
|
-
update_button = ttk.Button(controls_frame, text='Update', command=update_command)
|
|
47
|
-
update_button.pack(side='left')
|
|
48
|
-
|
|
49
|
-
delete_button = ttk.Button(controls_frame, text='Delete', command=lambda: delete_selected(const_table, cred_table))
|
|
50
|
-
delete_button.pack(side='left')
|
|
51
|
-
|
|
52
|
-
# Controls 2
|
|
53
|
-
controls_frame2 = ttk.Frame(tab)
|
|
54
|
-
controls_frame2.grid(row=5, column=0)
|
|
55
|
-
|
|
56
|
-
ttk.Button(controls_frame2, text='New constant', command=lambda: show_constant_popup(update_command)).pack(side='left')
|
|
57
|
-
ttk.Button(controls_frame2, text='New credential', command=lambda: show_credential_popup(update_command)).pack(side='left')
|
|
58
|
-
|
|
59
|
-
# Bindings
|
|
60
|
-
const_table.bind('<FocusIn>', lambda e: table_util.deselect_tables(cred_table))
|
|
61
|
-
const_table.bind('<Double-1>', lambda e: double_click_constant_table(e, const_table, update_command))
|
|
62
|
-
|
|
63
|
-
cred_table.bind('<FocusIn>', lambda e: table_util.deselect_tables(const_table))
|
|
64
|
-
cred_table.bind('<Double-1>', lambda e: double_click_credential_table(e, cred_table, update_command))
|
|
65
|
-
|
|
66
|
-
tab.bind_all('<Delete>', lambda e: delete_selected(const_table, cred_table))
|
|
67
|
-
|
|
68
|
-
return tab
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def update_tables(const_table: ttk.Treeview, cred_table: ttk.Treeview) -> None:
|
|
72
|
-
"""Update the constant and credential tables with
|
|
73
|
-
new values from the database.
|
|
74
|
-
|
|
75
|
-
Args:
|
|
76
|
-
const_table: The constants table object.
|
|
77
|
-
cred_table: The credentials table object.
|
|
78
|
-
"""
|
|
79
|
-
|
|
80
|
-
# Convert ORM objects to lists of values
|
|
81
|
-
const_list = [c.to_tuple() for c in db_util.get_constants()]
|
|
82
|
-
cred_list = [c.to_tuple() for c in db_util.get_credentials()]
|
|
83
|
-
|
|
84
|
-
table_util.update_table(const_table, const_list)
|
|
85
|
-
table_util.update_table(cred_table, cred_list)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def delete_selected(const_table: ttk.Treeview, cred_table: ttk.Treeview) -> None:
|
|
89
|
-
"""Deletes the currently selected constant or credential
|
|
90
|
-
from the database. Updates the tables afterwards.
|
|
91
|
-
|
|
92
|
-
Args:
|
|
93
|
-
const_table: The constants table object.
|
|
94
|
-
cred_table: The credentials table object.
|
|
95
|
-
"""
|
|
96
|
-
if const_table.selection():
|
|
97
|
-
name = const_table.item(const_table.selection()[0], 'values')[0]
|
|
98
|
-
if not messagebox.askyesno('Delete constant?', f"Are you sure you want to delete constant '{name}'?"):
|
|
99
|
-
return
|
|
100
|
-
db_util.delete_constant(name)
|
|
101
|
-
|
|
102
|
-
elif cred_table.selection():
|
|
103
|
-
name = cred_table.item(cred_table.selection()[0], 'values')[0]
|
|
104
|
-
if not messagebox.askyesno('Delete credential?', f"Are you sure you want to delete credential '{name}'?"):
|
|
105
|
-
return
|
|
106
|
-
db_util.delete_credential(name)
|
|
107
|
-
|
|
108
|
-
else:
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
update_tables(const_table, cred_table)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def show_constant_popup(on_close: callable, name: str=None, value: str=None) -> None:
|
|
115
|
-
"""Shows the new constant popup.
|
|
116
|
-
Optionally populates the entry widgets in the popup with 'name' and 'value'.
|
|
117
|
-
Binds a callable to the popup's on_close event.
|
|
118
|
-
|
|
119
|
-
Args:
|
|
120
|
-
on_close: A function to be called when the popup closes.
|
|
121
|
-
name (optional): A value to pre-populate the name entry widget with. Defaults to None.
|
|
122
|
-
value (optional): A value to pre-populate the value entry widget with. Defaults to None.
|
|
123
|
-
"""
|
|
124
|
-
popup = constant_popup.show_popup(name, value)
|
|
125
|
-
popup.bind('<Destroy>', lambda e: on_close() if e.widget == popup else ...)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def double_click_constant_table(event: tkinter.Event, const_table:ttk.Treeview, on_close:callable) -> None:
|
|
129
|
-
"""This function is called whenever the constants table is double clicked.
|
|
130
|
-
It opens the new constant popup with pre-populated values.
|
|
131
|
-
|
|
132
|
-
Args:
|
|
133
|
-
event: The event of the double click.
|
|
134
|
-
const_table: The constants table.
|
|
135
|
-
on_close: A function to be called when the popup is closed.
|
|
136
|
-
"""
|
|
137
|
-
row = const_table.identify_row(event.y)
|
|
138
|
-
if row:
|
|
139
|
-
name, value, *_ = const_table.item(row, 'values')
|
|
140
|
-
show_constant_popup(on_close, name, value)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def double_click_credential_table(event: tkinter.Event, cred_table: ttk.Treeview, on_close: callable) -> None:
|
|
144
|
-
"""This function is called whenever the credentials table is double clicked.
|
|
145
|
-
It opens the new credential popup with pre-populated values.
|
|
146
|
-
|
|
147
|
-
Args:
|
|
148
|
-
event: The event of the double click.
|
|
149
|
-
cred_table: The credentials table.
|
|
150
|
-
on_close: A function to be called when the popup is closed.
|
|
151
|
-
"""
|
|
152
|
-
row = cred_table.identify_row(event.y)
|
|
153
|
-
if row:
|
|
154
|
-
name, value, *_ = cred_table.item(row, 'values')
|
|
155
|
-
show_credential_popup(on_close, name, value)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def show_credential_popup(on_close: callable, name: str=None, username: str=None) -> None:
|
|
159
|
-
"""Shows the new credential popup.
|
|
160
|
-
Optionally populates the entry widgets in the popup with 'name' and 'username'.
|
|
161
|
-
Binds a callable to the popup's on_close event.
|
|
162
|
-
|
|
163
|
-
Args:
|
|
164
|
-
on_close: A function to be called when the popup closes.
|
|
165
|
-
name (optional): A value to pre-populate the name entry widget with. Defaults to None.
|
|
166
|
-
username (optional): A value to pre-populate the username entry widget with. Defaults to None.
|
|
167
|
-
"""
|
|
168
|
-
popup = credential_popup.show_popup(name, username)
|
|
169
|
-
popup.bind('<Destroy>', lambda e: on_close() if e.widget == popup else ...)
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
"""This module is responsible for the layout and functionality of the Logging tab
|
|
2
|
-
in Orchestrator."""
|
|
3
|
-
|
|
4
|
-
import tkinter
|
|
5
|
-
from tkinter import ttk, font, messagebox
|
|
6
|
-
from datetime import datetime
|
|
7
|
-
from ast import literal_eval
|
|
8
|
-
|
|
9
|
-
from OpenOrchestrator.database import db_util
|
|
10
|
-
from OpenOrchestrator.orchestrator import table_util
|
|
11
|
-
|
|
12
|
-
def create_tab(parent):
|
|
13
|
-
"""Create a new Logging tab object.
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
parent: The ttk.Notebook object that this tab is a child of.
|
|
17
|
-
|
|
18
|
-
Returns:
|
|
19
|
-
ttk.Frame: The created tab object as a ttk.Frame.
|
|
20
|
-
"""
|
|
21
|
-
tab = ttk.Frame(parent)
|
|
22
|
-
tab.pack(fill='both', expand=True)
|
|
23
|
-
|
|
24
|
-
tab.columnconfigure(0, weight=1)
|
|
25
|
-
tab.rowconfigure(0, weight=2, uniform='a')
|
|
26
|
-
tab.rowconfigure(1, weight=1, uniform='a')
|
|
27
|
-
|
|
28
|
-
#Table
|
|
29
|
-
table_frame = ttk.Frame(tab)
|
|
30
|
-
table_frame.grid(row=0, column=0, sticky="nsew")
|
|
31
|
-
|
|
32
|
-
table = table_util.create_table(table_frame, ("Time", "Level", "Process", "Message"))
|
|
33
|
-
table.column("Time", width=120, stretch=False)
|
|
34
|
-
table.column("Level", width=50, stretch=False)
|
|
35
|
-
table.column("Process", width=150, stretch=False)
|
|
36
|
-
table.bind("<Double-1>", lambda e: double_click_log(table))
|
|
37
|
-
|
|
38
|
-
# Filters
|
|
39
|
-
filter_frame = ttk.Frame(tab)
|
|
40
|
-
filter_frame.grid(row=1, column=0, sticky='nsew')
|
|
41
|
-
|
|
42
|
-
# Date filters
|
|
43
|
-
ttk.Label(filter_frame, text="Date from:").grid(row=0, column=0, sticky='w')
|
|
44
|
-
from_date_entry = ttk.Entry(filter_frame, width=21, validate='key')
|
|
45
|
-
reg = tab.register(validate_date(from_date_entry))
|
|
46
|
-
from_date_entry.configure(validatecommand=(reg, '%P'))
|
|
47
|
-
from_date_entry.insert(0, 'dd-mm-yyyy hh:mm:ss')
|
|
48
|
-
from_date_entry.grid(row=0, column=1)
|
|
49
|
-
|
|
50
|
-
ttk.Label(filter_frame, text='Date to:').grid(row=0, column=2)
|
|
51
|
-
to_date_entry = ttk.Entry(filter_frame, width=21, validate='key')
|
|
52
|
-
reg = tab.register(validate_date(to_date_entry))
|
|
53
|
-
to_date_entry.configure(validatecommand=(reg, '%P'))
|
|
54
|
-
to_date_entry.insert(0, 'dd-mm-yyyy hh:mm:ss')
|
|
55
|
-
to_date_entry.grid(row=0, column=3)
|
|
56
|
-
|
|
57
|
-
# Process filter
|
|
58
|
-
ttk.Label(filter_frame, text="Process name:").grid(row=1, column=0)
|
|
59
|
-
ttk.Style().configure("TMenubutton", background='white')
|
|
60
|
-
process_options_var = tkinter.StringVar()
|
|
61
|
-
process_options = ttk.OptionMenu(filter_frame, process_options_var, "", *("", "hej1", "med2", "dig3","hej4", "med5", "dig6"))
|
|
62
|
-
process_options['menu'].delete(0, 'end')
|
|
63
|
-
process_options.grid(row=1, column=1, columnspan=3, sticky='ew', pady=2)
|
|
64
|
-
|
|
65
|
-
# Level filter
|
|
66
|
-
ttk.Label(filter_frame, text="Log level:").grid(row=2, column=0)
|
|
67
|
-
log_level = tkinter.StringVar()
|
|
68
|
-
ttk.OptionMenu(filter_frame, log_level, "", *("", "Trace", "Info", "Error")).grid(row=2, column=1, sticky='ew', pady=2)
|
|
69
|
-
|
|
70
|
-
#Buttons
|
|
71
|
-
def update_command():
|
|
72
|
-
update(table, from_date_entry, to_date_entry, process_options, process_options_var, log_level)
|
|
73
|
-
update_button = ttk.Button(filter_frame, text="Update", command=update_command)
|
|
74
|
-
update_button.grid(row=4, column=0)
|
|
75
|
-
|
|
76
|
-
return tab
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def validate_date(entry: ttk.Entry) -> callable:
|
|
80
|
-
"""Creates a validator function to validate if
|
|
81
|
-
an datetime entered in the given entry is valid.
|
|
82
|
-
Changes the color of the Entry widget according
|
|
83
|
-
to the validity of the datetime.
|
|
84
|
-
|
|
85
|
-
Args:
|
|
86
|
-
entry: The entry widget to validate on.
|
|
87
|
-
|
|
88
|
-
Returns:
|
|
89
|
-
callable: The validator function.
|
|
90
|
-
"""
|
|
91
|
-
def inner(text: str):
|
|
92
|
-
if parse_date(text) is not None:
|
|
93
|
-
entry.configure(foreground='black')
|
|
94
|
-
else:
|
|
95
|
-
entry.configure(foreground='red')
|
|
96
|
-
return True
|
|
97
|
-
return inner
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def resize_table(table: ttk.Treeview) -> None:
|
|
101
|
-
"""Resizes the 'Message' column of the table to match the longest string
|
|
102
|
-
in the column.
|
|
103
|
-
|
|
104
|
-
Args:
|
|
105
|
-
table (ttk.Treeview): The table object.
|
|
106
|
-
"""
|
|
107
|
-
max_length = 0
|
|
108
|
-
default_font = font.nametofont("TkDefaultFont")
|
|
109
|
-
|
|
110
|
-
for row in table.get_children():
|
|
111
|
-
item = table.item(row)
|
|
112
|
-
message = item['values'][-1]
|
|
113
|
-
max_length = max(max_length, default_font.measure(message))
|
|
114
|
-
|
|
115
|
-
table.column("Message", width=max_length+20, stretch=False)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def update(table: ttk.Treeview, from_date_entry: ttk.Entry, to_date_entry: ttk.Entry,
|
|
119
|
-
process_options:ttk.OptionMenu, process_options_var:tkinter.StringVar,
|
|
120
|
-
log_level: tkinter.StringVar) -> None:
|
|
121
|
-
"""Updates the logs table with new values from the database
|
|
122
|
-
using the filter options given in the UI.
|
|
123
|
-
Updates the filter option menus with values from the database.
|
|
124
|
-
|
|
125
|
-
Args:
|
|
126
|
-
table: The logs table to update.
|
|
127
|
-
from_date_entry: The entry with the from date.
|
|
128
|
-
to_date_entry: The entry with the to date.
|
|
129
|
-
process_options: The options menu with process names.
|
|
130
|
-
process_options_var: The StringVar connected to process_options.
|
|
131
|
-
log_level: The log level to filter on.
|
|
132
|
-
"""
|
|
133
|
-
offset = 0
|
|
134
|
-
limit = 100
|
|
135
|
-
|
|
136
|
-
from_date = parse_date(from_date_entry.get()) or datetime(1900, 1, 1)
|
|
137
|
-
to_date = parse_date(to_date_entry.get()) or datetime(2100, 12, 31)
|
|
138
|
-
|
|
139
|
-
process_name = process_options_var.get()
|
|
140
|
-
log_level = log_level.get()
|
|
141
|
-
|
|
142
|
-
logs = db_util.get_logs(offset, limit, from_date, to_date, process_name, log_level)
|
|
143
|
-
|
|
144
|
-
# Convert log objects to lists of strings
|
|
145
|
-
logs_list = [
|
|
146
|
-
[
|
|
147
|
-
l.log_time.strftime('%d-%m-%Y %H:%M:%S'),
|
|
148
|
-
l.log_level.value,
|
|
149
|
-
l.process_name,
|
|
150
|
-
repr(l.log_message) # Convert message to single line text
|
|
151
|
-
]
|
|
152
|
-
for l in logs
|
|
153
|
-
]
|
|
154
|
-
|
|
155
|
-
table_util.update_table(table, logs_list)
|
|
156
|
-
resize_table(table)
|
|
157
|
-
|
|
158
|
-
# Update process_name OptionMenu
|
|
159
|
-
process_names = list(db_util.get_unique_log_process_names())
|
|
160
|
-
process_names.insert(0, "")
|
|
161
|
-
replace_options(process_options, process_options_var, process_names)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def parse_date(date_str: str) -> datetime | None:
|
|
165
|
-
"""Tries to parse a string to a datetime object with
|
|
166
|
-
a selection of different formats.
|
|
167
|
-
|
|
168
|
-
Args:
|
|
169
|
-
date_str: The string to parse.
|
|
170
|
-
|
|
171
|
-
Returns:
|
|
172
|
-
datetime: The parsed datetime if possible else None.
|
|
173
|
-
"""
|
|
174
|
-
formats = (
|
|
175
|
-
'%d-%m-%Y %H:%M:%S',
|
|
176
|
-
'%d-%m-%Y %H:%M',
|
|
177
|
-
'%d-%m-%Y'
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
for format_ in formats:
|
|
181
|
-
try:
|
|
182
|
-
return datetime.strptime(date_str, format_)
|
|
183
|
-
except ValueError:
|
|
184
|
-
...
|
|
185
|
-
|
|
186
|
-
return None
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def double_click_log(table: ttk.Treeview) -> None:
|
|
190
|
-
"""Handles double clicks on the logs table.
|
|
191
|
-
Opens a popup with the selected log's text.
|
|
192
|
-
|
|
193
|
-
Args:
|
|
194
|
-
table (ttk.Treeview): _description_
|
|
195
|
-
"""
|
|
196
|
-
item = table.selection()
|
|
197
|
-
|
|
198
|
-
if item:
|
|
199
|
-
values = list(table.item(item[0], 'values'))
|
|
200
|
-
|
|
201
|
-
# Convert message back to multiline text
|
|
202
|
-
values[-1] = literal_eval(values[-1])
|
|
203
|
-
|
|
204
|
-
text = "\n".join(values)
|
|
205
|
-
messagebox.showinfo("Info", text)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
def replace_options(option_menu: ttk.OptionMenu, option_menu_var: tkinter.StringVar, new_options: tuple[str]) -> None:
|
|
209
|
-
"""Replaces the options in a ttk.OptionsMenu.
|
|
210
|
-
|
|
211
|
-
Args:
|
|
212
|
-
option_menu: The OptionsMenu whose options to replace.
|
|
213
|
-
option_menu_var: The StringVar connected to the OptionsMenu.
|
|
214
|
-
new_options (tuple[str]): _description_
|
|
215
|
-
"""
|
|
216
|
-
selected = option_menu_var.get()
|
|
217
|
-
|
|
218
|
-
option_menu.set_menu(None, *new_options)
|
|
219
|
-
|
|
220
|
-
if selected in new_options:
|
|
221
|
-
option_menu_var.set(selected)
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
"""This module is responsible for the layout and functionality of the 'New Queue Trigger' popup."""
|
|
2
|
-
|
|
3
|
-
# Disable pylint duplicate code error since it
|
|
4
|
-
# mostly reacts to the layout code being similar.
|
|
5
|
-
# pylint: disable=R0801
|
|
6
|
-
|
|
7
|
-
import tkinter
|
|
8
|
-
from tkinter import ttk, messagebox
|
|
9
|
-
|
|
10
|
-
from OpenOrchestrator.database import db_util
|
|
11
|
-
from OpenOrchestrator.database.triggers import QueueTrigger
|
|
12
|
-
|
|
13
|
-
# pylint: disable-next=too-many-instance-attributes
|
|
14
|
-
class QueueTriggerPopup(tkinter.Toplevel):
|
|
15
|
-
"""A popup for creating/updating scheduled triggers."""
|
|
16
|
-
def __init__(self, trigger: QueueTrigger = None):
|
|
17
|
-
"""Create a new popup.
|
|
18
|
-
If a trigger is given it will be updated instead of creating a new trigger.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
trigger: The Scheduled Trigger to update if any.
|
|
22
|
-
"""
|
|
23
|
-
self.trigger = trigger
|
|
24
|
-
title = 'Update Queue Trigger' if trigger else 'New Queue Trigger'
|
|
25
|
-
|
|
26
|
-
super().__init__()
|
|
27
|
-
self.grab_set()
|
|
28
|
-
self.title(title)
|
|
29
|
-
self.geometry("300x350")
|
|
30
|
-
|
|
31
|
-
ttk.Label(self, text="Trigger Name:").pack()
|
|
32
|
-
self.trigger_entry = ttk.Entry(self)
|
|
33
|
-
self.trigger_entry.pack()
|
|
34
|
-
|
|
35
|
-
ttk.Label(self, text="Process Name:").pack()
|
|
36
|
-
self.name_entry = ttk.Entry(self)
|
|
37
|
-
self.name_entry.pack()
|
|
38
|
-
|
|
39
|
-
ttk.Label(self, text="Queue Name:").pack()
|
|
40
|
-
self.queue_entry = ttk.Entry(self)
|
|
41
|
-
self.queue_entry.pack()
|
|
42
|
-
|
|
43
|
-
ttk.Label(self, text="Min batch size:").pack()
|
|
44
|
-
self.size_entry = ttk.Entry(self)
|
|
45
|
-
self.size_entry.pack()
|
|
46
|
-
|
|
47
|
-
ttk.Label(self, text="Process Path:").pack()
|
|
48
|
-
self.path_entry = ttk.Entry(self)
|
|
49
|
-
self.path_entry.pack()
|
|
50
|
-
|
|
51
|
-
ttk.Label(self, text="Arguments:").pack()
|
|
52
|
-
self.args_entry = ttk.Entry(self)
|
|
53
|
-
self.args_entry.pack()
|
|
54
|
-
|
|
55
|
-
self.git_check = tkinter.IntVar()
|
|
56
|
-
ttk.Checkbutton(self, text="Is Git Repo?", variable=self.git_check).pack()
|
|
57
|
-
|
|
58
|
-
self.blocking_check = tkinter.IntVar()
|
|
59
|
-
ttk.Checkbutton(self, text="Is Blocking?", variable=self.blocking_check).pack()
|
|
60
|
-
|
|
61
|
-
button_text = 'Update' if trigger else 'Create'
|
|
62
|
-
|
|
63
|
-
ttk.Button(self, text=button_text, command=self.create_trigger).pack()
|
|
64
|
-
ttk.Button(self, text='Cancel', command=self.destroy).pack()
|
|
65
|
-
|
|
66
|
-
if self.trigger is not None:
|
|
67
|
-
self.pre_populate()
|
|
68
|
-
|
|
69
|
-
def pre_populate(self):
|
|
70
|
-
"""Populate the form with values from an existing trigger"""
|
|
71
|
-
self.trigger_entry.insert(0, self.trigger.trigger_name)
|
|
72
|
-
self.name_entry.insert(0, self.trigger.process_name)
|
|
73
|
-
self.queue_entry.insert(0, self.trigger.queue_name)
|
|
74
|
-
self.size_entry.insert(0, self.trigger.min_batch_size)
|
|
75
|
-
self.path_entry.insert(0, self.trigger.process_path)
|
|
76
|
-
self.args_entry.insert(0, self.trigger.process_args)
|
|
77
|
-
self.git_check.set(self.trigger.is_git_repo)
|
|
78
|
-
self.blocking_check.set(self.trigger.is_blocking)
|
|
79
|
-
|
|
80
|
-
def create_trigger(self):
|
|
81
|
-
"""Creates a new scheduled trigger in the database using the data entered in the UI.
|
|
82
|
-
If an existing trigger was given when creating the popup it is updated instead.
|
|
83
|
-
"""
|
|
84
|
-
trigger_name = self.trigger_entry.get()
|
|
85
|
-
process_name = self.name_entry.get()
|
|
86
|
-
queue_name = self.queue_entry.get()
|
|
87
|
-
min_batch_size = self.size_entry.get()
|
|
88
|
-
path = self.path_entry.get()
|
|
89
|
-
args = self.args_entry.get()
|
|
90
|
-
is_git = self.git_check.get()
|
|
91
|
-
is_blocking = self.blocking_check.get()
|
|
92
|
-
|
|
93
|
-
if not trigger_name:
|
|
94
|
-
messagebox.showerror('Error', 'Please enter a trigger name')
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
if not process_name:
|
|
98
|
-
messagebox.showerror('Error', 'Please enter a process name')
|
|
99
|
-
return
|
|
100
|
-
|
|
101
|
-
if not queue_name:
|
|
102
|
-
messagebox.showerror("Error", "Please enter a queue name")
|
|
103
|
-
|
|
104
|
-
try:
|
|
105
|
-
min_batch_size = int(min_batch_size)
|
|
106
|
-
except ValueError:
|
|
107
|
-
messagebox.showerror("Error", "Please enter a integer value for min batch size")
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
if not path:
|
|
111
|
-
messagebox.showerror('Error', 'Please enter a process path')
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
if self.trigger is None:
|
|
115
|
-
# Create new trigger in database
|
|
116
|
-
db_util.create_queue_trigger(trigger_name, process_name, queue_name, path, args, is_git, is_blocking, min_batch_size)
|
|
117
|
-
else:
|
|
118
|
-
# Update existing trigger
|
|
119
|
-
self.trigger.trigger_name = trigger_name
|
|
120
|
-
self.trigger.process_name = process_name
|
|
121
|
-
self.trigger.queue_name = queue_name
|
|
122
|
-
self.trigger.min_batch_size = min_batch_size
|
|
123
|
-
self.trigger.process_path = path
|
|
124
|
-
self.trigger.process_args = args
|
|
125
|
-
self.trigger.is_git_repo = is_git
|
|
126
|
-
self.trigger.is_blocking = is_blocking
|
|
127
|
-
db_util.update_trigger(self.trigger)
|
|
128
|
-
|
|
129
|
-
self.destroy()
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
"""This module is responsible for the layout and functionality of the 'New Scheduled Trigger' popup."""
|
|
2
|
-
|
|
3
|
-
# Disable pylint duplicate code error since it
|
|
4
|
-
# mostly reacts to the layout code being similar.
|
|
5
|
-
# pylint: disable=duplicate-code
|
|
6
|
-
|
|
7
|
-
import tkinter
|
|
8
|
-
from tkinter import ttk, messagebox
|
|
9
|
-
from datetime import datetime
|
|
10
|
-
import webbrowser
|
|
11
|
-
|
|
12
|
-
import croniter
|
|
13
|
-
|
|
14
|
-
from OpenOrchestrator.database import db_util
|
|
15
|
-
from OpenOrchestrator.database.triggers import ScheduledTrigger
|
|
16
|
-
|
|
17
|
-
# pylint: disable-next=too-many-instance-attributes
|
|
18
|
-
class ScheduledTriggerPopup(tkinter.Toplevel):
|
|
19
|
-
"""A popup for creating/updating scheduled triggers."""
|
|
20
|
-
def __init__(self, trigger: ScheduledTrigger = None):
|
|
21
|
-
"""Create a new popup.
|
|
22
|
-
If a trigger is given it will be updated instead of creating a new trigger.
|
|
23
|
-
|
|
24
|
-
Args:
|
|
25
|
-
trigger: The Scheduled Trigger to update if any.
|
|
26
|
-
"""
|
|
27
|
-
self.trigger = trigger
|
|
28
|
-
title = 'Update Scheduled Trigger' if trigger else 'New Scheduled Trigger'
|
|
29
|
-
|
|
30
|
-
super().__init__()
|
|
31
|
-
self.grab_set()
|
|
32
|
-
self.title(title)
|
|
33
|
-
self.geometry("300x350")
|
|
34
|
-
|
|
35
|
-
ttk.Label(self, text="Trigger Name:").pack()
|
|
36
|
-
self.trigger_entry = ttk.Entry(self)
|
|
37
|
-
self.trigger_entry.pack()
|
|
38
|
-
|
|
39
|
-
ttk.Label(self, text="Process Name:").pack()
|
|
40
|
-
self.name_entry = ttk.Entry(self)
|
|
41
|
-
self.name_entry.pack()
|
|
42
|
-
|
|
43
|
-
ttk.Label(self, text="Cron Expression:").pack()
|
|
44
|
-
self.cron_entry = ttk.Entry(self)
|
|
45
|
-
self.cron_entry.pack()
|
|
46
|
-
|
|
47
|
-
help_label = ttk.Label(self, text='Cron Help', cursor='hand2', foreground='blue')
|
|
48
|
-
help_label.bind('<Button-1>', lambda e: webbrowser.open('crontab.guru'))
|
|
49
|
-
help_label.pack()
|
|
50
|
-
|
|
51
|
-
ttk.Label(self, text="Process Path:").pack()
|
|
52
|
-
self.path_entry = ttk.Entry(self)
|
|
53
|
-
self.path_entry.pack()
|
|
54
|
-
|
|
55
|
-
ttk.Label(self, text="Arguments:").pack()
|
|
56
|
-
self.args_entry = ttk.Entry(self)
|
|
57
|
-
self.args_entry.pack()
|
|
58
|
-
|
|
59
|
-
self.git_check = tkinter.IntVar()
|
|
60
|
-
ttk.Checkbutton(self, text="Is Git Repo?", variable=self.git_check).pack()
|
|
61
|
-
|
|
62
|
-
self.blocking_check = tkinter.IntVar()
|
|
63
|
-
ttk.Checkbutton(self, text="Is Blocking?", variable=self.blocking_check).pack()
|
|
64
|
-
|
|
65
|
-
button_text = 'Update' if trigger else 'Create'
|
|
66
|
-
|
|
67
|
-
ttk.Button(self, text=button_text, command=self.create_trigger).pack()
|
|
68
|
-
ttk.Button(self, text='Cancel', command=self.destroy).pack()
|
|
69
|
-
|
|
70
|
-
if self.trigger is not None:
|
|
71
|
-
self.pre_populate()
|
|
72
|
-
|
|
73
|
-
def pre_populate(self):
|
|
74
|
-
"""Populate the form with values from an existing trigger"""
|
|
75
|
-
self.trigger_entry.insert(0, self.trigger.trigger_name)
|
|
76
|
-
self.name_entry.insert(0, self.trigger.process_name)
|
|
77
|
-
self.cron_entry.insert(0, self.trigger.cron_expr)
|
|
78
|
-
self.path_entry.insert(0, self.trigger.process_path)
|
|
79
|
-
self.args_entry.insert(0, self.trigger.process_args)
|
|
80
|
-
self.git_check.set(self.trigger.is_git_repo)
|
|
81
|
-
self.blocking_check.set(self.trigger.is_blocking)
|
|
82
|
-
|
|
83
|
-
def create_trigger(self):
|
|
84
|
-
"""Creates a new scheduled trigger in the database using the data entered in the UI.
|
|
85
|
-
If an existing trigger was given when creating the popup it is updated instead.
|
|
86
|
-
"""
|
|
87
|
-
trigger_name = self.trigger_entry.get()
|
|
88
|
-
process_name = self.name_entry.get()
|
|
89
|
-
cron_string = self.cron_entry.get()
|
|
90
|
-
path = self.path_entry.get()
|
|
91
|
-
args = self.args_entry.get()
|
|
92
|
-
is_git = self.git_check.get()
|
|
93
|
-
is_blocking = self.blocking_check.get()
|
|
94
|
-
|
|
95
|
-
if not trigger_name:
|
|
96
|
-
messagebox.showerror('Error', 'Please enter a trigger name')
|
|
97
|
-
return
|
|
98
|
-
|
|
99
|
-
if not process_name:
|
|
100
|
-
messagebox.showerror('Error', 'Please enter a process name')
|
|
101
|
-
return
|
|
102
|
-
|
|
103
|
-
try:
|
|
104
|
-
cron_iter = croniter.croniter(cron_string, datetime.now())
|
|
105
|
-
next_run = cron_iter.get_next(datetime)
|
|
106
|
-
except croniter.CroniterBadCronError as exc:
|
|
107
|
-
messagebox.showerror('Error', 'Please enter a valid cron expression\n'+str(exc))
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
if not path:
|
|
111
|
-
messagebox.showerror('Error', 'Please enter a process path')
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
if self.trigger is None:
|
|
115
|
-
# Create new trigger in database
|
|
116
|
-
db_util.create_scheduled_trigger(trigger_name, process_name, cron_string, next_run, path, args, is_git, is_blocking)
|
|
117
|
-
else:
|
|
118
|
-
# Update existing trigger
|
|
119
|
-
self.trigger.trigger_name = trigger_name
|
|
120
|
-
self.trigger.process_name = process_name
|
|
121
|
-
self.trigger.cron_expr = cron_string
|
|
122
|
-
self.trigger.next_run = next_run
|
|
123
|
-
self.trigger.process_path = path
|
|
124
|
-
self.trigger.process_args = args
|
|
125
|
-
self.trigger.is_git_repo = is_git
|
|
126
|
-
self.trigger.is_blocking = is_blocking
|
|
127
|
-
db_util.update_trigger(self.trigger)
|
|
128
|
-
|
|
129
|
-
self.destroy()
|