bfabric-web-apps 0.1.6__py3-none-any.whl → 0.1.7__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.
@@ -25,7 +25,7 @@ from .utils.callbacks import (
25
25
 
26
26
  from .utils.config import settings as config
27
27
 
28
- from. utils.run_main_pipeline import run_main_job
28
+ from. utils.run_main_pipeline import run_main_job, read_file_as_bytes
29
29
 
30
30
  from .utils.resource_utilities import (
31
31
  create_workunit,
@@ -34,6 +34,7 @@ from .utils.resource_utilities import (
34
34
  create_resources
35
35
  )
36
36
 
37
+ from .utils.charging import create_charge
37
38
  from .utils.redis_worker_init import run_worker, test_job
38
39
  from .utils.redis_queue import q
39
40
 
@@ -54,4 +55,6 @@ GSTORE_REMOTE_PATH = config.GSTORE_REMOTE_PATH
54
55
  SCRATCH_PATH = config.SCRATCH_PATH
55
56
  TRX_LOGIN = config.TRX_LOGIN
56
57
  TRX_SSH_KEY = config.TRX_SSH_KEY
57
- URL = config.URL
58
+ URL = config.URL
59
+
60
+ SERVICE_ID = config.SERVICE_ID
@@ -67,7 +67,7 @@ class BfabricInterface( Bfabric ):
67
67
 
68
68
  validation_url = VALIDATION_URL + token
69
69
  res = requests.get(validation_url, headers={"Host": HOST})
70
-
70
+
71
71
  if res.status_code != 200:
72
72
  res = requests.get(validation_url)
73
73
 
@@ -335,4 +335,4 @@ def get_redis_queue_layout():
335
335
 
336
336
  container_children = dbc.Row(queue_cards)
337
337
 
338
- return dbc.Container(container_children, className="mt-4")
338
+ return dbc.Container(container_children, className="mt-4")
@@ -0,0 +1,40 @@
1
+
2
+ from bfabric_web_apps.utils.get_logger import get_logger
3
+ from bfabric_web_apps.utils.get_power_user_wrapper import get_power_user_wrapper
4
+
5
+ def create_charge(token_data, container_id, service_id):
6
+ """
7
+ Create a charge in B-Fabric.
8
+
9
+ Args:
10
+ token_data (dict): Authentication token data.
11
+ container_id (int): Container ID (Order ID).
12
+ service_id (int): Service ID.
13
+
14
+ Returns:
15
+ list[dict]: List of charge data.
16
+ """
17
+
18
+ # Get a logger and an api wrapper
19
+ L = get_logger(token_data)
20
+ wrapper = get_power_user_wrapper(token_data)
21
+
22
+ # Get the user ID from the token data to assign a charger
23
+ usr_id = wrapper.read("user", {"login": token_data.get("user_data")})[0]['id']
24
+
25
+ charge_data = {
26
+ "serviceid": service_id,
27
+ "containerid": container_id,
28
+ "chargerid": usr_id
29
+ }
30
+
31
+ # Create and log the charge
32
+ charge = L.logthis(
33
+ api_call=wrapper.save,
34
+ endpoint="charge",
35
+ obj=charge_data,
36
+ params=None,
37
+ flush_logs=True
38
+ )
39
+
40
+ return charge
@@ -1,4 +1,5 @@
1
1
  from dash import html
2
+ import dash_daq as daq
2
3
 
3
4
  DEVELOPER_EMAIL = "gwhite@fgcz.ethz.ch"
4
5
 
@@ -19,4 +20,9 @@ auth = [html.Div(id="auth-div")]
19
20
  no_auth = [
20
21
  html.P("You are not currently logged into an active session. Please log into bfabric to continue:"),
21
22
  html.A('Login to Bfabric', href='https://fgcz-bfabric.uzh.ch/bfabric/')
22
- ]
23
+ ]
24
+
25
+ charge_switch = [
26
+ daq.BooleanSwitch(id='charge_run', on=True, label="Charge project for run"),
27
+ html.Br()
28
+ ]
@@ -24,6 +24,9 @@ class Settings(BaseSettings):
24
24
  TRX_SSH_KEY: str = "/home/user/.ssh/your_ssh_key"
25
25
  URL: str = "https:/fgcz/dummy/url"
26
26
 
27
+ # Which service id to use for the charge
28
+ SERVICE_ID: int = 0
29
+
27
30
  class Config:
28
31
 
29
32
  env_file = ".env"
@@ -16,6 +16,8 @@ from .resource_utilities import (
16
16
  create_resources
17
17
  )
18
18
 
19
+ from .charging import create_charge
20
+
19
21
  from .config import settings as config
20
22
  from datetime import datetime as dt
21
23
 
@@ -25,11 +27,15 @@ TRX_LOGIN = config.TRX_LOGIN
25
27
  TRX_SSH_KEY = config.TRX_SSH_KEY
26
28
  URL = config.URL
27
29
 
28
- def run_main_job(files_as_byte_strings: dict,
29
- bash_commands: list[str],
30
- resource_paths: dict,
31
- attachment_paths: list[dict],
32
- token: str):
30
+ def run_main_job(
31
+ files_as_byte_strings: dict,
32
+ bash_commands: list[str],
33
+ resource_paths: dict,
34
+ attachment_paths: list[dict],
35
+ token: str,
36
+ service_id: int = 0,
37
+ charge: bool = False,
38
+ ):
33
39
  """
34
40
  Main function to handle:
35
41
  1) Save Files on Server
@@ -37,6 +43,7 @@ def run_main_job(files_as_byte_strings: dict,
37
43
  3) Create workunits in B-Fabric
38
44
  4) Register resources in B-Fabric
39
45
  5) Attach additional gstore files (logs/reports/etc.) to entities in B-Fabric
46
+ 6) Automatically charge the relevant container for the service
40
47
 
41
48
  :param files_as_byte_strings: {destination_path: file as byte strings}
42
49
  :param bash_commands: List of bash commands to execute
@@ -44,6 +51,8 @@ def run_main_job(files_as_byte_strings: dict,
44
51
  :param attachment_paths: Dictionary mapping source file paths to their corresponding file names ({"path/test.txt": "name.txt"})
45
52
  for attachment to a B-Fabric entity (e.g., logs, final reports, etc.)
46
53
  :param token: Authentication token
54
+ :param service_id: ID of the service to charge
55
+ :param charge: Boolean indicating whether to charge the container for the service
47
56
 
48
57
 
49
58
  Dev Notes:
@@ -75,6 +84,7 @@ Dev Notes:
75
84
  summary = save_files_from_bytes(files_as_byte_strings, L)
76
85
  L.log_operation("Success", f"File copy summary: {summary}", params=None, flush_logs=True)
77
86
  print("Summary:", summary)
87
+
78
88
  except Exception as e:
79
89
  # If something unexpected blows up the entire process
80
90
  L.log_operation("Error", f"Failed to copy files: {e}", params=None, flush_logs=True)
@@ -117,6 +127,27 @@ Dev Notes:
117
127
  print("Error attaching extra files:", e)
118
128
 
119
129
 
130
+ # STEP 6: Charge the container for the service
131
+ if charge:
132
+
133
+ if service_id == 0:
134
+ print("Service ID not provided. Skipping charge creation.")
135
+ L.log_operation("Info", "Service ID not provided. Skipping charge creation.", params=None, flush_logs=True)
136
+ else:
137
+ container_ids = list(set(list(resource_paths.values())))
138
+ if not container_ids:
139
+ L.log_operation("Error", "No container IDs found for charging.", params=None, flush_logs=True)
140
+ print("Error: No container IDs found for charging.")
141
+ return
142
+ for container_id in container_ids:
143
+ charges = create_charge(token_data, container_id, service_id)
144
+ charge_id = charges[0].get("id")
145
+ L.log_operation("Success", f"Charge created for container {container_id} with service ID {service_id} and charge id {charge_id}", params=None, flush_logs=False)
146
+ print(f"Charge created with id {charge_id} for container {container_id} with service ID {service_id}")
147
+ L.flush_logs()
148
+ else:
149
+ L.log_operation("Info", "Charge creation skipped.", params=None, flush_logs=True)
150
+ print("Charge creation skipped.")
120
151
 
121
152
  #---------------------------------------------------------------------------------------------------------------------
122
153
  #---------------------------------------------------------------------------------------------------------------------
@@ -138,23 +169,23 @@ def save_files_from_bytes(files_as_byte_strings: dict, logger):
138
169
  """
139
170
  results = {} # Store results: (destination) -> True (if success) or error message (if failure)
140
171
 
172
+ message = "All files saved successfully."
173
+
141
174
  # First pass: attempt to write all files
142
175
  for destination, file_bytes in files_as_byte_strings.items():
143
176
  try:
144
- # Ensure the directory exists
145
- os.makedirs(os.path.dirname(destination), exist_ok=True)
146
-
147
177
  # Write file from byte string
148
- with open(destination, "wb") as f:
178
+ with open(destination, "+wb") as f:
149
179
  f.write(file_bytes)
150
- logger.log_operation("Files saved", "All files saved successfully.", params=None, flush_logs=True)
151
- return "All files saved successfully."
180
+ logger.log_operation(f"File saved", f"File {destination} saved successfully.", params=None, flush_logs=True)
152
181
 
153
182
  except Exception as e:
154
183
  error_msg = f"Error saving file: {destination}, Error: {str(e)}"
155
184
  logger.log_operation("Error", error_msg, params=None, flush_logs=True)
156
185
  print(error_msg)
157
- raise RuntimeError(error_msg)
186
+ message = f"Error saving some files."
187
+
188
+ return message
158
189
 
159
190
 
160
191
  # -----------------------------------------------------------------------------
@@ -412,3 +443,14 @@ def create_api_link(token_data, logger, entity_class, entity_id, file_name, fold
412
443
  logger.log_operation("Error", error_msg, params=None, flush_logs=True)
413
444
  print(error_msg)
414
445
 
446
+
447
+ def read_file_as_bytes(file_path, max_size_mb=400):
448
+ """Reads any file type and stores it as a byte string in a dictionary."""
449
+ file_size_mb = os.path.getsize(file_path) / (1024 * 1024) # Convert bytes to MB
450
+ if file_size_mb > max_size_mb:
451
+ raise ValueError(f"File {file_path} exceeds {max_size_mb}MB limit ({file_size_mb:.2f}MB).")
452
+
453
+ with open(file_path, "rb") as f: # Read as bytes
454
+ file_as_bytes = f.read()
455
+
456
+ return file_as_bytes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bfabric-web-apps
3
- Version: 0.1.6
3
+ Version: 0.1.7
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.10,<4.0
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.11
10
10
  Requires-Dist: bfabric (>=1.13.23,<2.0.0)
11
11
  Requires-Dist: dash (>=3.0.2,<4.0.0)
12
12
  Requires-Dist: dash-bootstrap-components (>=2.0.0,<3.0.0)
13
+ Requires-Dist: dash-daq (>=0.6.0,<0.7.0)
13
14
  Requires-Dist: pydantic-settings (>=2.8.1,<3.0.0)
14
15
  Requires-Dist: pydantic[email] (>=2.10.6,<3.0.0)
15
16
  Requires-Dist: rq (==1.15.1)
@@ -1,11 +1,12 @@
1
- bfabric_web_apps/__init__.py,sha256=D2jTCEYedVnp6yQxNGLRsjihupDTQxNkPVFQAUYXQys,1409
1
+ bfabric_web_apps/__init__.py,sha256=9OSketawJ_bkCbCN0Q3KaM6_u1_zyE74oC7jtrznFP8,1503
2
2
  bfabric_web_apps/layouts/layouts.py,sha256=z8gL4n4wwLdpLGomO9CftBLnGpc3r6OpmUc2-wBg8uo,14661
3
- bfabric_web_apps/objects/BfabricInterface.py,sha256=wmcL9JuSC0QEopgImvkZxmtCIS7izt6bwb6y_ch0zus,10178
3
+ bfabric_web_apps/objects/BfabricInterface.py,sha256=2BNskMzV5K1a-tXFHQubcBk7Rt-8g9du7mNAJrGohMY,10170
4
4
  bfabric_web_apps/objects/Logger.py,sha256=62LC94xhm7YG5LUw3yH46NqvJQsAX7wnc9D4zbY16rA,5224
5
5
  bfabric_web_apps/utils/app_init.py,sha256=RCdpCXp19cF74bouYJLPe-KSETZ0Vwqtd02Ta2VXEF8,428
6
- bfabric_web_apps/utils/callbacks.py,sha256=m5d6IPiYX77-kJN8I2OptZN-GPxZgrI76o1DGFxjpPU,12686
7
- bfabric_web_apps/utils/components.py,sha256=V7ECGmF2XYy5O9ciDJVH1nofJYP2a_ELQF3z3X_ADbo,844
8
- bfabric_web_apps/utils/config.py,sha256=i93fe49Ak4Z7cm_G80m2cBCPp-5qCYLAJEtEr-mYSwQ,1044
6
+ bfabric_web_apps/utils/callbacks.py,sha256=tB1xtHl_ePY6KJWNz3erkrZw3HFhRneewGqZm9xIYtI,12687
7
+ bfabric_web_apps/utils/charging.py,sha256=oNNazH59SFkbxJKPvCel0IxdsRHC8xpJ0AXCLvI88FI,1069
8
+ bfabric_web_apps/utils/components.py,sha256=X3NRnv--LsHWMtWL83Pzr2whOZLSEJIwXTklQdAQpZE,984
9
+ bfabric_web_apps/utils/config.py,sha256=vJzhmc6ooFb46MM1Eg3m8gNrM4fJa-l5Tao2Py-SF_I,1115
9
10
  bfabric_web_apps/utils/create_app_in_bfabric.py,sha256=eVk3cQDXxW-yo9b9n_zzGO6kLg_SLxYbIDECyvEPJXU,2752
10
11
  bfabric_web_apps/utils/get_logger.py,sha256=0Y3SrXW93--eglS0_ZOc34NOriAt6buFPik5n0ltzRA,434
11
12
  bfabric_web_apps/utils/get_power_user_wrapper.py,sha256=T33z64XjmJ0KSlmfEmrEP8eYpbpINCVD6Xld_V7PR2g,1027
@@ -13,8 +14,8 @@ bfabric_web_apps/utils/redis_connection.py,sha256=qXSPxW6m55Ogv44BhmPCl9ACuvzmpf
13
14
  bfabric_web_apps/utils/redis_queue.py,sha256=MCx7z_I2NusJ4P42mcLvV7STtXBFMIIvun83fM8zOGI,168
14
15
  bfabric_web_apps/utils/redis_worker_init.py,sha256=9SUc9bbgBeMbUdqJD9EkWPA4wcJjvyX6Tzanv5JfqEg,691
15
16
  bfabric_web_apps/utils/resource_utilities.py,sha256=4LnV_eQjKkcpZJBsWFx--dmASyE7jfJfktk2hdHn5Fk,5856
16
- bfabric_web_apps/utils/run_main_pipeline.py,sha256=1YSbk3uP_T3tL6mZZXGv7a7FJc8exro_Eb49gnJjdrs,16864
17
- bfabric_web_apps-0.1.6.dist-info/LICENSE,sha256=k0O_i2k13i9e35aO-j7FerJafAqzzu8x0kkBs0OWF3c,1065
18
- bfabric_web_apps-0.1.6.dist-info/METADATA,sha256=bFw2hFwCuonWxGFxyCSj6y_l2rIjKfxbzXCAGK3O0Ek,646
19
- bfabric_web_apps-0.1.6.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
20
- bfabric_web_apps-0.1.6.dist-info/RECORD,,
17
+ bfabric_web_apps/utils/run_main_pipeline.py,sha256=RG-Jb3-O1Ok8L0i1gddWVeAEqJT6REITtcD9XDoC_Dc,18766
18
+ bfabric_web_apps-0.1.7.dist-info/LICENSE,sha256=k0O_i2k13i9e35aO-j7FerJafAqzzu8x0kkBs0OWF3c,1065
19
+ bfabric_web_apps-0.1.7.dist-info/METADATA,sha256=Jf3A0eVEOXvoMzFJCmxpWnoGvKNnHd2ckHg4o9w6nm0,687
20
+ bfabric_web_apps-0.1.7.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
21
+ bfabric_web_apps-0.1.7.dist-info/RECORD,,