qmenta-sdk-lib 2.2.dev3494__tar.gz → 2.2.dev3495__tar.gz
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.
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/PKG-INFO +1 -1
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/pyproject.toml +1 -1
- qmenta_sdk_lib-2.2.dev3495/python/qmenta/sdk/tool_maker/base_ui.py +190 -0
- qmenta_sdk_lib-2.2.dev3495/python/qmenta/sdk/tool_maker/integration_ui.py +268 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/integration_workflow_ui.py +101 -156
- qmenta_sdk_lib-2.2.dev3495/python/qmenta/sdk/tool_maker/launch_gui.py +131 -0
- qmenta_sdk_lib-2.2.dev3494/python/qmenta/sdk/tool_maker/integration_ui.py +0 -415
- qmenta_sdk_lib-2.2.dev3494/python/qmenta/sdk/tool_maker/launch_gui.py +0 -173
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/__init__.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/__init__.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/bids/__init__.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/bids/make_entrypoint.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/bids/wrapper.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/client.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/communication.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/context.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/directory_utils.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/executor.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/init.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/local/__init__.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/local/client.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/local/context.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/local/executor.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/local/parse_settings.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/log_capture.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/make_entrypoint.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/__init__.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/context.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/file_filter.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/inputs.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/make_files.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/modalities.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/outputs.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/run_test_docker.py +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/templates_tool_maker/Dockerfile_schema +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/templates_tool_maker/description_schema +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/templates_tool_maker/qmenta.png +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/templates_tool_maker/test_tool_schema +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/templates_tool_maker/tool_schema +0 -0
- {qmenta_sdk_lib-2.2.dev3494 → qmenta_sdk_lib-2.2.dev3495}/python/qmenta/sdk/tool_maker/tool_maker.py +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from tkinter import BOTH, CENTER, LEFT, RIGHT, TOP, YES, X, Y, messagebox
|
|
3
|
+
|
|
4
|
+
import ttkbootstrap as ttk
|
|
5
|
+
from PIL import Image, ImageTk
|
|
6
|
+
|
|
7
|
+
# Constants for style
|
|
8
|
+
FONT_HEADER = "Helvetica 16 bold"
|
|
9
|
+
FONT_SUBHEADER = "Helvetica 12 bold"
|
|
10
|
+
PADDING_OUTER = 20
|
|
11
|
+
PADDING_ROW = 5
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UnifiedToolWindow:
|
|
15
|
+
"""
|
|
16
|
+
A unified GUI window class that provides standardized styling
|
|
17
|
+
and helper methods. Scrolling is optional.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
title="Tool GUI",
|
|
23
|
+
geometry="600x800",
|
|
24
|
+
theme="darkly",
|
|
25
|
+
scrollable=True,
|
|
26
|
+
):
|
|
27
|
+
self.window = ttk.Window(themename=theme)
|
|
28
|
+
self.window.title(title)
|
|
29
|
+
self.window.geometry(geometry)
|
|
30
|
+
|
|
31
|
+
# Only allow resizing if specifically needed or strictly scrollable
|
|
32
|
+
self.window.resizable(True, True)
|
|
33
|
+
|
|
34
|
+
self.gui_content = {}
|
|
35
|
+
self.scrollable = scrollable
|
|
36
|
+
|
|
37
|
+
if self.scrollable:
|
|
38
|
+
self._setup_scroll_area()
|
|
39
|
+
self._setup_scroll_bindings()
|
|
40
|
+
else:
|
|
41
|
+
self._setup_static_area()
|
|
42
|
+
|
|
43
|
+
def _setup_scroll_area(self):
|
|
44
|
+
"""Sets up the complex canvas and scrollbar structure."""
|
|
45
|
+
self.main_container = ttk.Frame(self.window)
|
|
46
|
+
self.main_container.pack(fill=BOTH, expand=YES)
|
|
47
|
+
|
|
48
|
+
self.canvas = ttk.Canvas(self.main_container)
|
|
49
|
+
self.vbar = ttk.Scrollbar(
|
|
50
|
+
self.main_container, orient="vertical", command=self.canvas.yview
|
|
51
|
+
)
|
|
52
|
+
self.canvas.configure(yscrollcommand=self.vbar.set)
|
|
53
|
+
|
|
54
|
+
self.vbar.pack(side=RIGHT, fill=Y)
|
|
55
|
+
self.canvas.pack(side=LEFT, fill=BOTH, expand=YES)
|
|
56
|
+
|
|
57
|
+
self.content_frame = ttk.Frame(self.canvas, padding=PADDING_OUTER)
|
|
58
|
+
self.canvas_window_id = self.canvas.create_window(
|
|
59
|
+
(0, 0), window=self.content_frame, anchor="nw", tags="inner_frame"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def _setup_static_area(self):
|
|
63
|
+
"""Sets up a simple frame without scrolling."""
|
|
64
|
+
self.content_frame = ttk.Frame(self.window, padding=PADDING_OUTER)
|
|
65
|
+
self.content_frame.pack(fill=BOTH, expand=YES)
|
|
66
|
+
|
|
67
|
+
def _setup_scroll_bindings(self):
|
|
68
|
+
"""
|
|
69
|
+
Sets up resize events and mousewheel scrolling
|
|
70
|
+
Only for scrollable windows
|
|
71
|
+
"""
|
|
72
|
+
self.content_frame.bind(
|
|
73
|
+
"<Configure>",
|
|
74
|
+
lambda e: self.canvas.configure(
|
|
75
|
+
scrollregion=self.canvas.bbox("all")
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
self.canvas.bind(
|
|
79
|
+
"<Configure>",
|
|
80
|
+
lambda e: self.canvas.itemconfig(
|
|
81
|
+
self.canvas_window_id, width=e.width
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def _on_mousewheel(event):
|
|
86
|
+
if event.delta: # Windows/macOS
|
|
87
|
+
self.canvas.yview_scroll(
|
|
88
|
+
int(-1 * (event.delta / 120)), "units"
|
|
89
|
+
)
|
|
90
|
+
elif event.num == 4: # Linux Up
|
|
91
|
+
self.canvas.yview_scroll(-1, "units")
|
|
92
|
+
elif event.num == 5: # Linux Down
|
|
93
|
+
self.canvas.yview_scroll(1, "units")
|
|
94
|
+
|
|
95
|
+
self.window.bind_all("<MouseWheel>", _on_mousewheel)
|
|
96
|
+
self.window.bind_all("<Button-4>", _on_mousewheel)
|
|
97
|
+
self.window.bind_all("<Button-5>", _on_mousewheel)
|
|
98
|
+
|
|
99
|
+
# --- Widget Helpers ---
|
|
100
|
+
|
|
101
|
+
def add_logo(self, relative_path="templates_tool_maker/qmenta.png"):
|
|
102
|
+
try:
|
|
103
|
+
img_path = os.path.join(os.path.dirname(__file__), relative_path)
|
|
104
|
+
if os.path.exists(img_path):
|
|
105
|
+
logo = Image.open(img_path).resize((500, 110))
|
|
106
|
+
img = ImageTk.PhotoImage(logo)
|
|
107
|
+
lbl = ttk.Label(
|
|
108
|
+
master=self.content_frame, image=img, anchor=CENTER
|
|
109
|
+
)
|
|
110
|
+
lbl.image = img
|
|
111
|
+
lbl.pack(side=TOP, pady=(0, 20))
|
|
112
|
+
except Exception as e:
|
|
113
|
+
print(f"Image load failed: {e}")
|
|
114
|
+
|
|
115
|
+
def add_header(self, text, subtext=None, warning_text=None):
|
|
116
|
+
ttk.Label(self.content_frame, text=text, font=FONT_HEADER).pack(
|
|
117
|
+
pady=10
|
|
118
|
+
)
|
|
119
|
+
if subtext:
|
|
120
|
+
ttk.Label(self.content_frame, text=subtext, bootstyle="info").pack(
|
|
121
|
+
pady=(0, 5)
|
|
122
|
+
)
|
|
123
|
+
if warning_text:
|
|
124
|
+
ttk.Label(
|
|
125
|
+
self.content_frame, text=warning_text, bootstyle="warning"
|
|
126
|
+
).pack(pady=(0, 20))
|
|
127
|
+
|
|
128
|
+
def add_section_label(self, text):
|
|
129
|
+
ttk.Label(self.content_frame, text=text, font=FONT_SUBHEADER).pack(
|
|
130
|
+
pady=10
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def create_row(self, label_text, is_password=False, default=None):
|
|
134
|
+
frame = ttk.Frame(master=self.content_frame)
|
|
135
|
+
frame.pack(fill=X, pady=PADDING_ROW)
|
|
136
|
+
|
|
137
|
+
lbl = ttk.Label(master=frame, text=label_text, width=30)
|
|
138
|
+
lbl.pack(side=LEFT)
|
|
139
|
+
|
|
140
|
+
entry = ttk.Entry(master=frame, show="*" if is_password else None)
|
|
141
|
+
if default:
|
|
142
|
+
entry.insert(0, str(default))
|
|
143
|
+
entry.pack(side=LEFT, fill=X, expand=YES, padx=10)
|
|
144
|
+
return entry
|
|
145
|
+
|
|
146
|
+
def add_separator(self):
|
|
147
|
+
ttk.Separator(self.content_frame, orient="horizontal").pack(
|
|
148
|
+
fill=X, pady=20
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def add_action_buttons(
|
|
152
|
+
self, submit_command, quit_command=None, submit_text="Publish"
|
|
153
|
+
):
|
|
154
|
+
action_frame = ttk.Frame(master=self.content_frame)
|
|
155
|
+
action_frame.pack(pady=20)
|
|
156
|
+
|
|
157
|
+
def wrapped_submit():
|
|
158
|
+
if submit_command():
|
|
159
|
+
self.window.destroy()
|
|
160
|
+
|
|
161
|
+
def wrapped_quit():
|
|
162
|
+
if quit_command:
|
|
163
|
+
quit_command()
|
|
164
|
+
self.window.destroy()
|
|
165
|
+
exit()
|
|
166
|
+
|
|
167
|
+
ttk.Button(
|
|
168
|
+
master=action_frame,
|
|
169
|
+
text=submit_text,
|
|
170
|
+
command=wrapped_submit,
|
|
171
|
+
bootstyle="primary",
|
|
172
|
+
).pack(side=LEFT, padx=10)
|
|
173
|
+
|
|
174
|
+
ttk.Button(
|
|
175
|
+
master=action_frame,
|
|
176
|
+
text="Quit",
|
|
177
|
+
command=wrapped_quit,
|
|
178
|
+
bootstyle="danger",
|
|
179
|
+
).pack(side=LEFT, padx=10)
|
|
180
|
+
|
|
181
|
+
def run(self):
|
|
182
|
+
self.window.mainloop()
|
|
183
|
+
|
|
184
|
+
@staticmethod
|
|
185
|
+
def show_error(title, message):
|
|
186
|
+
messagebox.showerror(title, message)
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def show_info(title, message):
|
|
190
|
+
messagebox.showinfo(title, message)
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#! /usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
from tkinter import END, LEFT, filedialog
|
|
7
|
+
|
|
8
|
+
import ttkbootstrap as ttk
|
|
9
|
+
from qmenta.core import platform
|
|
10
|
+
from qmenta.core.auth import Needs2FAError
|
|
11
|
+
from qmenta.sdk.tool_maker.make_files import raise_if_false
|
|
12
|
+
|
|
13
|
+
# Import shared UI
|
|
14
|
+
from base_ui import UnifiedToolWindow
|
|
15
|
+
|
|
16
|
+
MIN = 0
|
|
17
|
+
MAX_CORES = 10
|
|
18
|
+
MAX_RAM = 16
|
|
19
|
+
|
|
20
|
+
dir_name = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def gui_tkinter():
|
|
24
|
+
# Initialize the Shared Window
|
|
25
|
+
gui = UnifiedToolWindow(title="Tool Publishing GUI", scrollable=True)
|
|
26
|
+
|
|
27
|
+
# Logo & Headers
|
|
28
|
+
gui.add_logo()
|
|
29
|
+
gui.add_header(
|
|
30
|
+
"QMENTA Platform credentials",
|
|
31
|
+
warning_text="(*) indicates mandatory field.",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Credential Fields
|
|
35
|
+
user_id_entry = gui.create_row("Username*")
|
|
36
|
+
password_entry = gui.create_row("Password*", is_password=True)
|
|
37
|
+
|
|
38
|
+
# Custom File Browse Row (Logic specific to this script)
|
|
39
|
+
write_dir = ttk.StringVar()
|
|
40
|
+
|
|
41
|
+
def browse_folder():
|
|
42
|
+
global dir_name
|
|
43
|
+
dir_name = filedialog.askdirectory()
|
|
44
|
+
write_dir.set(dir_name)
|
|
45
|
+
tool_id_entry.delete(0, END)
|
|
46
|
+
tool_id_entry.insert(0, os.path.basename(dir_name))
|
|
47
|
+
|
|
48
|
+
# checking version
|
|
49
|
+
version_file = os.path.join(dir_name, "version")
|
|
50
|
+
if os.path.exists(version_file):
|
|
51
|
+
with open(version_file) as fv:
|
|
52
|
+
version_ = fv.read().strip()
|
|
53
|
+
tool_version_entry.delete(0, END)
|
|
54
|
+
tool_version_entry.insert(0, version_)
|
|
55
|
+
image_name_entry.delete(0, END)
|
|
56
|
+
image_name_entry.insert(
|
|
57
|
+
0, os.path.basename(dir_name) + f":{version_}"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
folder_frame = ttk.Frame(master=gui.content_frame)
|
|
61
|
+
folder_frame.pack(fill="x", pady=10)
|
|
62
|
+
ttk.Label(folder_frame, text="Select tool folder*", width=30).pack(
|
|
63
|
+
side=LEFT
|
|
64
|
+
)
|
|
65
|
+
ttk.Button(folder_frame, text="Browse Folder", command=browse_folder).pack(
|
|
66
|
+
side=LEFT, padx=10
|
|
67
|
+
)
|
|
68
|
+
ttk.Label(folder_frame, textvariable=write_dir).pack(side=LEFT, padx=10)
|
|
69
|
+
|
|
70
|
+
# Tool Info Fields
|
|
71
|
+
tool_id_entry = gui.create_row("Specify the tool ID*")
|
|
72
|
+
tool_version_entry = gui.create_row("Specify the tool version*")
|
|
73
|
+
tool_name_entry = gui.create_row("Specify the tool name*")
|
|
74
|
+
num_cores_entry = gui.create_row(
|
|
75
|
+
f"Cores required (integer, max {MAX_CORES})", default="1"
|
|
76
|
+
)
|
|
77
|
+
memory_entry = gui.create_row(
|
|
78
|
+
f"RAM required GB (integer, max {MAX_RAM})", default="1"
|
|
79
|
+
)
|
|
80
|
+
image_name_entry = gui.create_row("Docker image name")
|
|
81
|
+
|
|
82
|
+
gui.add_separator()
|
|
83
|
+
|
|
84
|
+
# Docker Registry Fields
|
|
85
|
+
gui.add_section_label("Docker Registry Credentials")
|
|
86
|
+
docker_url_entry = gui.create_row(
|
|
87
|
+
"Docker registry URL", default="hub.docker.com"
|
|
88
|
+
)
|
|
89
|
+
docker_user_entry = gui.create_row("Docker registry user")
|
|
90
|
+
docker_password_entry = gui.create_row(
|
|
91
|
+
"Docker registry password*", is_password=True
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# --- Logic ---
|
|
95
|
+
gui_content = {}
|
|
96
|
+
|
|
97
|
+
def generate_output_object():
|
|
98
|
+
return {
|
|
99
|
+
"qmenta_user": user_id_entry.get(),
|
|
100
|
+
"qmenta_password": password_entry.get(),
|
|
101
|
+
"code": tool_id_entry.get(),
|
|
102
|
+
"version": tool_version_entry.get(),
|
|
103
|
+
"name": tool_name_entry.get(),
|
|
104
|
+
"cores": num_cores_entry.get(),
|
|
105
|
+
"memory": memory_entry.get(),
|
|
106
|
+
"image_name": image_name_entry.get(),
|
|
107
|
+
"docker_url": docker_url_entry.get(),
|
|
108
|
+
"docker_user": docker_user_entry.get(),
|
|
109
|
+
"docker_password": docker_password_entry.get(),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
def perform_selection_check(values):
|
|
113
|
+
try:
|
|
114
|
+
raise_if_false(
|
|
115
|
+
isinstance(values["code"], str), "Tool ID must be a string."
|
|
116
|
+
)
|
|
117
|
+
raise_if_false(values["code"] != "", "Tool ID must be defined.")
|
|
118
|
+
raise_if_false(
|
|
119
|
+
" " not in values["code"], "Tool ID can't have spaces."
|
|
120
|
+
)
|
|
121
|
+
values["code"] = values["code"].lower()
|
|
122
|
+
values["short_name"] = values["code"].lower().replace(" ", "_")
|
|
123
|
+
|
|
124
|
+
raise_if_false(
|
|
125
|
+
isinstance(values["name"], str), "Tool name must be a string."
|
|
126
|
+
)
|
|
127
|
+
raise_if_false(values["name"] != "", "Tool name must be defined.")
|
|
128
|
+
raise_if_false(
|
|
129
|
+
values["version"] != "", "Tool version must be defined."
|
|
130
|
+
)
|
|
131
|
+
raise_if_false(
|
|
132
|
+
re.search(r"^(\d+\.)?(\d+\.)?(\*|\d+)$", values["version"]),
|
|
133
|
+
"Version format not valid.",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
raise_if_false(values["cores"], "Number of cores must be defined.")
|
|
137
|
+
raise_if_false(
|
|
138
|
+
values["cores"].isnumeric(),
|
|
139
|
+
"Number of cores must be an integer.",
|
|
140
|
+
)
|
|
141
|
+
raise_if_false(
|
|
142
|
+
MAX_CORES >= int(values["cores"]) > MIN,
|
|
143
|
+
f"Number of cores must be between {MIN} and {MAX_CORES}.",
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
raise_if_false(values["memory"], "RAM must be defined.")
|
|
147
|
+
raise_if_false(
|
|
148
|
+
values["memory"].isnumeric(), "RAM must be an integer."
|
|
149
|
+
)
|
|
150
|
+
raise_if_false(
|
|
151
|
+
MAX_RAM >= int(values["memory"]) > MIN,
|
|
152
|
+
f"RAM must be {MIN} and {MAX_RAM}.",
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
values["memory"] = int(values["memory"])
|
|
156
|
+
values["image_name"] = (
|
|
157
|
+
values["image_name"]
|
|
158
|
+
or values["code"] + ":" + values["version"]
|
|
159
|
+
)
|
|
160
|
+
return values
|
|
161
|
+
except AssertionError as e:
|
|
162
|
+
gui.show_error("Error", f"AN EXCEPTION OCCURRED! {e}")
|
|
163
|
+
return False
|
|
164
|
+
|
|
165
|
+
def submit_form():
|
|
166
|
+
gui_content.update(generate_output_object())
|
|
167
|
+
res = perform_selection_check(gui_content)
|
|
168
|
+
if res:
|
|
169
|
+
gui_content.update(res)
|
|
170
|
+
return True # Signal to close window
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
# Add Buttons & Run
|
|
174
|
+
gui.add_action_buttons(
|
|
175
|
+
submit_form, submit_text="Publish in QMENTA Platform"
|
|
176
|
+
)
|
|
177
|
+
gui.run()
|
|
178
|
+
|
|
179
|
+
return gui_content
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def main():
|
|
183
|
+
content_build = gui_tkinter()
|
|
184
|
+
if not content_build:
|
|
185
|
+
exit()
|
|
186
|
+
# Ensure dir_name was set by browse_folder
|
|
187
|
+
if dir_name:
|
|
188
|
+
os.chdir(dir_name)
|
|
189
|
+
|
|
190
|
+
os.chdir(dir_name)
|
|
191
|
+
|
|
192
|
+
user = content_build["qmenta_user"]
|
|
193
|
+
password = content_build["qmenta_password"]
|
|
194
|
+
try:
|
|
195
|
+
auth = platform.Auth.login(
|
|
196
|
+
username=user,
|
|
197
|
+
password=password,
|
|
198
|
+
base_url="https://platform.qmenta.com",
|
|
199
|
+
ask_for_2fa_input=False,
|
|
200
|
+
)
|
|
201
|
+
except Needs2FAError as needs2faerror:
|
|
202
|
+
gui_tkinter.show_info(
|
|
203
|
+
"Message",
|
|
204
|
+
str(needs2faerror)
|
|
205
|
+
+ " Please check the terminal to add the code sent to your phone.",
|
|
206
|
+
)
|
|
207
|
+
auth = platform.Auth.login(
|
|
208
|
+
username=user,
|
|
209
|
+
password=password,
|
|
210
|
+
base_url="https://platform.qmenta.com",
|
|
211
|
+
code_2fa=input("Input 2-FA code:"),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Get information from the advanced options file.
|
|
215
|
+
advanced_options = "settings.json"
|
|
216
|
+
raise_if_false(
|
|
217
|
+
os.path.exists(advanced_options),
|
|
218
|
+
"Settings do not exist! Run the local test to create it.",
|
|
219
|
+
)
|
|
220
|
+
with open(advanced_options) as fr:
|
|
221
|
+
content_build["advanced_options"] = fr.read()
|
|
222
|
+
|
|
223
|
+
# Get information from the description file.
|
|
224
|
+
with open("description.html") as fr:
|
|
225
|
+
content_build["description"] = fr.read()
|
|
226
|
+
|
|
227
|
+
with open("results_configuration.json", "r") as file:
|
|
228
|
+
results_config = json.load(file)
|
|
229
|
+
# The screen value is expected as a string with escaped chars (dict)
|
|
230
|
+
results_config["screen"] = json.dumps(results_config["screen"])
|
|
231
|
+
content_build["results_configuration"] = json.dumps(
|
|
232
|
+
results_config
|
|
233
|
+
).replace("{}", "")
|
|
234
|
+
|
|
235
|
+
content_build.update(
|
|
236
|
+
{
|
|
237
|
+
"start_condition_code": "output={'OK': True, 'code': 1}",
|
|
238
|
+
"entry_point": "/root/entrypoint.sh",
|
|
239
|
+
"tool_path": "tool:run",
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# After creating the workflow, the ID of the workflow must be requested
|
|
244
|
+
# and added to the previous dictionary
|
|
245
|
+
# otherwise it will keep creating new workflows on the platform
|
|
246
|
+
# creating conflicts.
|
|
247
|
+
res = platform.post(
|
|
248
|
+
auth, "analysis_manager/upsert_user_tool", data=content_build
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if res.json()["success"] == 1:
|
|
252
|
+
print("Tool updated successfully!")
|
|
253
|
+
print(
|
|
254
|
+
"Tool name:",
|
|
255
|
+
content_build["name"],
|
|
256
|
+
"(",
|
|
257
|
+
content_build["code"],
|
|
258
|
+
":",
|
|
259
|
+
content_build["version"],
|
|
260
|
+
")",
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
print("ERROR setting the tool.")
|
|
264
|
+
print(res.json())
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
if __name__ == "__main__":
|
|
268
|
+
main()
|