Skip to content

Commit c523108

Browse files
mhmdk0hasan7n
andauthored
Refactor web UI with Alpine.js and TailwindCSS, enhance task management (#677)
* change webui design - use alpine.js and tailwindcss instead of bootstrap * return entity ID after aggregator and training experiment registration * remove aggregator association - add set aggregator functionality * allow multiple tasks to be ran and stopped (tasks to be ran in containers) * fix training submission to return the new entity id (to be used in the webui) * fix ui logs - run tasks in interactive mode * fix events error - that already exists (logs) * allow multiple tasks to be run in webui * add support for training in webui routes * add training in webui templates and static * fix UI issues - JS refactoring according to the new flow * change task names in routes according to the new design. * fix task logs upon changing profiles - reinitialization * modify new added features to match the new design * fix code scanning errors * fix cli unit tests - training tests * fix modal not showing up caused by race condition * add attributes for tests * fix e2e tests * fix lint error * remove remaining aggregator-association logic * edit confusing message * use update_training_exp for setting aggregator * use proper serializer There is no difference compared to WriteTrainingExpiermentSerializer, but since this is a read endpoint, let's use the Read one * keep aggregator_association app minimally, add delete migration * fix interactive prints * fix email overflow in user dropdown * enhance login prints * update webui according to new changes * fix data mismatch webui - left during testing * remove login warning about terminal from webui * fix mentioning compatiblity tests when there is no tests * better ui/ux for cc input, fix js logic for cc forms * make sync policy disappear if configure cc is toggled off * fix sync policy button * make it visible that users synced cc policy * fix dataset.is_prepared * updatesubmit_as_prepared to new web design * remove dataset hash check from dataset dashboard for large datasets, this makes loading the page very slow * check hashes before configuring CC * store data in bucket as a last step * refactor benchmark models same html snippet for models+reference model * don't show reference model if it's a CC benchmark * remove unused css * refactor cc configuration to leave long operations to the end * scroll to bottom when showing panel * use interactive mode for policy sync * update cc policy webui: show stages * allow users to delete certs anyway in case we ask them to do so for some a reason like expired which is not automatically detected yet * update medperf client version * add progress bar to large file gcp upload * quick fix for javascript caching in browsers * remove Medperf title * use "You" instead of entity owner ID when user is owner * use proper prism dark mode * update docs --------- Co-authored-by: hasan7n <hasankassim7@hotmail.com>
1 parent 209fe17 commit c523108

File tree

168 files changed

+7762
-7301
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+7762
-7301
lines changed

cli/cli_tests_training.sh

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,6 @@ echo "AGG_UID=$AGG_UID" >> "$LAST_ENV_FILE"
163163

164164
echo "\n"
165165

166-
##########################################################
167-
echo "====================================="
168-
echo "Running aggregator association step"
169-
echo "====================================="
170-
print_eval medperf aggregator associate -a $AGG_UID -t $TRAINING_UID -y
171-
checkFailed "aggregator association step failed"
172-
##########################################################
173-
174-
echo "\n"
175-
176166
##########################################################
177167
echo "====================================="
178168
echo "Activate modelowner profile"
@@ -185,10 +175,10 @@ echo "\n"
185175

186176
##########################################################
187177
echo "====================================="
188-
echo "Approve aggregator association"
178+
echo "Running set aggregator step"
189179
echo "====================================="
190-
print_eval medperf association approve -t $TRAINING_UID -a $AGG_UID
191-
checkFailed "agg association approval failed"
180+
print_eval medperf training set_aggregator -t $TRAINING_UID -a $AGG_UID -y
181+
checkFailed "Setting aggregator failed"
192182
##########################################################
193183

194184
echo "\n"

cli/medperf/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.2.0"
1+
__version__ = "0.3.0"

cli/medperf/asset_management/asset_management.py

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from medperf.asset_management.asset_storage_manager import AssetStorageManager
1010
from medperf.asset_management.asset_policy_manager import AssetPolicyManager
1111
from medperf.asset_management.cc_operator import OperatorManager
12-
from medperf.utils import tar, generate_tmp_path
12+
from medperf.utils import tar, generate_tmp_path, remove_path
1313
import secrets
1414
from medperf.exceptions import MedperfException
1515
from medperf import config as medperf_config
@@ -42,55 +42,52 @@ def setup_dataset_for_cc(dataset: Dataset):
4242
cc_policy = dataset.get_cc_policy()
4343
__verify_cloud_environment(cc_config)
4444

45-
# create dataset asset
45+
# policy setup
46+
medperf_config.ui.text = "Generating encryption key"
47+
encryption_key = generate_encryption_key()
48+
asset_policy_manager = AssetPolicyManager(cc_config)
49+
asset_policy_manager.setup_policy(cc_policy, encryption_key)
50+
51+
# storage
4652
medperf_config.ui.text = "Compressing dataset"
4753
asset_path = generate_tmp_path()
4854
tar(asset_path, [dataset.data_path, dataset.labels_path])
49-
50-
__setup_asset_for_cc(cc_config, cc_policy, asset_path)
55+
asset_storage_manager = AssetStorageManager(cc_config, asset_path, encryption_key)
56+
asset_storage_manager.store_asset()
57+
del encryption_key
58+
remove_path(asset_path)
5159

5260

5361
def setup_model_for_cc(model: Model):
5462
if not model.is_cc_configured():
5563
return
5664
cc_config = model.get_cc_config()
5765
cc_policy = model.get_cc_policy()
58-
if model.type != "ASSET":
66+
if not model.is_asset():
5967
raise MedperfException(
6068
f"Model {model.id} is not a file-based asset and cannot be set up for confidential computing."
6169
)
6270
asset = model.asset_obj
63-
# create model asset
6471
asset_path = asset.get_archive_path()
6572

6673
__verify_cloud_environment(cc_config)
67-
__setup_asset_for_cc(cc_config, cc_policy, asset_path, for_model=True)
68-
69-
70-
def __verify_cloud_environment(cc_config: dict):
71-
AssetStorageManager(cc_config, None, None).setup()
72-
7374

74-
def __setup_asset_for_cc(
75-
cc_config: dict,
76-
cc_policy: dict,
77-
asset_path: str,
78-
for_model: bool = False,
79-
):
80-
# create encryption key
75+
# policy setup
76+
medperf_config.ui.text = "Generating encryption key"
8177
encryption_key = generate_encryption_key()
82-
83-
asset_storage_manager = AssetStorageManager(cc_config, asset_path, encryption_key)
84-
asset_policy_manager = AssetPolicyManager(cc_config, for_model=for_model)
78+
asset_policy_manager = AssetPolicyManager(cc_config, for_model=True)
79+
asset_policy_manager.setup_policy(cc_policy, encryption_key)
8580

8681
# storage
82+
asset_storage_manager = AssetStorageManager(cc_config, asset_path, encryption_key)
8783
asset_storage_manager.store_asset()
88-
89-
# policy setup
90-
asset_policy_manager.setup_policy(cc_policy, encryption_key)
9184
del encryption_key
9285

9386

87+
def __verify_cloud_environment(cc_config: dict):
88+
AssetStorageManager(cc_config, None, None).setup()
89+
90+
9491
def update_dataset_cc_policy(dataset: Dataset, permitted_workloads: list[CCWorkloadID]):
9592
if not dataset.is_cc_configured():
9693
raise MedperfException(
@@ -108,7 +105,7 @@ def update_model_cc_policy(model: Model, permitted_workloads: list[CCWorkloadID]
108105
f"Model {model.id} does not have a configuration for confidential computing."
109106
)
110107
cc_config = model.get_cc_config()
111-
if model.type != "ASSET":
108+
if not model.is_asset():
112109
raise MedperfException(
113110
f"Model {model.id} is not a file-based asset and cannot be set up for confidential computing."
114111
)

cli/medperf/asset_management/asset_storage_manager.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@
66
remove_path,
77
)
88
from medperf.encryption import SymmetricEncryption
9-
from medperf.asset_management.gcp_utils import GCPAssetConfig, upload_file_to_gcs
9+
from medperf.asset_management.gcp_utils import (
10+
GCPAssetConfig,
11+
upload_from_file_object_to_gcs,
12+
)
1013
from medperf.asset_management.asset_check import verify_asset_owner_setup
14+
from medperf.asset_management.utils import CustomWriter, get_file_size
1115
from medperf.exceptions import MedperfException
1216
from medperf import config as medperf_config
17+
from tqdm import tqdm
1318

1419

1520
class AssetStorageManager:
@@ -30,12 +35,25 @@ def __encrypt_asset(self):
3035
asset_hash = get_file_hash(tmp_encrypted_asset_path)
3136
return tmp_encrypted_asset_path, asset_hash
3237

33-
def __upload_encrypted_asset(self, tmp_encrypted_asset_path):
34-
upload_file_to_gcs(
35-
self.config,
36-
tmp_encrypted_asset_path,
37-
self.config.encrypted_asset_bucket_file,
38-
)
38+
def __upload_encrypted_asset(self, tmp_encrypted_asset_path: str):
39+
with open(tmp_encrypted_asset_path, "rb") as in_file:
40+
with tqdm.wrapattr(
41+
in_file,
42+
"read",
43+
total=get_file_size(in_file),
44+
miniters=1,
45+
desc="Uploading encrypted dataset to the bucket",
46+
unit="B",
47+
unit_scale=True,
48+
unit_divisor=1024,
49+
file=CustomWriter(),
50+
) as file_obj:
51+
upload_from_file_object_to_gcs(
52+
self.config,
53+
file_obj,
54+
self.config.encrypted_asset_bucket_file,
55+
)
56+
remove_path(tmp_encrypted_asset_path)
3957

4058
def setup(self):
4159
medperf_config.ui.text = "Verifying Cloud Environment"

cli/medperf/asset_management/gcp_utils/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .kms import set_kms_iam_policy, encrypt_with_kms_key
33
from .storage import (
44
upload_file_to_gcs,
5+
upload_from_file_object_to_gcs,
56
upload_string_to_gcs,
67
download_file_from_gcs,
78
download_string_from_gcs,
@@ -19,6 +20,7 @@
1920
"set_kms_iam_policy",
2021
"encrypt_with_kms_key",
2122
"upload_file_to_gcs",
23+
"upload_from_file_object_to_gcs",
2224
"upload_string_to_gcs",
2325
"download_file_from_gcs",
2426
"download_string_from_gcs",

cli/medperf/asset_management/gcp_utils/storage.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ def upload_file_to_gcs(
1313
blob.upload_from_filename(local_file)
1414

1515

16+
def upload_from_file_object_to_gcs(
17+
config: Union[GCPAssetConfig, GCPOperatorConfig], file: object, gcs_path: str
18+
):
19+
"""Upload file to Google Cloud Storage."""
20+
client = storage.Client()
21+
bucket = client.bucket(config.bucket)
22+
blob = bucket.blob(gcs_path)
23+
blob.upload_from_file(file)
24+
25+
1626
def upload_string_to_gcs(
1727
config: Union[GCPAssetConfig, GCPOperatorConfig], content: bytes, gcs_path: str
1828
):
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from medperf import config
2+
import os
3+
4+
5+
class CustomWriter:
6+
"""class to use with tqdm to print progress using config.ui"""
7+
8+
def write(self, msg):
9+
config.ui.print(msg)
10+
11+
def flush(self):
12+
pass
13+
14+
15+
def get_file_size(file_object) -> int:
16+
"""Get the size of a file in bytes."""
17+
try:
18+
total_bytes = os.fstat(file_object.fileno()).st_size
19+
except (AttributeError, OSError):
20+
total_bytes = None
21+
return total_bytes

cli/medperf/commands/aggregator/aggregator.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import medperf.config as config
66
from medperf.decorators import clean_except
77
from medperf.commands.aggregator.submit import SubmitAggregator
8-
from medperf.commands.aggregator.associate import AssociateAggregator
98
from medperf.commands.aggregator.run import StartAggregator
109

1110
from medperf.commands.list import EntityList
@@ -33,22 +32,6 @@ def submit(
3332
config.ui.print("✅ Done!")
3433

3534

36-
@app.command("associate")
37-
@clean_except
38-
def associate(
39-
aggregator_id: int = typer.Option(
40-
..., "--aggregator_id", "-a", help="UID of benchmark to associate with"
41-
),
42-
training_exp_id: int = typer.Option(
43-
..., "--training_exp_id", "-t", help="UID of benchmark to associate with"
44-
),
45-
approval: bool = typer.Option(False, "-y", help="Skip approval step"),
46-
):
47-
"""Associates an aggregator with a training experiment."""
48-
AssociateAggregator.run(aggregator_id, training_exp_id, approved=approval)
49-
config.ui.print("✅ Done!")
50-
51-
5235
@app.command("start")
5336
@clean_except
5437
def run(

cli/medperf/commands/aggregator/run.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ def run(
2424
training_exp_id (int): Training experiment UID.
2525
"""
2626
execution = cls(training_exp_id, publish_on, overwrite)
27-
execution.prepare()
28-
execution.validate()
29-
execution.check_existing_outputs()
30-
execution.prepare_aggregator()
31-
execution.prepare_participants_list()
32-
execution.prepare_plan()
33-
execution.prepare_pki_assets()
3427
with config.ui.interactive():
28+
execution.prepare()
29+
execution.validate()
30+
execution.check_existing_outputs()
31+
execution.prepare_aggregator()
32+
execution.prepare_participants_list()
33+
execution.prepare_plan()
34+
execution.prepare_pki_assets()
3535
execution.run_experiment()
3636

3737
def __init__(self, training_exp_id, publish_on, overwrite) -> None:

cli/medperf/commands/aggregator/submit.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def run(cls, name: str, address: str, port: int, aggregation_mlcube: int):
2323
updated_benchmark_body = submission.submit()
2424
ui.print("Uploaded")
2525
submission.write(updated_benchmark_body)
26+
return submission.aggregator.id
2627

2728
def __init__(self, name: str, address: str, port: int, aggregation_mlcube: int):
2829
self.ui = config.ui
@@ -41,5 +42,5 @@ def submit(self):
4142

4243
def write(self, updated_body):
4344
remove_path(self.aggregator.path)
44-
aggregator = Aggregator(**updated_body)
45-
aggregator.write()
45+
self.aggregator = Aggregator(**updated_body)
46+
self.aggregator.write()

0 commit comments

Comments
 (0)