bfabric-web-apps 0.1.3__py3-none-any.whl → 0.1.5__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.
- bfabric_web_apps/__init__.py +30 -73
- bfabric_web_apps/layouts/layouts.py +98 -6
- bfabric_web_apps/objects/BfabricInterface.py +4 -11
- bfabric_web_apps/utils/callbacks.py +172 -4
- bfabric_web_apps/utils/config.py +38 -0
- bfabric_web_apps/utils/redis_connection.py +6 -0
- bfabric_web_apps/utils/redis_queue.py +6 -0
- bfabric_web_apps/utils/redis_worker_init.py +28 -0
- bfabric_web_apps/utils/resource_utilities.py +112 -161
- bfabric_web_apps/utils/run_main_pipeline.py +414 -0
- {bfabric_web_apps-0.1.3.dist-info → bfabric_web_apps-0.1.5.dist-info}/METADATA +4 -1
- bfabric_web_apps-0.1.5.dist-info/RECORD +20 -0
- bfabric_web_apps/utils/defaults.py +0 -11
- bfabric_web_apps-0.1.3.dist-info/RECORD +0 -16
- {bfabric_web_apps-0.1.3.dist-info → bfabric_web_apps-0.1.5.dist-info}/LICENSE +0 -0
- {bfabric_web_apps-0.1.3.dist-info → bfabric_web_apps-0.1.5.dist-info}/WHEEL +0 -0
bfabric_web_apps/__init__.py
CHANGED
@@ -16,85 +16,42 @@ from .utils.get_power_user_wrapper import get_power_user_wrapper
|
|
16
16
|
from .utils.create_app_in_bfabric import create_app_in_bfabric
|
17
17
|
|
18
18
|
# Export callbacks
|
19
|
-
from .utils.callbacks import
|
19
|
+
from .utils.callbacks import (
|
20
|
+
process_url_and_token,
|
21
|
+
submit_bug_report,
|
22
|
+
populate_workunit_details,
|
23
|
+
get_redis_queue_layout
|
24
|
+
)
|
20
25
|
|
21
|
-
from .utils import
|
26
|
+
from .utils.config import settings as config
|
22
27
|
|
23
|
-
from
|
24
|
-
HOST = os.getenv("HOST", defaults.HOST)
|
25
|
-
PORT = int(os.getenv("PORT", defaults.PORT)) # Convert to int since env variables are strings
|
26
|
-
DEV = os.getenv("DEV", str(defaults.DEV)).lower() in ["true", "1", "yes"] # Convert to bool
|
27
|
-
CONFIG_FILE_PATH = os.getenv("CONFIG_FILE_PATH", defaults.CONFIG_FILE_PATH)
|
28
|
+
from. utils.run_main_pipeline import run_main_job
|
28
29
|
|
29
|
-
|
30
|
-
|
30
|
+
from .utils.resource_utilities import (
|
31
|
+
create_workunit,
|
32
|
+
create_resource,
|
33
|
+
create_workunits,
|
34
|
+
create_resources
|
35
|
+
)
|
31
36
|
|
37
|
+
from .utils.redis_worker_init import run_worker, test_job
|
38
|
+
from .utils.redis_queue import q
|
32
39
|
|
33
|
-
|
34
|
-
|
35
|
-
"BfabricInterface",
|
36
|
-
"Logger",
|
37
|
-
"components",
|
38
|
-
"get_static_layout",
|
39
|
-
"create_app",
|
40
|
-
"process_url_and_token",
|
41
|
-
"submit_bug_report",
|
42
|
-
'get_logger',
|
43
|
-
'get_power_user_wrapper',
|
44
|
-
'HOST',
|
45
|
-
'PORT',
|
46
|
-
'DEV',
|
47
|
-
'CONFIG_FILE_PATH',
|
48
|
-
'DEVELOPER_EMAIL_ADDRESS',
|
49
|
-
'BUG_REPORT_EMAIL_ADDRESS',
|
50
|
-
'create_app_in_bfabric',
|
51
|
-
'create_workunit',
|
52
|
-
'create_resource'
|
53
|
-
]
|
40
|
+
REDIS_HOST = config.REDIS_HOST
|
41
|
+
REDIS_PORT = config.REDIS_PORT
|
54
42
|
|
43
|
+
HOST = config.HOST
|
44
|
+
PORT = config.PORT
|
45
|
+
DEV = config.DEV
|
46
|
+
DEBUG = config.DEBUG
|
55
47
|
|
48
|
+
CONFIG_FILE_PATH = config.CONFIG_FILE_PATH
|
56
49
|
|
57
|
-
|
58
|
-
|
59
|
-
from .utils import defaults
|
60
|
-
|
61
|
-
# Private variable for CONFIG_FILE_PATH
|
62
|
-
_CONFIG_FILE_PATH = os.getenv("CONFIG_FILE_PATH", defaults.CONFIG_FILE_PATH)
|
63
|
-
|
64
|
-
def set_config_file_path(path):
|
65
|
-
"""
|
66
|
-
Setter for the CONFIG_FILE_PATH variable.
|
67
|
-
"""
|
68
|
-
global _CONFIG_FILE_PATH
|
69
|
-
if not isinstance(path, str):
|
70
|
-
raise ValueError("CONFIG_FILE_PATH must be a string.")
|
71
|
-
_CONFIG_FILE_PATH = path
|
72
|
-
|
73
|
-
def get_config_file_path():
|
74
|
-
"""
|
75
|
-
Getter for the CONFIG_FILE_PATH variable.
|
76
|
-
"""
|
77
|
-
return _CONFIG_FILE_PATH
|
78
|
-
|
79
|
-
# Expose CONFIG_FILE_PATH as a read-only property
|
80
|
-
class Config:
|
81
|
-
@property
|
82
|
-
def CONFIG_FILE_PATH(self):
|
83
|
-
return get_config_file_path()
|
84
|
-
|
85
|
-
config = Config()
|
86
|
-
|
87
|
-
'''
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
'''
|
92
|
-
from bfabric import config
|
93
|
-
|
94
|
-
config.CONFIG_FILE_PATH
|
95
|
-
'''
|
50
|
+
DEVELOPER_EMAIL_ADDRESS = config.DEVELOPER_EMAIL_ADDRESS
|
51
|
+
BUG_REPORT_EMAIL_ADDRESS = config.BUG_REPORT_EMAIL_ADDRESS
|
96
52
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
53
|
+
GSTORE_REMOTE_PATH = config.GSTORE_REMOTE_PATH
|
54
|
+
SCRATCH_PATH = config.SCRATCH_PATH
|
55
|
+
TRX_LOGIN = config.TRX_LOGIN
|
56
|
+
TRX_SSH_KEY = config.TRX_SSH_KEY
|
57
|
+
URL = config.URL
|
@@ -2,7 +2,7 @@ from dash import html, dcc
|
|
2
2
|
import dash_bootstrap_components as dbc
|
3
3
|
import bfabric_web_apps
|
4
4
|
|
5
|
-
def get_static_layout(base_title=None, main_content=None, documentation_content=None):
|
5
|
+
def get_static_layout(base_title=None, main_content=None, documentation_content=None, layout_config={}):
|
6
6
|
"""
|
7
7
|
Returns a layout with static tabs for Main, Documentation, and Report a Bug.
|
8
8
|
The main content is customizable, while the other tabs are generic.
|
@@ -11,10 +11,24 @@ def get_static_layout(base_title=None, main_content=None, documentation_content=
|
|
11
11
|
base_title (str): The main title to be displayed in the banner.
|
12
12
|
main_content (html.Div): Content to be displayed in the "Main" tab.
|
13
13
|
documentation_content (html.Div): Content for the "Documentation" tab.
|
14
|
+
layout_config (dict): Configuration for the layout, determining which tabs are shown.
|
14
15
|
|
15
16
|
Returns:
|
16
17
|
html.Div: The complete static layout of the web app.
|
17
18
|
"""
|
19
|
+
|
20
|
+
tab_list = [
|
21
|
+
dbc.Tab(main_content, label="Main", tab_id="main"),
|
22
|
+
dbc.Tab(dcc.Loading(get_documentation_tab(documentation_content)), label="Documentation", tab_id="documentation"),
|
23
|
+
]
|
24
|
+
|
25
|
+
if layout_config.get("workunits", False):
|
26
|
+
tab_list.append(dbc.Tab(dcc.Loading(get_workunits_tab()), label="Workunits", tab_id="workunits"))
|
27
|
+
if layout_config.get("queue", False):
|
28
|
+
tab_list.append(dbc.Tab(get_queue_tab(), label="Queue", tab_id="queue"))
|
29
|
+
if layout_config.get("bug", False):
|
30
|
+
tab_list.append(dbc.Tab(dcc.Loading(get_report_bug_tab()), label="Report a Bug", tab_id="report-bug"))
|
31
|
+
|
18
32
|
return html.Div(
|
19
33
|
children=[
|
20
34
|
dcc.Location(id='url', refresh=False),
|
@@ -142,11 +156,7 @@ def get_static_layout(base_title=None, main_content=None, documentation_content=
|
|
142
156
|
|
143
157
|
# Tabs Section
|
144
158
|
dbc.Tabs(
|
145
|
-
|
146
|
-
dbc.Tab(main_content, label="Main", tab_id="main"),
|
147
|
-
dbc.Tab(get_documentation_tab(documentation_content), label="Documentation", tab_id="documentation"),
|
148
|
-
dbc.Tab(get_report_bug_tab(), label="Report a Bug", tab_id="report-bug"),
|
149
|
-
],
|
159
|
+
tab_list,
|
150
160
|
id="tabs",
|
151
161
|
active_tab="main",
|
152
162
|
),
|
@@ -250,10 +260,92 @@ def get_report_bug_tab():
|
|
250
260
|
"margin-left": "2vw",
|
251
261
|
"font-size": "20px",
|
252
262
|
"padding-right": "40px",
|
263
|
+
"overflow-y": "scroll",
|
264
|
+
"max-height": "65vh",
|
253
265
|
},
|
254
266
|
),
|
255
267
|
width=9,
|
256
268
|
),
|
257
269
|
],
|
258
270
|
style={"margin-top": "0px", "min-height": "40vh"},
|
271
|
+
)
|
272
|
+
|
273
|
+
|
274
|
+
def get_workunits_tab():
|
275
|
+
"""
|
276
|
+
Returns the content for the Workunits tab with the upgraded layout.
|
277
|
+
"""
|
278
|
+
return dbc.Row(
|
279
|
+
id="page-content-workunits",
|
280
|
+
children=[
|
281
|
+
dbc.Col(
|
282
|
+
html.Div(
|
283
|
+
id="sidebar_workunits",
|
284
|
+
children=[], # Optional: Add sidebar content here if needed
|
285
|
+
style={
|
286
|
+
"border-right": "2px solid #d4d7d9",
|
287
|
+
"height": "100%",
|
288
|
+
"padding": "20px",
|
289
|
+
"font-size": "20px",
|
290
|
+
},
|
291
|
+
),
|
292
|
+
width=3,
|
293
|
+
),
|
294
|
+
dbc.Col(
|
295
|
+
html.Div(
|
296
|
+
id="page-content-workunits-children",
|
297
|
+
children=[
|
298
|
+
html.H2("Workunits"),
|
299
|
+
html.Div(id="refresh-workunits", children=[]),
|
300
|
+
html.Div(
|
301
|
+
id="workunits-content"
|
302
|
+
)
|
303
|
+
],
|
304
|
+
style={
|
305
|
+
"margin-top": "2vh",
|
306
|
+
"margin-left": "2vw",
|
307
|
+
"font-size": "20px",
|
308
|
+
"padding-right": "40px",
|
309
|
+
"overflow-y": "scroll",
|
310
|
+
"max-height": "65vh",
|
311
|
+
},
|
312
|
+
),
|
313
|
+
width=9,
|
314
|
+
),
|
315
|
+
],
|
316
|
+
style={"margin-top": "0px", "min-height": "40vh"},
|
317
|
+
)
|
318
|
+
|
319
|
+
|
320
|
+
def get_queue_tab():
|
321
|
+
|
322
|
+
return dbc.Row(
|
323
|
+
id="page-content-queue",
|
324
|
+
children=[
|
325
|
+
dbc.Col(
|
326
|
+
html.Div(
|
327
|
+
id="page-content-queue-children",
|
328
|
+
children=[],
|
329
|
+
style={
|
330
|
+
"margin-top": "2vh",
|
331
|
+
"margin-left": "2vw",
|
332
|
+
"font-size": "20px",
|
333
|
+
"padding-right": "40px",
|
334
|
+
"overflow-y": "scroll",
|
335
|
+
"max-height": "65vh",
|
336
|
+
},
|
337
|
+
),
|
338
|
+
),
|
339
|
+
dbc.Col(
|
340
|
+
children = [
|
341
|
+
dcc.Interval(
|
342
|
+
id="queue-interval",
|
343
|
+
interval=5 * 1000, # in milliseconds
|
344
|
+
n_intervals=0,
|
345
|
+
),
|
346
|
+
],
|
347
|
+
style={"display": "none"}
|
348
|
+
)
|
349
|
+
],
|
350
|
+
style={"margin-top": "0px", "min-height": "40vh"},
|
259
351
|
)
|
@@ -9,12 +9,9 @@ from bfabric_web_apps.utils.get_logger import get_logger
|
|
9
9
|
import os
|
10
10
|
import bfabric_web_apps
|
11
11
|
|
12
|
-
|
13
|
-
|
14
12
|
VALIDATION_URL = "https://fgcz-bfabric.uzh.ch/bfabric/rest/token/validate?token="
|
15
13
|
HOST = "fgcz-bfabric.uzh.ch"
|
16
14
|
|
17
|
-
|
18
15
|
class BfabricInterface( Bfabric ):
|
19
16
|
_instance = None # Singleton instance
|
20
17
|
_wrapper = None # Shared wrapper instance
|
@@ -50,8 +47,7 @@ class BfabricInterface( Bfabric ):
|
|
50
47
|
if self._wrapper is None:
|
51
48
|
raise RuntimeError("Bfabric wrapper is not initialized. Token validation must run first.")
|
52
49
|
return self._wrapper
|
53
|
-
|
54
|
-
|
50
|
+
|
55
51
|
|
56
52
|
def token_to_data(self, token):
|
57
53
|
"""
|
@@ -96,8 +92,6 @@ class BfabricInterface( Bfabric ):
|
|
96
92
|
|
97
93
|
environment_dict = {"Production":"https://fgcz-bfabric.uzh.ch/bfabric","Test":"https://fgcz-bfabric-test.uzh.ch/bfabric"}
|
98
94
|
|
99
|
-
print('userinfo', userinfo)
|
100
|
-
|
101
95
|
token_data = dict(
|
102
96
|
environment = userinfo['environment'],
|
103
97
|
user_data = userinfo['user'],
|
@@ -267,7 +261,7 @@ class BfabricInterface( Bfabric ):
|
|
267
261
|
|
268
262
|
# Extract App ID, Name, and Description
|
269
263
|
app_info = app_data_dict[0] # First (and only) result
|
270
|
-
|
264
|
+
|
271
265
|
json_data = json.dumps({
|
272
266
|
"id": app_info.get("id", "Unknown"),
|
273
267
|
"name": app_info.get("name", "Unknown"),
|
@@ -312,10 +306,9 @@ class BfabricInterface( Bfabric ):
|
|
312
306
|
os.system(mail)
|
313
307
|
|
314
308
|
return True
|
309
|
+
|
315
310
|
|
316
|
-
|
317
|
-
|
318
|
-
|
311
|
+
|
319
312
|
# Create a globally accessible instance
|
320
313
|
bfabric_interface = BfabricInterface()
|
321
314
|
|
@@ -1,9 +1,13 @@
|
|
1
1
|
from dash import Input, Output, State, html, dcc
|
2
|
-
from bfabric_web_apps.objects.BfabricInterface import
|
2
|
+
from bfabric_web_apps.objects.BfabricInterface import bfabric_interface
|
3
3
|
import json
|
4
4
|
import dash_bootstrap_components as dbc
|
5
5
|
from datetime import datetime as dt
|
6
6
|
from bfabric_web_apps.utils.get_logger import get_logger
|
7
|
+
from rq import Queue
|
8
|
+
from .redis_connection import redis_conn
|
9
|
+
from rq.registry import StartedJobRegistry, FailedJobRegistry, FinishedJobRegistry
|
10
|
+
|
7
11
|
|
8
12
|
def process_url_and_token(url_params):
|
9
13
|
"""
|
@@ -28,7 +32,6 @@ def process_url_and_token(url_params):
|
|
28
32
|
return None, None, None, None, base_title, None, None
|
29
33
|
|
30
34
|
token = "".join(url_params.split('token=')[1:])
|
31
|
-
bfabric_interface = BfabricInterface()
|
32
35
|
tdata_raw = bfabric_interface.token_to_data(token)
|
33
36
|
|
34
37
|
if tdata_raw:
|
@@ -88,7 +91,6 @@ def process_url_and_token(url_params):
|
|
88
91
|
return None, None, None, None, base_title, None, None
|
89
92
|
|
90
93
|
|
91
|
-
|
92
94
|
def submit_bug_report(n_clicks, bug_description, token, entity_data):
|
93
95
|
"""
|
94
96
|
Submits a bug report based on user input, token, and entity data.
|
@@ -103,7 +105,7 @@ def submit_bug_report(n_clicks, bug_description, token, entity_data):
|
|
103
105
|
tuple: A tuple containing two boolean values indicating success and failure status of the submission.
|
104
106
|
(is_open_success, is_open_failure)
|
105
107
|
"""
|
106
|
-
|
108
|
+
|
107
109
|
print("submit bug report", token)
|
108
110
|
|
109
111
|
# Parse token data if token is provided, otherwise set it to an empty dictionary
|
@@ -168,3 +170,169 @@ def submit_bug_report(n_clicks, bug_description, token, entity_data):
|
|
168
170
|
|
169
171
|
return False, False
|
170
172
|
|
173
|
+
|
174
|
+
def populate_workunit_details(token_data):
|
175
|
+
|
176
|
+
"""
|
177
|
+
Function to populate workunit data for the current app instance.
|
178
|
+
|
179
|
+
Args:
|
180
|
+
token_data (dict): Token metadata.
|
181
|
+
|
182
|
+
Returns:
|
183
|
+
html.Div: A div containing the populated workunit data.
|
184
|
+
"""
|
185
|
+
|
186
|
+
environment_urls = {
|
187
|
+
"Test": "https://fgcz-bfabric-test.uzh.ch/bfabric/workunit/show.html?id=",
|
188
|
+
"Production": "https://fgcz-bfabric.uzh.ch/bfabric/workunit/show.html?id="
|
189
|
+
}
|
190
|
+
|
191
|
+
if token_data:
|
192
|
+
|
193
|
+
jobId = token_data.get('jobId', None)
|
194
|
+
print("jobId", jobId)
|
195
|
+
|
196
|
+
job = bfabric_interface.get_wrapper().read("job", {"id": jobId})[0]
|
197
|
+
workunits = job.get("workunit", [])
|
198
|
+
|
199
|
+
if workunits:
|
200
|
+
wus = bfabric_interface.get_wrapper().read(
|
201
|
+
"workunit",
|
202
|
+
{"id": [wu["id"] for wu in workunits]}
|
203
|
+
)
|
204
|
+
else:
|
205
|
+
return html.Div(
|
206
|
+
[
|
207
|
+
html.P("No workunits found for the current job.")
|
208
|
+
]
|
209
|
+
)
|
210
|
+
|
211
|
+
wu_cards = []
|
212
|
+
|
213
|
+
for wu in wus:
|
214
|
+
print(wu)
|
215
|
+
wu_card = html.A(
|
216
|
+
dbc.Card([
|
217
|
+
dbc.CardHeader(html.B(f"Workunit {wu['id']}")),
|
218
|
+
dbc.CardBody([
|
219
|
+
html.P(f"Name: {wu.get('name', 'n/a')}"),
|
220
|
+
html.P(f"Description: {wu.get('description', 'n/a')}"),
|
221
|
+
html.P(f"Num Resources: {len(wu.get('resource', []))}"),
|
222
|
+
html.P(f"Created: {wu.get('created', 'n/a')}"),
|
223
|
+
html.P(f"Status: {wu.get('status', 'n/a')}")
|
224
|
+
])
|
225
|
+
], style={"width": "400px", "margin":"10px"}),
|
226
|
+
href=environment_urls[token_data.get("environment", "Test")] + str(wu["id"]),
|
227
|
+
target="_blank",
|
228
|
+
style={"text-decoration": "none"}
|
229
|
+
)
|
230
|
+
|
231
|
+
wu_cards.append(wu_card)
|
232
|
+
|
233
|
+
return dbc.Container(wu_cards, style={"display": "flex", "flex-wrap": "wrap"})
|
234
|
+
else:
|
235
|
+
return html.Div()
|
236
|
+
|
237
|
+
def get_redis_queue_layout():
|
238
|
+
# Get all queues dynamically
|
239
|
+
queues = Queue.all(connection=redis_conn)
|
240
|
+
|
241
|
+
queue_cards = []
|
242
|
+
|
243
|
+
print("QUEUES", queues)
|
244
|
+
|
245
|
+
for queue in queues:
|
246
|
+
queue_name = queue.name
|
247
|
+
|
248
|
+
# Get queue stats
|
249
|
+
started_registry = StartedJobRegistry(queue_name, connection=redis_conn)
|
250
|
+
failed_registry = FailedJobRegistry(queue_name, connection=redis_conn)
|
251
|
+
finished_registry = FinishedJobRegistry(queue_name, connection=redis_conn)
|
252
|
+
|
253
|
+
stats = {
|
254
|
+
"Jobs in queue": queue.count,
|
255
|
+
"Running": started_registry.count,
|
256
|
+
"Failed": failed_registry.count,
|
257
|
+
"Completed": finished_registry.count,
|
258
|
+
}
|
259
|
+
|
260
|
+
print("STAT", stats)
|
261
|
+
|
262
|
+
stats_row = dbc.Row([
|
263
|
+
dbc.Col([
|
264
|
+
html.P([html.B("Jobs in queue: "), f"{queue.count}"]),
|
265
|
+
html.P([html.B("Running: "), f"{started_registry.count}"]),
|
266
|
+
],width=6),
|
267
|
+
dbc.Col([
|
268
|
+
html.P([html.B("Failed: "), f"{failed_registry.count}"]),
|
269
|
+
html.P([html.B("Completed: "), f"{finished_registry.count}"]),
|
270
|
+
], width=6)
|
271
|
+
])
|
272
|
+
|
273
|
+
# Fetch job details
|
274
|
+
job_cards = []
|
275
|
+
for job_id in started_registry.get_job_ids():
|
276
|
+
job = queue.fetch_job(job_id)
|
277
|
+
if job:
|
278
|
+
job_cards.append(
|
279
|
+
dbc.Card(
|
280
|
+
dbc.CardBody([
|
281
|
+
html.H6(f"Job ID: {job.id}", className="card-title"),
|
282
|
+
html.P(f"Function: {job.func_name}", className="card-text"),
|
283
|
+
html.P(f"Status: Running", className="text-success"),
|
284
|
+
]),
|
285
|
+
style={"maxWidth": "36vw", "backgroundColor": "#d4edda"}, className="mb-2"
|
286
|
+
)
|
287
|
+
)
|
288
|
+
|
289
|
+
for job_id in failed_registry.get_job_ids():
|
290
|
+
job = queue.fetch_job(job_id)
|
291
|
+
if job:
|
292
|
+
job_cards.append(
|
293
|
+
dbc.Card(
|
294
|
+
dbc.CardBody([
|
295
|
+
html.H6(f"Job ID: {job.id}", className="card-title"),
|
296
|
+
html.P(f"Function: {job.func_name}", className="card-text"),
|
297
|
+
html.P(f"Status: Failed", className="text-danger"),
|
298
|
+
]),
|
299
|
+
style={"maxWidth": "36vw", "backgroundColor": "#f8d7da"}, className="mb-2"
|
300
|
+
)
|
301
|
+
)
|
302
|
+
|
303
|
+
for job_id in finished_registry.get_job_ids():
|
304
|
+
job = queue.fetch_job(job_id)
|
305
|
+
if job:
|
306
|
+
finished_time = job.ended_at.strftime("%Y-%m-%d %H:%M:%S") if job.ended_at else "Unknown"
|
307
|
+
job_cards.append(
|
308
|
+
dbc.Card(
|
309
|
+
dbc.CardBody([
|
310
|
+
html.H6(f"Job ID: {job.id}", className="card-title"),
|
311
|
+
html.P(f"Function: {job.func_name}", className="card-text"),
|
312
|
+
html.P(f"Status: Completed", className="text-primary"),
|
313
|
+
html.P(f"Finished at: {finished_time}", className="text-muted"),
|
314
|
+
]),
|
315
|
+
style={"maxWidth": "36vw", "backgroundColor": "#d1ecf1"}, className="mb-2"
|
316
|
+
)
|
317
|
+
)
|
318
|
+
|
319
|
+
# Create queue card
|
320
|
+
queue_card = dbc.Col([
|
321
|
+
dbc.Card(
|
322
|
+
[
|
323
|
+
dbc.CardHeader(html.H5(f"Queue: {queue_name}")),
|
324
|
+
dbc.CardBody([
|
325
|
+
stats_row, # Fixed: Convert dictionary to list
|
326
|
+
html.Hr(),
|
327
|
+
*job_cards # Add job sub-cards
|
328
|
+
], style={"maxHeight": "58vh", "overflow-y": "scroll"})
|
329
|
+
],
|
330
|
+
style={"maxWidth": "36vw", "backgroundColor": "#f8f9fa", "max-height":"60vh"}, className="mb-4"
|
331
|
+
)
|
332
|
+
])
|
333
|
+
|
334
|
+
queue_cards.append(queue_card)
|
335
|
+
|
336
|
+
container_children = dbc.Row(queue_cards)
|
337
|
+
|
338
|
+
return dbc.Container(container_children, className="mt-4")
|
@@ -0,0 +1,38 @@
|
|
1
|
+
from pydantic_settings import BaseSettings
|
2
|
+
from pydantic import EmailStr
|
3
|
+
|
4
|
+
class Settings(BaseSettings):
|
5
|
+
|
6
|
+
REDIS_HOST: str = "localhost"
|
7
|
+
REDIS_PORT: int = 6379
|
8
|
+
|
9
|
+
CONFIG_FILE_PATH: str = "~/.bfabricpy.yml"
|
10
|
+
|
11
|
+
HOST: str = "127.0.0.1"
|
12
|
+
PORT: int = 8050
|
13
|
+
|
14
|
+
DEV: bool = False
|
15
|
+
DEBUG: bool = False
|
16
|
+
|
17
|
+
DEVELOPER_EMAIL_ADDRESS: EmailStr = "griffin@gwcustom.com"
|
18
|
+
BUG_REPORT_EMAIL_ADDRESS: EmailStr = "gwtools@fgcz.system"
|
19
|
+
|
20
|
+
#Run main pipeline config (only FGCZ specific)
|
21
|
+
GSTORE_REMOTE_PATH: str = "/path/to/remote/gstore"
|
22
|
+
SCRATCH_PATH: str = "/scratch/folder"
|
23
|
+
TRX_LOGIN: str = "trxcopy@fgcz-server.uzh.ch"
|
24
|
+
TRX_SSH_KEY: str = "/home/user/.ssh/your_ssh_key"
|
25
|
+
URL: str = "https:/fgcz/dummy/url"
|
26
|
+
|
27
|
+
class Config:
|
28
|
+
|
29
|
+
env_file = ".env"
|
30
|
+
|
31
|
+
# Disable reading from environment variables
|
32
|
+
@classmethod
|
33
|
+
def customise_sources(cls, init_settings, env_settings, file_secret_settings):
|
34
|
+
return file_secret_settings, init_settings
|
35
|
+
|
36
|
+
# Instantiate settings
|
37
|
+
settings = Settings()
|
38
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import redis
|
2
|
+
from rq import Worker, Queue, Connection
|
3
|
+
import time
|
4
|
+
|
5
|
+
def test_job():
|
6
|
+
|
7
|
+
"""
|
8
|
+
A test job that prints a message to the console.
|
9
|
+
"""
|
10
|
+
print("Hello, this is a test job!")
|
11
|
+
time.sleep(10)
|
12
|
+
print("Test job finished!")
|
13
|
+
return
|
14
|
+
|
15
|
+
|
16
|
+
def run_worker(host, port, queue_names):
|
17
|
+
"""
|
18
|
+
Provides internal interface for running workers on a specified host and port.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
host (str): The host to run
|
22
|
+
port (int): The port to run
|
23
|
+
queue_names (list): A list of queue names to listen to
|
24
|
+
"""
|
25
|
+
conn = redis.Redis(host=host, port=port)
|
26
|
+
with Connection(conn):
|
27
|
+
worker = Worker(map(Queue, queue_names))
|
28
|
+
worker.work()
|