-
Notifications
You must be signed in to change notification settings - Fork 36
Web UI #634
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Web UI #634
Changes from all commits
040d446
97e6bd7
0382684
55fe60e
fb1bca3
64cf53e
36611e1
56fa5c4
8563887
07ce4ab
b260401
b7980a8
ca356cc
6efd724
319b1bf
58008f3
375d89e
c6d8a56
74f7743
b312882
0e282cb
6b28ebb
881b281
e28107b
5b718eb
0f95027
444786e
e273577
eea1e77
7b68911
8251c42
b669358
039f496
c225a5e
ad0451f
12ffef2
cedad96
3ac8a74
6170b53
53b557b
2b73c4f
75d6776
c47a751
d837837
c58efd8
f2f25c0
4da2628
8e73e54
a78ef8d
64f26ff
14f87a9
812cd7e
7f86b1b
cfcf9df
08f2ca7
04f8c11
d617a04
1bd0926
f38a6ab
f0769b2
1384b21
64d8b3c
7d6f01a
015354e
96362de
b3c81c1
43d2b77
75f9c5c
f2ddd62
df0e2c2
e082cec
4926f07
4b45f49
35ded73
2ab585d
a7fdd52
d9d2932
397aed4
7780bc0
7d20e31
2a1d55d
48e388e
23908f5
020da3e
e59d877
88c5eb0
e265382
d4e7151
e636cdd
79e25e6
72b2eef
2bdd9f4
638fc5e
37b5106
bb7a5ae
a21dea0
e135f09
4640730
c6381d8
dc9e6fd
32a4665
3211ca6
05b35cc
2dc1dfb
8c104ae
68aa2dc
47eb79d
65c6407
4df5ca8
f63f4fd
3377a3d
1bca4cc
493e969
6144fa0
b8c72f3
2d5187e
7be2bac
18812cf
c074993
4acedd4
72099a2
9795660
ac3d0b3
cdd049f
ed1d25f
73df291
6a26f74
8c8c6c3
db5e4f5
b8ee61b
8a86944
15ea1c4
b6fcda9
4618ab3
227c509
c65a0cf
bc1f3ad
d0394bf
47a8c33
f81a57d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,7 +89,8 @@ def run(cls, dataset_id: int, approve_sending_reports: bool = False): | |
preparation.prompt_for_report_sending_approval() | ||
|
||
if preparation.should_run_prepare(): | ||
preparation.run_prepare() | ||
with preparation.ui.interactive(): | ||
preparation.run_prepare() | ||
|
||
with preparation.ui.interactive(): | ||
preparation.run_sanity_check() | ||
|
@@ -277,6 +278,8 @@ def __generate_report_dict(self): | |
with open(self.report_path, "r") as f: | ||
report_dict = yaml.safe_load(f) | ||
|
||
# TODO: this specific logic with status is very tuned to the RANO. Hope we'd | ||
# make it more general once | ||
report = pd.DataFrame(report_dict) | ||
if "status" in report.keys(): | ||
report_status = report.status.value_counts() / len(report) | ||
|
@@ -288,6 +291,7 @@ def __generate_report_dict(self): | |
|
||
return report_status_dict | ||
|
||
@staticmethod | ||
def prompt_for_report_sending_approval(self): | ||
example = { | ||
"execution_status": "running", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,8 @@ def set_operational(self): | |
self.dataset.state = "OPERATION" | ||
|
||
def update(self): | ||
msg = "This is the information that is going to be transmitted to the medperf server" | ||
config.ui.print_warning(msg) | ||
body = self.todict() | ||
dict_pretty_print(body) | ||
msg = "Do you approve sending the presented data to MedPerf? [Y/n] " | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,11 +43,17 @@ | |
submit_as_prepared, | ||
for_test, | ||
) | ||
preparation.validate() | ||
preparation.validate_prep_cube() | ||
preparation.create_dataset_object() | ||
if submit_as_prepared: | ||
preparation.make_dataset_prepared() | ||
submission_dict = preparation.prepare_dict(submit_as_prepared) | ||
dict_pretty_print(submission_dict) | ||
|
||
msg = "Do you approve the registration of the presented data to MedPerf? [Y/n] " | ||
warning = ( | ||
"Upon submission, your email address will be visible to the Data Preparation" | ||
+ " Owner for traceability and debugging purposes." | ||
) | ||
config.ui.print_warning(warning) | ||
preparation.approved = preparation.approved or approval_prompt(msg) | ||
|
||
updated_dataset_dict = preparation.upload() | ||
preparation.to_permanent_path(updated_dataset_dict) | ||
preparation.write(updated_dataset_dict) | ||
|
@@ -69,8 +75,8 @@ | |
for_test: bool, | ||
): | ||
self.ui = config.ui | ||
self.data_path = str(Path(data_path).resolve()) | ||
Check failureCode scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading This path depends on a user-provided value Error loading related location Loading |
||
self.labels_path = str(Path(labels_path).resolve()) | ||
Check failureCode scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading This path depends on a user-provided value Error loading related location Loading |
||
self.metadata_path = metadata_path | ||
self.name = name | ||
self.description = description | ||
|
@@ -82,9 +88,9 @@ | |
self.for_test = for_test | ||
|
||
def validate(self): | ||
if not os.path.exists(self.data_path): | ||
Check failureCode scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading This path depends on a user-provided value Error loading related location Loading |
||
raise InvalidArgumentError("The provided data path doesn't exist") | ||
if not os.path.exists(self.labels_path): | ||
Check failureCode scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading This path depends on a user-provided value Error loading related location Loading |
||
raise InvalidArgumentError("The provided labels path doesn't exist") | ||
|
||
if not self.submit_as_prepared and self.metadata_path: | ||
|
@@ -137,8 +143,8 @@ | |
self.dataset = dataset | ||
|
||
def make_dataset_prepared(self): | ||
shutil.copytree(self.data_path, self.dataset.data_path) | ||
Check failureCode scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading This path depends on a user-provided value Error loading related location Loading |
||
shutil.copytree(self.labels_path, self.dataset.labels_path) | ||
Check failureCode scanning / CodeQL Uncontrolled data used in path expression High
This path depends on a
user-provided value Error loading related location Loading This path depends on a user-provided value Error loading related location Loading |
||
if self.metadata_path: | ||
shutil.copytree(self.metadata_path, self.dataset.metadata_path) | ||
else: | ||
|
@@ -147,17 +153,16 @@ | |
# have prepared datasets with no the metadata information | ||
os.makedirs(self.dataset.metadata_path, exist_ok=True) | ||
|
||
def upload(self): | ||
submission_dict = self.dataset.todict() | ||
dict_pretty_print(submission_dict) | ||
msg = "Do you approve the registration of the presented data to MedPerf? [Y/n] " | ||
warning = ( | ||
"Upon submission, your email address will be visible to the Data Preparation" | ||
+ " Owner for traceability and debugging purposes." | ||
) | ||
self.ui.print_warning(warning) | ||
self.approved = self.approved or approval_prompt(msg) | ||
def prepare_dict(self, submit_as_prepared: bool): | ||
self.validate() | ||
self.validate_prep_cube() | ||
self.create_dataset_object() | ||
if submit_as_prepared: | ||
self.make_dataset_prepared() | ||
|
||
return self.dataset.todict() | ||
|
||
def upload(self): | ||
if self.approved: | ||
updated_body = self.dataset.upload() | ||
return updated_body | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,8 @@ def run( | |
"""Lists all local datasets | ||
|
||
Args: | ||
entity_class: entity class to instantiate (Dataset, Model, etc.) | ||
fields (list[str]): list of fields to display | ||
unregistered (bool, optional): Display only local unregistered results. Defaults to False. | ||
mine_only (bool, optional): Display all registered current-user results. Defaults to False. | ||
kwargs (dict): Additional parameters for filtering entity lists. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
from medperf import config | ||
from medperf.entities.cube import Cube | ||
from medperf.entities.benchmark import Benchmark | ||
from medperf.exceptions import CleanExit | ||
from medperf.utils import dict_pretty_print, approval_prompt | ||
from medperf.commands.compatibility_test.run import CompatibilityTestExecution | ||
|
||
|
@@ -42,4 +43,4 @@ def run( | |
metadata = {"test_result": results} | ||
comms.associate_benchmark_model(cube_uid, benchmark_uid, metadata) | ||
else: | ||
ui.print("Model association operation cancelled") | ||
raise CleanExit("Model association operation cancelled") |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ def run(cls, submit_info: dict): | |
updated_cube_dict = submission.upload() | ||
submission.to_permanent_path(updated_cube_dict) | ||
submission.write(updated_cube_dict) | ||
return submission.cube.id | ||
|
||
def __init__(self, submit_info: dict): | ||
self.comms = config.comms | ||
|
@@ -49,5 +50,5 @@ def to_permanent_path(self, cube_dict): | |
os.rename(old_cube_loc, new_cube_loc) | ||
|
||
def write(self, updated_cube_dict): | ||
cube = Cube(**updated_cube_dict) | ||
cube.write() | ||
self.cube = Cube(**updated_cube_dict) | ||
self.cube.write() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,7 @@ def activate(profile: str): | |
config_p = read_config() | ||
|
||
if profile not in config_p: | ||
raise InvalidArgumentError("The provided profile does not exists") | ||
raise InvalidArgumentError("The provided profile does not exist") | ||
|
||
config_p.activate(profile) | ||
write_config(config_p) | ||
|
@@ -81,6 +81,8 @@ def view(profile: str = typer.Argument(None)): | |
config_p = read_config() | ||
profile_config = config_p.active_profile | ||
if profile: | ||
if profile not in config_p: | ||
raise InvalidArgumentError("The provided profile does not exist") | ||
profile_config = config_p[profile] | ||
|
||
profile_config.pop(config.credentials_keyword, None) | ||
|
@@ -99,7 +101,7 @@ def delete(profile: str): | |
""" | ||
config_p = read_config() | ||
if profile not in config_p.profiles: | ||
raise InvalidArgumentError("The provided profile does not exists") | ||
raise InvalidArgumentError("The provided profile does not exist") | ||
|
||
if profile in [ | ||
config.default_profile_name, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,7 @@ def run( | |
ignore_failed_experiments=False, | ||
no_cache=False, | ||
show_summary=False, | ||
): | ||
) -> list[Result]: | ||
"""Benchmark execution flow. | ||
|
||
Args: | ||
|
@@ -48,7 +48,8 @@ def run( | |
ignore_model_errors, | ||
ignore_failed_experiments, | ||
) | ||
execution.prepare() | ||
with execution.ui.interactive(): | ||
execution.prepare() | ||
execution.validate() | ||
execution.prepare_models() | ||
if not no_cache: | ||
|
@@ -166,7 +167,7 @@ def __get_cube(self, uid: int, name: str) -> Cube: | |
self.ui.print(f"> Container '{name}' download complete") | ||
return cube | ||
|
||
def run_experiments(self): | ||
def run_experiments(self) -> list[Result]: | ||
for model_uid in self.models_uids: | ||
if model_uid in self.cached_results: | ||
self.experiments.append( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,11 +39,14 @@ def login(self, email): | |
interval = device_code_response["interval"] | ||
|
||
config.ui.print( | ||
"\nPlease go to the following link to complete your login request:\n" | ||
f"\t{verification_uri_complete}\n\n" | ||
"Make sure that you will be presented with the following code:\n" | ||
f"\t{user_code}\n\n" | ||
"\nPlease go to the following link to complete your login request:\n\t" | ||
) | ||
config.ui.print_url(verification_uri_complete) | ||
config.ui.print( | ||
"\n\nMake sure that you will be presented with the following code:\n\t" | ||
) | ||
config.ui.print_code(user_code) | ||
config.ui.print("\n\n") | ||
config.ui.print_warning( | ||
"Keep this terminal open until you complete your login request. " | ||
"The command will exit on its own once you complete the request. " | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,11 @@ | |
class CommsFactory: | ||
@staticmethod | ||
def create_comms(name: str, host: str) -> Comms: | ||
name = name.lower() | ||
if name == "rest": | ||
return REST(host) | ||
else: | ||
msg = "the indicated communication interface doesn't exist" | ||
raise InvalidArgumentError(msg) | ||
if isinstance(name, str): | ||
name = name.lower() | ||
if name == "rest": | ||
return REST(host) | ||
else: | ||
msg = "the indicated communication interface doesn't exist" | ||
raise InvalidArgumentError(msg) | ||
return REST(host) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -220,6 +220,7 @@ | |
logs_backup_count = 100 | ||
cleanup = True | ||
ui = "CLI" | ||
webui = "WEBUI" | ||
|
||
default_profile_name = "default" | ||
testauth_profile_name = "testauth" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from datetime import datetime | ||
from typing import Optional | ||
|
||
from medperf.entities.schemas import ApprovableSchema, MedperfSchema | ||
|
||
|
||
class Association(MedperfSchema, ApprovableSchema): | ||
id: int | ||
metadata: dict | ||
dataset: Optional[int] | ||
model_mlcube: Optional[int] | ||
benchmark: int | ||
initiated_by: int | ||
created_at: Optional[datetime] | ||
modified_at: Optional[datetime] | ||
name: str = "Association" # The server data doesn't have name, while MedperfSchema requires it |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
Copilot Autofix
AI 6 days ago
To fix the issue, we need to validate the
raw_data_path
to ensure it is within a predefined safe root directory. This can be achieved by:raw_data_path
usingos.path.realpath
orPath.resolve
to remove any..
segments or symbolic links.The validation should be added in the
validate_input
method of theImportDataset
class, as this is where the input parameters are initially checked.