bfabric-web-apps 0.1.1__py3-none-any.whl → 0.1.3__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.
@@ -1,25 +1,34 @@
1
+ import os
2
+
1
3
  # Export objects and classes
2
4
  from bfabric_web_apps.objects import BfabricInterface, Logger
3
5
 
4
6
  # Export components
5
- from .utils.components import *
7
+ from .utils import components
6
8
 
7
9
  # Export layouts
8
10
  from .layouts.layouts import get_static_layout
9
11
 
10
12
  # Export app initialization utilities
11
13
  from .utils.app_init import create_app
12
- from .utils.app_config import load_config
13
14
  from .utils.get_logger import get_logger
14
15
  from .utils.get_power_user_wrapper import get_power_user_wrapper
16
+ from .utils.create_app_in_bfabric import create_app_in_bfabric
15
17
 
16
18
  # Export callbacks
17
19
  from .utils.callbacks import process_url_and_token, submit_bug_report
18
20
 
19
- HOST = '0.0.0.0'
20
- PORT = 8050
21
- DEV = False
22
- CONFIG_FILE_PATH = "~/.bfabricpy.yml"
21
+ from .utils import defaults
22
+
23
+ from bfabric_web_apps.utils.resource_utilities import create_workunit, create_resource
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
+
29
+ DEVELOPER_EMAIL_ADDRESS = os.getenv("DEVELOPER_EMAIL_ADDRESS", defaults.DEVELOPER_EMAIL_ADDRESS)
30
+ BUG_REPORT_EMAIL_ADDRESS = os.getenv("BUG_REPORT_EMAIL_ADDRESS", defaults.BUG_REPORT_EMAIL_ADDRESS)
31
+
23
32
 
24
33
  # Define __all__ for controlled imports
25
34
  __all__ = [
@@ -28,9 +37,64 @@ __all__ = [
28
37
  "components",
29
38
  "get_static_layout",
30
39
  "create_app",
31
- "load_config",
32
40
  "process_url_and_token",
33
41
  "submit_bug_report",
34
42
  'get_logger',
35
- 'get_power_user_wrapper'
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'
36
53
  ]
54
+
55
+
56
+
57
+ '''
58
+ import os
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
+ '''
96
+
97
+ '''
98
+ from bfabric import set_config_file_path
99
+ set_config_file_path("new/path/to/config.json")
100
+ '''
@@ -1,25 +1,38 @@
1
1
  from dash import html, dcc
2
2
  import dash_bootstrap_components as dbc
3
+ import bfabric_web_apps
3
4
 
4
- 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):
5
6
  """
6
7
  Returns a layout with static tabs for Main, Documentation, and Report a Bug.
7
8
  The main content is customizable, while the other tabs are generic.
9
+
10
+ Args:
11
+ base_title (str): The main title to be displayed in the banner.
12
+ main_content (html.Div): Content to be displayed in the "Main" tab.
13
+ documentation_content (html.Div): Content for the "Documentation" tab.
14
+
15
+ Returns:
16
+ html.Div: The complete static layout of the web app.
8
17
  """
9
18
  return html.Div(
10
19
  children=[
11
20
  dcc.Location(id='url', refresh=False),
12
21
  dcc.Store(id='token', storage_type='session'),
13
22
  dcc.Store(id='entity', storage_type='session'),
23
+ dcc.Store(id='app_data', storage_type='session'),
14
24
  dcc.Store(id='token_data', storage_type='session'),
25
+ dcc.Store(id='dynamic-link-store', storage_type='session'), # Store for dynamic job link
26
+
15
27
  dbc.Container(
16
28
  children=[
17
- # Banner
29
+ # Banner Section
18
30
  dbc.Row(
19
31
  dbc.Col(
20
32
  html.Div(
21
33
  className="banner",
22
34
  children=[
35
+ # Title
23
36
  html.Div(
24
37
  children=[
25
38
  html.P(
@@ -35,30 +48,70 @@ def get_static_layout(base_title = None, main_content = None, documentation_cont
35
48
  )
36
49
  ],
37
50
  style={"background-color": "#000000", "border-radius": "10px"}
38
- )
51
+ ),
52
+ ],
53
+ style={"position": "relative", "padding": "10px"}
54
+ ),
55
+ ),
56
+ ),
57
+
58
+ # Page Title Section + View Logs Button (Aligned Right)
59
+ dbc.Row(
60
+ dbc.Col(
61
+ html.Div(
62
+ children=[
63
+ # Page Title (Aligned Left)
64
+ html.P(
65
+ id="page-title",
66
+ children=[str(" ")],
67
+ style={"font-size": "40px", "margin-left": "20px", "margin-top": "10px"}
68
+ ),
69
+
70
+ # View Logs Button (Aligned Right)
71
+ html.Div(
72
+ children=[
73
+ html.A(
74
+ dbc.Button(
75
+ "View Logs",
76
+ id="dynamic-link-button",
77
+ color="secondary", # Greyish color
78
+ style={
79
+ "font-size": "18px",
80
+ "padding": "10px 20px",
81
+ "border-radius": "8px"
82
+ }
83
+ ),
84
+ id="dynamic-link",
85
+ href="#", # Will be dynamically set in the callback
86
+ target="_blank"
87
+ )
88
+ ],
89
+ style={
90
+ "position": "absolute",
91
+ "right": "20px",
92
+ "top": "10px", # Aligns with title
93
+ }
94
+ ),
39
95
  ],
96
+ style={
97
+ "position": "relative", # Ensures absolute positioning works
98
+ "margin-top": "0px",
99
+ "min-height": "80px",
100
+ "height": "6vh",
101
+ "border-bottom": "2px solid #d4d7d9",
102
+ "display": "flex",
103
+ "align-items": "center",
104
+ "justify-content": "space-between", # Title left, button right
105
+ "padding-right": "20px" # Space between button & right edge
106
+ }
40
107
  ),
41
108
  ),
42
109
  ),
43
- # Page Title
110
+
111
+ # Bug Report Alerts (Restored)
44
112
  dbc.Row(
45
113
  dbc.Col(
46
114
  [
47
- html.Div(
48
- children=[
49
- html.P(
50
- id="page-title",
51
- children=[str(" ")],
52
- style={"font-size": "40px", "margin-left": "20px", "margin-top": "10px"}
53
- )
54
- ],
55
- style={
56
- "margin-top": "0px",
57
- "min-height": "80px",
58
- "height": "6vh",
59
- "border-bottom": "2px solid #d4d7d9"
60
- }
61
- ),
62
115
  dbc.Alert(
63
116
  "Your bug report has been submitted. Thanks for helping us improve!",
64
117
  id="alert-fade-bug-success",
@@ -86,7 +139,8 @@ def get_static_layout(base_title = None, main_content = None, documentation_cont
86
139
  ]
87
140
  )
88
141
  ),
89
- # Tabs
142
+
143
+ # Tabs Section
90
144
  dbc.Tabs(
91
145
  [
92
146
  dbc.Tab(main_content, label="Main", tab_id="main"),
@@ -167,8 +221,8 @@ def get_report_bug_tab():
167
221
  [
168
222
  "Please use the form below to report a bug. If you have any questions, please email the developer at ",
169
223
  html.A(
170
- "griffin@gwcustom.com",
171
- href="mailto:griffin@gwcustom.com",
224
+ bfabric_web_apps.DEVELOPER_EMAIL_ADDRESS,
225
+ href=f"mailto:{bfabric_web_apps.DEVELOPER_EMAIL_ADDRESS}",
172
226
  ),
173
227
  ]
174
228
  ),
@@ -5,10 +5,10 @@ import datetime
5
5
  import bfabric
6
6
  from bfabric import BfabricAuth
7
7
  from bfabric import BfabricClientConfig
8
- from dash import html
9
- import dash_bootstrap_components as dbc
10
- from bfabric_web_apps.objects.Logger import Logger
8
+ from bfabric_web_apps.utils.get_logger import get_logger
11
9
  import os
10
+ import bfabric_web_apps
11
+
12
12
 
13
13
 
14
14
  VALIDATION_URL = "https://fgcz-bfabric.uzh.ch/bfabric/rest/token/validate?token="
@@ -16,6 +16,8 @@ HOST = "fgcz-bfabric.uzh.ch"
16
16
 
17
17
 
18
18
  class BfabricInterface( Bfabric ):
19
+ _instance = None # Singleton instance
20
+ _wrapper = None # Shared wrapper instance
19
21
  """
20
22
  A class to interface with the Bfabric API, providing methods to validate tokens,
21
23
  retrieve data, and send bug reports.
@@ -27,6 +29,30 @@ class BfabricInterface( Bfabric ):
27
29
  """
28
30
  pass
29
31
 
32
+ def __new__(cls, *args, **kwargs):
33
+ """Ensure only one instance exists (Singleton Pattern)."""
34
+ if cls._instance is None:
35
+ cls._instance = super(BfabricInterface, cls).__new__(cls)
36
+ return cls._instance
37
+
38
+ def _initialize_wrapper(self, token_data):
39
+ """Internal method to initialize the Bfabric wrapper after token validation."""
40
+ if not token_data:
41
+ raise ValueError("Token data is required to initialize the wrapper.")
42
+
43
+ # Create and store the wrapper
44
+ if self._wrapper is None:
45
+ self._wrapper = self.token_response_to_bfabric(token_data)
46
+
47
+
48
+ def get_wrapper(self):
49
+ """Return the existing wrapper or raise an error if not initialized."""
50
+ if self._wrapper is None:
51
+ raise RuntimeError("Bfabric wrapper is not initialized. Token validation must run first.")
52
+ return self._wrapper
53
+
54
+
55
+
30
56
  def token_to_data(self, token):
31
57
  """
32
58
  Validates the given token and retrieves its associated data.
@@ -70,6 +96,8 @@ class BfabricInterface( Bfabric ):
70
96
 
71
97
  environment_dict = {"Production":"https://fgcz-bfabric.uzh.ch/bfabric","Test":"https://fgcz-bfabric-test.uzh.ch/bfabric"}
72
98
 
99
+ print('userinfo', userinfo)
100
+
73
101
  token_data = dict(
74
102
  environment = userinfo['environment'],
75
103
  user_data = userinfo['user'],
@@ -83,6 +111,9 @@ class BfabricInterface( Bfabric ):
83
111
  jobId = userinfo['jobId']
84
112
  )
85
113
 
114
+ # Initialize the wrapper right after validating the token
115
+ self._initialize_wrapper(token_data)
116
+
86
117
  return json.dumps(token_data)
87
118
 
88
119
 
@@ -110,7 +141,6 @@ class BfabricInterface( Bfabric ):
110
141
 
111
142
 
112
143
  def entity_data(self, token_data: dict) -> str:
113
-
114
144
  """
115
145
  Retrieves entity data associated with the provided token.
116
146
 
@@ -119,7 +149,7 @@ class BfabricInterface( Bfabric ):
119
149
 
120
150
  Returns:
121
151
  str: A JSON string containing entity data.
122
- None: If the retrieval fails.
152
+ {}: If the retrieval fails or token_data is invalid.
123
153
  """
124
154
 
125
155
  entity_class_map = {
@@ -132,24 +162,18 @@ class BfabricInterface( Bfabric ):
132
162
  }
133
163
 
134
164
  if not token_data:
135
- return None
136
-
137
- wrapper = self.token_response_to_bfabric(token_data)
165
+ return json.dumps({})
166
+
167
+ wrapper = self.get_wrapper()
138
168
  entity_class = token_data.get('entityClass_data', None)
139
169
  endpoint = entity_class_map.get(entity_class, None)
140
170
  entity_id = token_data.get('entity_id_data', None)
141
171
  jobId = token_data.get('jobId', None)
142
172
  username = token_data.get("user_data", "None")
143
- environment= token_data.get("environment", "None")
144
-
173
+ environment = token_data.get("environment", "None")
145
174
 
146
175
  if wrapper and entity_class and endpoint and entity_id and jobId:
147
-
148
- L = Logger(
149
- jobid = jobId,
150
- username= username,
151
- environment= environment
152
- )
176
+ L = get_logger(token_data)
153
177
 
154
178
  # Log the read operation directly using Logger L
155
179
  entity_data_dict = L.logthis(
@@ -157,9 +181,10 @@ class BfabricInterface( Bfabric ):
157
181
  endpoint=endpoint,
158
182
  obj={"id": entity_id},
159
183
  max_results=None,
160
- params = None,
161
- flush_logs = True
184
+ params=None,
185
+ flush_logs=True
162
186
  )[0]
187
+
163
188
 
164
189
  if entity_data_dict:
165
190
  json_data = json.dumps({
@@ -171,18 +196,86 @@ class BfabricInterface( Bfabric ):
171
196
  return json_data
172
197
  else:
173
198
  L.log_operation(
174
- operation= "entity_data",
175
- message= "Entity data retrieval failed or returned None.",
199
+ operation="entity_data",
200
+ message="Entity data retrieval failed or returned None.",
176
201
  params=None,
177
202
  flush_logs=True
178
203
  )
179
204
  print("entity_data_dict is empty or None")
180
- return None
205
+ return json.dumps({})
181
206
 
182
207
  else:
183
208
  print("Invalid input or entity information")
184
- return None
209
+ return json.dumps({})
185
210
 
211
+
212
+ def app_data(self, token_data: dict) -> str:
213
+ """
214
+ Retrieves application data (App Name and Description) associated with the provided token.
215
+
216
+ Args:
217
+ token_data (dict): The token data.
218
+
219
+ Returns:
220
+ str: A JSON string containing application data.
221
+ {}: If retrieval fails or token_data is invalid.
222
+ """
223
+
224
+ if not token_data:
225
+ return json.dumps({}) # Return empty JSON if no token data
226
+
227
+ # Extract App ID from token
228
+ app_data_raw = token_data.get("application_data", None)
229
+
230
+ try:
231
+ app_id = int(app_data_raw)
232
+ except:
233
+ print("Invalid application_data format in token_data")
234
+ return json.dumps({}) # Return empty JSON if app_id is invalid
235
+
236
+ # Define API endpoint
237
+ endpoint = "application"
238
+
239
+ # Initialize Logger
240
+ L = get_logger(token_data)
241
+
242
+ # Get API wrapper
243
+ wrapper = self.get_wrapper()
244
+ if not wrapper:
245
+ print("Failed to get Bfabric API wrapper")
246
+ return json.dumps({})
247
+
248
+ # Make API Call
249
+ app_data_dict = L.logthis(
250
+ api_call=wrapper.read,
251
+ endpoint=endpoint,
252
+ obj={"id": app_id}, # Query using the App ID
253
+ max_results=None,
254
+ params=None,
255
+ flush_logs=True
256
+ )
257
+
258
+ # If API call fails, return empty JSON
259
+ if not app_data_dict or len(app_data_dict) == 0:
260
+ L.log_operation(
261
+ operation="app_data",
262
+ message=f"Failed to retrieve application data for App ID {app_id}",
263
+ params=None,
264
+ flush_logs=True
265
+ )
266
+ return json.dumps({})
267
+
268
+ # Extract App ID, Name, and Description
269
+ app_info = app_data_dict[0] # First (and only) result
270
+ print('app_info', app_info)
271
+ json_data = json.dumps({
272
+ "id": app_info.get("id", "Unknown"),
273
+ "name": app_info.get("name", "Unknown"),
274
+ "description": app_info.get("description", "No description available")
275
+ })
276
+
277
+ return json_data
278
+
186
279
 
187
280
  def send_bug_report(self, token_data = None, entity_data = None, description = None):
188
281
  """
@@ -207,7 +300,7 @@ class BfabricInterface( Bfabric ):
207
300
  """
208
301
 
209
302
  mail = f"""
210
- echo "{mail_string}" | mail -s "Bug Report" gwtools@fgcz.system
303
+ echo "{mail_string}" | mail -s "Bug Report" {bfabric_web_apps.BUG_REPORT_EMAIL_ADDRESS}
211
304
  """
212
305
 
213
306
  print("MAIL STRING:")
@@ -220,3 +313,10 @@ class BfabricInterface( Bfabric ):
220
313
 
221
314
  return True
222
315
 
316
+
317
+
318
+
319
+ # Create a globally accessible instance
320
+ bfabric_interface = BfabricInterface()
321
+
322
+
@@ -4,9 +4,8 @@ from typing import List
4
4
  from bfabric import Bfabric
5
5
  from datetime import datetime as dt
6
6
  import base64
7
- from ..utils.app_config import load_config
7
+ import bfabric_web_apps
8
8
 
9
- CONFIG_FILE_PATH = load_config()["CONFIG_FILE_PATH"]
10
9
 
11
10
  class Logger:
12
11
  """
@@ -23,6 +22,7 @@ class Logger:
23
22
  """
24
23
  self.jobid = jobid
25
24
  self.username = username
25
+ self.config_file_path = bfabric_web_apps.CONFIG_FILE_PATH
26
26
  self.power_user_wrapper = self._get_power_user_wrapper(environment)
27
27
  self.logs = []
28
28
 
@@ -37,7 +37,7 @@ class Logger:
37
37
  Bfabric: An authenticated Bfabric instance.
38
38
  """
39
39
  power_user_wrapper = Bfabric.from_config(
40
- config_path = os.path.expanduser(CONFIG_FILE_PATH),
40
+ config_path = os.path.expanduser(self.config_file_path),
41
41
  config_env = environment.upper()
42
42
  )
43
43
  return power_user_wrapper
@@ -2,25 +2,30 @@ from dash import Input, Output, State, html, dcc
2
2
  from bfabric_web_apps.objects.BfabricInterface import BfabricInterface
3
3
  import json
4
4
  import dash_bootstrap_components as dbc
5
- from bfabric_web_apps.objects.Logger import Logger
6
5
  from datetime import datetime as dt
6
+ from bfabric_web_apps.utils.get_logger import get_logger
7
7
 
8
8
  def process_url_and_token(url_params):
9
9
  """
10
10
  Processes URL parameters to extract the token, validates it, and retrieves the corresponding data.
11
+ Additionally, it constructs a dynamic job link based on the environment and job ID.
11
12
 
12
13
  Args:
13
14
  url_params (str): The URL parameters containing the token.
14
- base_title (str): The base title of the page.
15
15
 
16
16
  Returns:
17
- tuple: A tuple containing token data, entity data, and the page content.
18
- (token, token_data, entity_data, page_content, page_title)
17
+ tuple: A tuple containing:
18
+ - token (str): Authentication token.
19
+ - token_data (dict): Token metadata.
20
+ - entity_data (dict): Retrieved entity information.
21
+ - page_title (str): Title for the page header.
22
+ - session_details (list): HTML-formatted session details.
23
+ - job_link (str): Dynamically generated link to the job page.
19
24
  """
20
25
  base_title = " "
21
26
 
22
27
  if not url_params:
23
- return None, None, None, base_title, None
28
+ return None, None, None, None, base_title, None, None
24
29
 
25
30
  token = "".join(url_params.split('token=')[1:])
26
31
  bfabric_interface = BfabricInterface()
@@ -28,43 +33,60 @@ def process_url_and_token(url_params):
28
33
 
29
34
  if tdata_raw:
30
35
  if tdata_raw == "EXPIRED":
31
- return None, None, None, base_title, None
36
+ return None, None, None, None, base_title, None, None
32
37
  else:
33
38
  tdata = json.loads(tdata_raw)
34
39
  else:
35
- return None, None, None, base_title, None
40
+ return None, None, None, None, base_title, None, None
36
41
 
37
42
  if tdata:
38
43
  entity_data_json = bfabric_interface.entity_data(tdata)
44
+ app_data_json = bfabric_interface.app_data(tdata)
39
45
  entity_data = json.loads(entity_data_json)
46
+ app_data = json.loads(app_data_json)
40
47
  page_title = (
41
- f"{tdata['entityClass_data']} - {entity_data['name']} "
42
- f"({tdata['environment']} System)"
48
+ f"{tdata.get('entityClass_data', 'Unknown')} - {entity_data.get('name', 'Unknown')} "
49
+ f"({tdata.get('environment', 'Unknown')} System)"
43
50
  ) if tdata else "Bfabric App Interface"
44
51
 
45
- if not entity_data:
46
- return token, tdata, None, page_title, None
47
- else:
48
- session_details = [
52
+ environment = tdata.get("environment", "").strip().lower() # 'test' or 'prod'
53
+ job_id = tdata.get("jobId", None) # Extract job ID
54
+
55
+ job_link = None
56
+ if job_id:
57
+ if "test" in environment:
58
+ job_link = f"https://fgcz-bfabric-test.uzh.ch/bfabric/job/show.html?id={job_id}&tab=details"
59
+ else:
60
+ job_link = f"https://fgcz-bfabric.uzh.ch/bfabric/job/show.html?id={job_id}&tab=details"
61
+
62
+ session_details = [
49
63
  html.P([
50
- html.B("Entity Name: "), entity_data['name'],
64
+ html.B("Entity Name: "), entity_data.get('name', 'Unknown'),
65
+ html.Br(),
66
+ html.B("Entity Class: "), tdata.get('entityClass_data', 'Unknown'),
51
67
  html.Br(),
52
- html.B("Entity Class: "), tdata['entityClass_data'],
68
+ html.B("Environment: "), tdata.get('environment', 'Unknown'),
53
69
  html.Br(),
54
- html.B("Environment: "), tdata['environment'],
70
+ html.B("Entity ID: "), tdata.get('entity_id_data', 'Unknown'),
55
71
  html.Br(),
56
- html.B("Entity ID: "), tdata['entity_id_data'],
72
+ html.B("Job ID: "), job_id if job_id else "Unknown",
57
73
  html.Br(),
58
- html.B("User Name: "), tdata['user_data'],
74
+ html.B("User Name: "), tdata.get('user_data', 'Unknown'),
59
75
  html.Br(),
60
- html.B("Session Expires: "), tdata['token_expires'],
76
+ html.B("Session Expires: "), tdata.get('token_expires', 'Unknown'),
77
+ html.Br(),
78
+ html.B("App Name: "), app_data.get("name", "Unknown"),
79
+ html.Br(),
80
+ html.B("App Description: "), app_data.get("description", "No description available"),
61
81
  html.Br(),
62
82
  html.B("Current Time: "), str(dt.now().strftime("%Y-%m-%d %H:%M:%S"))
63
83
  ])
64
84
  ]
65
- return token, tdata, entity_data, page_title, session_details
85
+
86
+ return token, tdata, entity_data, app_data, page_title, session_details, job_link
66
87
  else:
67
- return None, None, None, base_title, None
88
+ return None, None, None, None, base_title, None, None
89
+
68
90
 
69
91
 
70
92
  def submit_bug_report(n_clicks, bug_description, token, entity_data):
@@ -100,11 +122,7 @@ def submit_bug_report(n_clicks, bug_description, token, entity_data):
100
122
  # Initialize the logger only if token_data is available
101
123
  L = None
102
124
  if token_data:
103
- L = Logger(
104
- jobid=jobId,
105
- username=username,
106
- environment=environment
107
- )
125
+ L = get_logger(token_data)
108
126
 
109
127
  if n_clicks:
110
128
  # Log the operation only if the logger is initialized
@@ -0,0 +1,86 @@
1
+ from bfabric import Bfabric
2
+
3
+ def get_user_input():
4
+ """Prompt user for necessary inputs."""
5
+
6
+ systems = {"TEST": "TEST", "PROD": "PRODUCTION"}
7
+
8
+ technologies = {
9
+ "TEST": {
10
+ "1": "Genomics / Transcriptomics",
11
+ "2": "Proteomics",
12
+ "4": "Metabolomics / Biophysics",
13
+ "6": "General",
14
+ "10": "New Tech"
15
+ },
16
+ "PRODUCTION": {
17
+ "1": "Genomics / Transcriptomics",
18
+ "2": "Proteomics",
19
+ "4": "Metabolomics / Biophysics",
20
+ "6": "General",
21
+ "10": "Bioinformatics"
22
+ }
23
+ }
24
+
25
+ # Get system input
26
+ system = input("In which system do you want to create the app? (Type 'TEST' for the test system or 'PROD' for the production system): ").strip().upper()
27
+
28
+ while system not in systems:
29
+ print("Invalid input! Please enter 'TEST' or 'PROD'.")
30
+ system = input("Enter system (TEST/PROD): ").strip().upper()
31
+
32
+ selected_system = systems[system] # Map input to full system name
33
+
34
+ # Display technology options based on selected system
35
+ print("\nAvailable Technologies:")
36
+ for key, value in technologies[selected_system].items():
37
+ print(f"{key}: {value}")
38
+
39
+ # Get technology ID from user
40
+ technologyid = input("\nEnter the number corresponding to your chosen application type: ").strip()
41
+
42
+ while technologyid not in technologies[selected_system]:
43
+ print("Invalid technology ID! Please select a valid number from the list.")
44
+ technologyid = input("Enter a valid technology ID: ").strip()
45
+
46
+ # Get remaining inputs
47
+ name = input("Enter app name: ")
48
+ weburl = input("Enter web URL: ")
49
+ description = input("Enter description: ")
50
+
51
+ return {
52
+ "system": selected_system,
53
+ "name": name,
54
+ "weburl": weburl,
55
+ "type": "WebApp",
56
+ "technologyid": technologyid,
57
+ "supervisorid": "2446",
58
+ "enabled": True,
59
+ "valid": True,
60
+ "hidden": False,
61
+ "description": description
62
+ }
63
+
64
+ def create_app_in_bfabric():
65
+ """Create an app in B-Fabric using user inputs."""
66
+ # Get user input for parameters
67
+ user_input = get_user_input()
68
+
69
+ # Determine configuration environment based on user input
70
+ config_env = user_input.pop("system")
71
+
72
+ # Initialize Bfabric instance
73
+ bfabric = Bfabric.from_config(config_env=config_env)
74
+
75
+ # Set endpoint for app creation
76
+ endpoint = "application"
77
+
78
+ # Make API call to save the app
79
+ try:
80
+ result = bfabric.save(endpoint=endpoint, obj=user_input)
81
+ print("App created successfully:", result)
82
+ except Exception as e:
83
+ print("Failed to create app:", str(e))
84
+
85
+ if __name__ == "__main__":
86
+ create_app_in_bfabric()
@@ -0,0 +1,11 @@
1
+ # defaults.py
2
+ CONFIG_FILE_PATH = "~/.bfabricpy.yml"
3
+
4
+ # Default values for application settings
5
+ HOST = "0.0.0.0"
6
+ PORT = 8050
7
+ DEV = False
8
+
9
+ # Developer and bug report email addresses
10
+ DEVELOPER_EMAIL_ADDRESS = "griffin@gwcustom.com"
11
+ BUG_REPORT_EMAIL_ADDRESS = "gwtools@fgcz.system"
@@ -10,5 +10,5 @@ def get_logger(token_data):
10
10
  return Logger(
11
11
  jobid=jobId,
12
12
  username=username,
13
- environment=environment
13
+ environment=environment,
14
14
  )
@@ -1,8 +1,6 @@
1
1
  import os
2
2
  from bfabric import Bfabric
3
- from .app_config import load_config
4
-
5
- CONFIG_FILE_PATH = load_config()["CONFIG_FILE_PATH"]
3
+ import bfabric_web_apps
6
4
 
7
5
  def get_power_user_wrapper(token_data):
8
6
  """
@@ -13,7 +11,7 @@ def get_power_user_wrapper(token_data):
13
11
  determined by the `CONFIG_FILE_PATH` from the application's configuration.
14
12
 
15
13
  Args:
16
- token_data (dict): A dictionary containing token information.
14
+ token_data (dict): A dictionary containing token information
17
15
  The key "environment" is used to determine the environment
18
16
  (default is "None" if not specified).
19
17
 
@@ -24,6 +22,6 @@ def get_power_user_wrapper(token_data):
24
22
  environment = token_data.get("environment", "None")
25
23
 
26
24
  return Bfabric.from_config(
27
- config_path = os.path.expanduser(CONFIG_FILE_PATH),
25
+ config_path = os.path.expanduser(bfabric_web_apps.CONFIG_FILE_PATH),
28
26
  config_env = environment.upper()
29
27
  )
@@ -0,0 +1,218 @@
1
+ from bfabric_web_apps.utils.get_logger import get_logger
2
+ from bfabric_web_apps.objects.BfabricInterface import bfabric_interface
3
+ from bfabric_web_apps.utils.get_power_user_wrapper import get_power_user_wrapper
4
+ from bfabric_scripts.bfabric_upload_resource import bfabric_upload_resource
5
+ from pathlib import Path
6
+
7
+ def create_workunit(token_data, application_name, application_description, application_id, container_ids):
8
+ """
9
+ Create a new workunit in B-Fabric for each container ID.
10
+
11
+ Args:
12
+ token_data (dict): Authentication token data.
13
+ application_name (str): Name of the application.
14
+ application_description (str): Description of the application.
15
+ application_id (int): Application ID.
16
+ container_ids (list): List of container IDs.
17
+
18
+ Returns:
19
+ list: List of created workunit IDs.
20
+ """
21
+ L = get_logger(token_data)
22
+ wrapper = bfabric_interface.get_wrapper()
23
+ workunit_ids = []
24
+
25
+ # Ensure container_ids is a list
26
+ if not isinstance(container_ids, list):
27
+ container_ids = [container_ids] # Convert to list if single value
28
+
29
+ for container_id in container_ids:
30
+ workunit_data = {
31
+ "name": f"{application_name} - Order {container_id}",
32
+ "description": f"{application_description} for Order {container_id}",
33
+ "applicationid": int(application_id),
34
+ "containerid": container_id, # Assigning order ID dynamically
35
+ }
36
+
37
+ try:
38
+ workunit_response = L.logthis(
39
+ api_call=wrapper.save,
40
+ endpoint="workunit",
41
+ obj=workunit_data,
42
+ params=None,
43
+ flush_logs=True
44
+ )
45
+ workunit_id = workunit_response[0].get("id")
46
+ print(f"Created Workunit ID: {workunit_id} for Order ID: {container_id}")
47
+ workunit_ids.append(workunit_id)
48
+
49
+ except Exception as e:
50
+ L.log_operation(
51
+ "Error",
52
+ f"Failed to create workunit for Order {container_id}: {e}",
53
+ params=None,
54
+ flush_logs=True,
55
+ )
56
+ print(f"Failed to create workunit for Order {container_id}: {e}")
57
+
58
+ return workunit_ids # Returning a list of all created workunits
59
+
60
+
61
+ def create_resource(token_data, workunit_id, gz_file_path):
62
+ """
63
+ Upload a .gz resource to an existing B-Fabric workunit.
64
+
65
+ Args:
66
+ token_data (dict): Authentication token data.
67
+ workunit_id (int): ID of the workunit to associate the resource with.
68
+ gz_file_path (str): Full path to the .gz file to upload.
69
+
70
+ Returns:
71
+ int: Resource ID if successful, None otherwise.
72
+ """
73
+ L = get_logger(token_data)
74
+ wrapper = get_power_user_wrapper(token_data)
75
+
76
+ try:
77
+ file_path = Path(gz_file_path)
78
+
79
+ # Use the proper upload function
80
+ print("test", wrapper, file_path, workunit_id)
81
+ result = bfabric_upload_resource(wrapper, file_path, workunit_id)
82
+
83
+ if result:
84
+ print(f"Resource uploaded: {file_path.name}")
85
+ L.log_operation(
86
+ "upload_resource",
87
+ f"Resource uploaded successfully: {file_path.name}",
88
+ params=None,
89
+ flush_logs=True,
90
+ )
91
+ return result
92
+ else:
93
+ raise ValueError(f"Failed to upload resource: {file_path.name}")
94
+
95
+ except Exception as e:
96
+ L.log_operation(
97
+ "error",
98
+ f"Failed to upload resource: {e}",
99
+ params=None,
100
+ flush_logs=True,
101
+ )
102
+ print(f"Failed to upload resource: {e}")
103
+ return None
104
+
105
+
106
+
107
+ '''
108
+
109
+
110
+
111
+ # Upload a resource to the created workunit
112
+ resource_name = "example_resource.txt"
113
+ resource_content = b"This is an example resource content."
114
+
115
+ try:
116
+ resource_response = bfabric.upload_resource(
117
+ resource_name=resource_name,
118
+ content=resource_content,
119
+ workunit_id=workunit_id
120
+ )
121
+ print(f"Resource '{resource_name}' uploaded successfully.")
122
+ except Exception as e:
123
+ print(f"Failed to upload resource: {e}")
124
+ exit(1)
125
+
126
+
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+ import subprocess
135
+ from zeep import Client
136
+ import os
137
+ from bfabric_web_apps.utils.get_logger import get_logger
138
+
139
+ BFABRIC_WORKUNIT_WSDL = "https://fgcz-bfabric-test.uzh.ch:443/bfabric/workunit?wsdl"
140
+ BFABRIC_RESOURCE_WSDL = "https://fgcz-bfabric-test.uzh.ch:443/bfabric/resource?wsdl"
141
+
142
+ def run_pipeline_and_register_in_bfabric(run_name: str, output_dir: str):
143
+ """
144
+ Startet die Nextflow-Pipeline und speichert die Ergebnisse in B-Fabric.
145
+
146
+ :param run_name: Name des Sequenzierungslaufs
147
+ :param output_dir: Verzeichnis, in dem die FASTQ-Dateien gespeichert werden
148
+ """
149
+ print(f"[INFO] Starte Nextflow-Pipeline für {run_name}...")
150
+
151
+ # Nextflow Pipeline starten
152
+ process = subprocess.run([
153
+ "nextflow", "run", "nf-core/bclconvert",
154
+ "-profile", "docker",
155
+ "--outdir", output_dir,
156
+ "-resume"
157
+ ], capture_output=True, text=True)
158
+
159
+ if process.returncode != 0:
160
+ print(f"[ERROR] Nextflow Pipeline fehlgeschlagen: {process.stderr}")
161
+ return False
162
+
163
+ print(f"[INFO] Pipeline abgeschlossen. Ergebnisse werden registriert...")
164
+
165
+ # Workunit in B-Fabric anlegen
166
+ workunit_id = create_bfabric_workunit(run_name)
167
+
168
+ # Falls Workunit erfolgreich erstellt, dann Ressourcen speichern
169
+ if workunit_id:
170
+ register_fastq_files_in_bfabric(output_dir, workunit_id)
171
+ else:
172
+ print("[ERROR] Konnte Workunit nicht in B-Fabric registrieren!")
173
+
174
+ return True
175
+
176
+ def create_bfabric_workunit(run_name: str):
177
+ """Erstellt eine Workunit in B-Fabric."""
178
+ try:
179
+ client = Client(BFABRIC_WORKUNIT_WSDL)
180
+ workunit_data = {
181
+ "name": run_name,
182
+ "description": "Illumina BCL zu FASTQ Konvertierung",
183
+ "status": "Completed"
184
+ }
185
+ L = get_logger({})
186
+ response = L.logthis(
187
+ api_call=client.service.createWorkunit,
188
+ obj=workunit_data
189
+ )[0]
190
+ print(f"[INFO] Workunit erstellt mit ID: {response}")
191
+ return response
192
+ except Exception as e:
193
+ print(f"[ERROR] Fehler beim Erstellen der Workunit: {e}")
194
+ return None
195
+
196
+ def register_fastq_files_in_bfabric(output_dir: str, workunit_id: int):
197
+ """Registriert alle FASTQ-Dateien aus dem Output-Verzeichnis in B-Fabric."""
198
+ try:
199
+ client = Client(BFABRIC_RESOURCE_WSDL)
200
+ L = get_logger({})
201
+ for file_name in os.listdir(output_dir):
202
+ if file_name.endswith(".fastq.gz"):
203
+ file_path = os.path.join(output_dir, file_name)
204
+ resource_data = {
205
+ "name": file_name,
206
+ "description": "Erzeugt von nf-core/bclconvert",
207
+ "path": file_path,
208
+ "type": "FASTQ",
209
+ "workunitId": workunit_id
210
+ }
211
+ response = L.logthis(
212
+ api_call=client.service.createResource,
213
+ obj=resource_data
214
+ )[0]
215
+ print(f"[INFO] Ressource gespeichert mit ID: {response}")
216
+ except Exception as e:
217
+ print(f"[ERROR] Fehler beim Registrieren der Ressourcen: {e}")
218
+ '''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bfabric-web-apps
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: A package containing handy boilerplate utilities for developing bfabric web-applications
5
5
  Author: Marc Zuber, Griffin White, GWC GmbH
6
6
  Requires-Python: >=3.8,<4.0
@@ -0,0 +1,16 @@
1
+ bfabric_web_apps/__init__.py,sha256=xqh-kddXaV4xblnMcDA73bTuy3zn5viucueuuIx41HM,2495
2
+ bfabric_web_apps/layouts/layouts.py,sha256=XoSLcQPcgMBhQz2VfxkUzNL23FLBXFRFvbCL2mNLfnk,11636
3
+ bfabric_web_apps/objects/BfabricInterface.py,sha256=YC6VimARZfG2t90TK5xhQDsNzgMNljwqbCOT8Qz_Uhs,10247
4
+ bfabric_web_apps/objects/Logger.py,sha256=62LC94xhm7YG5LUw3yH46NqvJQsAX7wnc9D4zbY16rA,5224
5
+ bfabric_web_apps/utils/app_init.py,sha256=RCdpCXp19cF74bouYJLPe-KSETZ0Vwqtd02Ta2VXEF8,428
6
+ bfabric_web_apps/utils/callbacks.py,sha256=PiP1ZJ-QxdrOAZ-Mt-MN-g9wJLSOoLkWkXwPq_TLqDI,6472
7
+ bfabric_web_apps/utils/components.py,sha256=V7ECGmF2XYy5O9ciDJVH1nofJYP2a_ELQF3z3X_ADbo,844
8
+ bfabric_web_apps/utils/create_app_in_bfabric.py,sha256=eVk3cQDXxW-yo9b9n_zzGO6kLg_SLxYbIDECyvEPJXU,2752
9
+ bfabric_web_apps/utils/defaults.py,sha256=B82j3JEbysLEU9JDZgoDBTX7WGvW3Hn5YMZaWAcjZew,278
10
+ bfabric_web_apps/utils/get_logger.py,sha256=0Y3SrXW93--eglS0_ZOc34NOriAt6buFPik5n0ltzRA,434
11
+ bfabric_web_apps/utils/get_power_user_wrapper.py,sha256=T33z64XjmJ0KSlmfEmrEP8eYpbpINCVD6Xld_V7PR2g,1027
12
+ bfabric_web_apps/utils/resource_utilities.py,sha256=AVaqIXEfmCdWZfXDt32QfkZ9ChTL8D8cm1lCHXfT4Nc,7317
13
+ bfabric_web_apps-0.1.3.dist-info/LICENSE,sha256=k0O_i2k13i9e35aO-j7FerJafAqzzu8x0kkBs0OWF3c,1065
14
+ bfabric_web_apps-0.1.3.dist-info/METADATA,sha256=gTd86dYPrHKfypKFPry0SKjYtydW1z17fA-tSxz8vuM,480
15
+ bfabric_web_apps-0.1.3.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
16
+ bfabric_web_apps-0.1.3.dist-info/RECORD,,
@@ -1,27 +0,0 @@
1
- import os
2
- import importlib.util
3
-
4
- def load_config(params_path="./PARAMS.py"):
5
- """Load configuration for the Dash app."""
6
- if os.path.exists(params_path):
7
- try:
8
- # Dynamically import the PARAMS module
9
- spec = importlib.util.spec_from_file_location("PARAMS", params_path)
10
- params_module = importlib.util.module_from_spec(spec)
11
- spec.loader.exec_module(params_module)
12
-
13
- # Retrieve values with defaults
14
- PORT = getattr(params_module, "PORT", 8050)
15
- HOST = getattr(params_module, "HOST", "localhost")
16
- DEV = getattr(params_module, "DEV", True)
17
- CONFIG_FILE_PATH = getattr(params_module, "CONFIG_FILE_PATH", "~/.bfabricpy.yml")
18
- except ImportError:
19
- # Fallback to default values in case of import errors
20
- PORT, HOST, DEV = 8050, 'localhost', True
21
- CONFIG_FILE_PATH = "~/.bfabricpy.yml"
22
- else:
23
- # Fallback to default values if PARAMS.py is not found
24
- PORT, HOST, DEV = 8050, 'localhost', True
25
- CONFIG_FILE_PATH = "~/.bfabricpy.yml"
26
-
27
- return {"PORT": PORT, "HOST": HOST, "DEV": DEV, "CONFIG_FILE_PATH": CONFIG_FILE_PATH}
@@ -1,14 +0,0 @@
1
- bfabric_web_apps/__init__.py,sha256=XDzAg0boEE3nj3RXGB85YbVc0KgEeRNOcQfrSkJoV-0,880
2
- bfabric_web_apps/layouts/layouts.py,sha256=oQHfT65PdoFU5js8JkEJqFhxfgNjwU0GRvD1xbkfYD4,8758
3
- bfabric_web_apps/objects/BfabricInterface.py,sha256=pHse1UgC_XFD-PsYqOs0Ho3xTRMDRCvtRgsILVYy-0k,6919
4
- bfabric_web_apps/objects/Logger.py,sha256=ikMJJZYDPhJZEmgCY63_htGmPvXv1ItSmfb_xiFFZMQ,5225
5
- bfabric_web_apps/utils/app_config.py,sha256=u7lz8sonf9CSkJsVK0nhTzIls227FksGCOb_SzYE_fE,1204
6
- bfabric_web_apps/utils/app_init.py,sha256=RCdpCXp19cF74bouYJLPe-KSETZ0Vwqtd02Ta2VXEF8,428
7
- bfabric_web_apps/utils/callbacks.py,sha256=OlNelwAJUKsEtnaJ4OuTdrgyu9PhEgm4uqCde4NNKHA,5270
8
- bfabric_web_apps/utils/components.py,sha256=V7ECGmF2XYy5O9ciDJVH1nofJYP2a_ELQF3z3X_ADbo,844
9
- bfabric_web_apps/utils/get_logger.py,sha256=_cn0en-itaGEeeCDtws-nw2ubd36w2xc6VfDNoBMFu4,433
10
- bfabric_web_apps/utils/get_power_user_wrapper.py,sha256=ZUrv-xNLEXtMVeFEpAwmPbPFUmcAxu3hITIuPY2dhF8,1078
11
- bfabric_web_apps-0.1.1.dist-info/LICENSE,sha256=k0O_i2k13i9e35aO-j7FerJafAqzzu8x0kkBs0OWF3c,1065
12
- bfabric_web_apps-0.1.1.dist-info/METADATA,sha256=R1zgL1bVK18CV4uPQc1vUABgBnOn6us0YBbcC6yqy0g,480
13
- bfabric_web_apps-0.1.1.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
14
- bfabric_web_apps-0.1.1.dist-info/RECORD,,