From 34c9d4b35f6dacc0a1527c9117cefbda93c6ff93 Mon Sep 17 00:00:00 2001 From: Jonathan Karr Date: Fri, 11 Feb 2022 11:42:30 -0500 Subject: [PATCH 1/2] feat: added submission of simulators to both dev and prod APIs --- biosimulators_test_suite/_version.py | 2 +- biosimulators_test_suite/config.py | 20 ++++++++---- biosimulators_test_suite/exec_gh_action.py | 19 ++++++++---- requirements.txt | 2 +- tests/test_config.py | 6 ++-- tests/test_exec_gh_action.py | 36 ++++++++++++++-------- 6 files changed, 57 insertions(+), 28 deletions(-) diff --git a/biosimulators_test_suite/_version.py b/biosimulators_test_suite/_version.py index 28341fc..916b829 100644 --- a/biosimulators_test_suite/_version.py +++ b/biosimulators_test_suite/_version.py @@ -1 +1 @@ -__version__ = '0.1.83' +__version__ = '0.1.84' diff --git a/biosimulators_test_suite/config.py b/biosimulators_test_suite/config.py index 67235f2..0d51e08 100644 --- a/biosimulators_test_suite/config.py +++ b/biosimulators_test_suite/config.py @@ -20,7 +20,8 @@ class Config(object): biosimulators_audience (:obj:`str`): Open API audience for the BioSimulators API biosimulators_api_client_id (:obj:`str`): Client id of the BioSimulators API biosimulators_api_client_secret (:obj:`str`): Client secret of the BioSimulators API - biosimulators_api_endpoint (:obj:`str`): Base URL for the BioSimulators API + biosimulators_prod_api_endpoint (:obj:`str`): Base URL for the BioSimulators API + biosimulators_dev_api_endpoint (:obj:`str`): Base URL for the BioSimulators API biosimulators_curator_gh_ids (:obj:`list` of :obj:`str`): GitHub user ids of the BioSimulators curators biosimulators_default_specifications_version (:obj:`str`): Default version of the BioSimulators simulation specifications biosimulators_default_image_version (:obj:`str`): Default version of the BioSimulators Docker image format @@ -47,7 +48,8 @@ def __init__(self, pull_docker_image=None, docker_hub_username=None, docker_hub_token=None, biosimulators_auth_endpoint=None, biosimulators_audience=None, biosimulators_api_client_id=None, biosimulators_api_client_secret=None, - biosimulators_api_endpoint=None, + biosimulators_prod_api_endpoint=None, + biosimulators_dev_api_endpoint=None, biosimulators_curator_gh_ids=None, biosimulators_default_specifications_version=None, biosimulators_default_image_version=None, biosimulators_docker_registry_url=None, biosimulators_docker_registry_username=None, biosimulators_docker_registry_token=None, @@ -67,7 +69,8 @@ def __init__(self, biosimulators_audience (:obj:`str`, optional): Open API audience for the BioSimulators API biosimulators_api_client_id (:obj:`str`, optional): Client id of the BioSimulators API biosimulators_api_client_secret (:obj:`str`, optional): Client secret of the BioSimulators API - biosimulators_api_endpoint (:obj:`str`, optional): Base URL for the BioSimulators API + biosimulators_prod_api_endpoint (:obj:`str`, optional): Base URL for the BioSimulators API + biosimulators_dev_api_endpoint (:obj:`str`, optional): Base URL for the BioSimulators API biosimulators_curator_gh_ids (:obj:`list` of :obj:`str`, optional): GitHub user ids of the BioSimulators curators biosimulators_default_specifications_version (:obj:`str`, optional): Default version of the BioSimulators simulation specifications biosimulators_default_image_version (:obj:`str`, optional): Default version of the BioSimulators Docker image format @@ -127,10 +130,15 @@ def __init__(self, else: self.biosimulators_api_client_secret = biosimulators_api_client_secret - if biosimulators_api_endpoint is None: - self.biosimulators_api_endpoint = os.getenv('BIOSIMULATORS_API_ENDPOINT', 'https://api.biosimulators.org/') + if biosimulators_prod_api_endpoint is None: + self.biosimulators_prod_api_endpoint = os.getenv('BIOSIMULATORS_PROD_API_ENDPOINT', 'https://api.biosimulators.org/') else: - self.biosimulators_api_endpoint = biosimulators_api_endpoint + self.biosimulators_prod_api_endpoint = biosimulators_prod_api_endpoint + + if biosimulators_dev_api_endpoint is None: + self.biosimulators_dev_api_endpoint = os.getenv('BIOSIMULATORS_DEV_API_ENDPOINT', 'https://api.biosimulators.dev/') + else: + self.biosimulators_dev_api_endpoint = biosimulators_dev_api_endpoint if biosimulators_curator_gh_ids is None: ids = os.getenv('BIOSIMULATORS_CURATOR_GH_IDS', 'jonrkarr').strip() diff --git a/biosimulators_test_suite/exec_gh_action.py b/biosimulators_test_suite/exec_gh_action.py index a8f6130..a2deb6d 100644 --- a/biosimulators_test_suite/exec_gh_action.py +++ b/biosimulators_test_suite/exec_gh_action.py @@ -14,7 +14,7 @@ from .results.io import write_test_results from .utils import get_singularity_image_filename from biosimulators_utils.biosimulations.utils import validate_biosimulations_api_response -from biosimulators_utils.config import Colors +from biosimulators_utils.config import Colors, Config as BioSimulatorsUtilsConfig from biosimulators_utils.gh_action.data_model import Comment, GitHubActionCaughtError # noqa: F401 from biosimulators_utils.gh_action.core import GitHubAction, GitHubActionErrorHandling from biosimulators_utils.image import get_docker_image @@ -96,7 +96,8 @@ def run(self): self.add_labels_to_issue(self.issue_number, [IssueLabel.validated.value]) # get specifications of other versions of simulator - existing_version_specifications = get_simulator_version_specs(specifications['id']) + config = BioSimulatorsUtilsConfig() + existing_version_specifications = get_simulator_version_specs(specifications['id'], config) # determine if simulator is approved: issue is a revision of an existing version of a validated simulator, # a new version of a validated simulator, or the issue has been manually approved by the BioSimulators Team @@ -445,7 +446,12 @@ def commit_simulator(self, submission, specifications, existing_version_specific specifications['biosimulators']['validated'] = False specifications['biosimulators']['validationTests'] = None - self.post_entry_to_biosimulators_api(specifications, existing_version_specifications) + config = BioSimulatorsUtilsConfig(BIOSIMULATORS_API_ENDPOINT=self.config.biosimulators_prod_api_endpoint) + self.post_entry_to_biosimulators_api(specifications, existing_version_specifications, self.config.biosimulators_prod_api_endpoint) + + config = BioSimulatorsUtilsConfig(BIOSIMULATORS_API_ENDPOINT=self.config.biosimulators_dev_api_endpoint) + existing_version_specifications = get_simulator_version_specs(specifications['id'], config) + self.post_entry_to_biosimulators_api(specifications, existing_version_specifications, self.config.biosimulators_dev_api_endpoint) # commit image if submission.validate_image: @@ -528,12 +534,13 @@ def trigger_conversion_of_docker_image_to_singularity(self, specifications): }) validate_biosimulations_api_response(response, 'A Singularity image could not be generated for the Docker image') - def post_entry_to_biosimulators_api(self, specifications, existing_version_specifications): + def post_entry_to_biosimulators_api(self, specifications, existing_version_specifications, biosimulators_api_endpoint): """ Post the simulation to the BioSimulators database Args: specifications (:obj:`dict`): specifications of a simulation tool existing_version_specifications (:obj:`list` of :obj:`dict`): specifications of other versions of simulation tool + biosimulators_api_endpoint (:obj:`str`): endpoint for the BioSimulators API (e.g., ``https://api.biosimulators.org``) """ auth_headers = self.get_auth_headers_for_biosimulations_api( self.config.biosimulators_auth_endpoint, self.config.biosimulators_audience, @@ -542,11 +549,11 @@ def post_entry_to_biosimulators_api(self, specifications, existing_version_speci existing_versions = [existing_version_spec['version'] for existing_version_spec in existing_version_specifications] update_simulator = specifications['version'] in existing_versions if update_simulator: - endpoint = '{}simulators/{}/{}'.format(self.config.biosimulators_api_endpoint, specifications['id'], specifications['version']) + endpoint = '{}simulators/{}/{}'.format(biosimulators_api_endpoint, specifications['id'], specifications['version']) requests_method = requests.put method = 'updated' else: - endpoint = '{}simulators'.format(self.config.biosimulators_api_endpoint) + endpoint = '{}simulators'.format(biosimulators_api_endpoint) requests_method = requests.post method = 'added' response = requests_method(endpoint, headers=auth_headers, json=specifications) diff --git a/requirements.txt b/requirements.txt index 246c905..fb0b67d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -biosimulators_utils[containers,bngl,cellml,lems,neuroml,sbml,smoldyn] >= 0.1.144 +biosimulators_utils[containers,bngl,cellml,lems,neuroml,sbml,smoldyn] >= 0.1.160 cement docker natsort diff --git a/tests/test_config.py b/tests/test_config.py index 4b51b15..784b542 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -43,7 +43,8 @@ def test_arguments(self): pull_docker_image=True, docker_hub_username='user', docker_hub_token='token', biosimulators_auth_endpoint='https://auth.biosimulators.org', biosimulators_audience='biosimulators_audience', biosimulators_api_client_id='biosimulators_client_id', biosimulators_api_client_secret='biosimulators_client_secret', - biosimulators_api_endpoint='https://api.biosimulators.org', + biosimulators_prod_api_endpoint='https://api.biosimulators.org', + biosimulators_dev_api_endpoint='https://api.biosimulators.dev', biosimulators_curator_gh_ids=['user'], biosimulators_default_specifications_version='1.0.0', biosimulators_default_image_version='1.0.0', biosimulators_docker_registry_url='ghcr.io', biosimulators_docker_registry_username='user@ghcr.io', biosimulators_docker_registry_token='token@ghcr.io', @@ -60,7 +61,8 @@ def test_arguments(self): self.assertEqual(config.biosimulators_audience, 'biosimulators_audience') self.assertEqual(config.biosimulators_api_client_id, 'biosimulators_client_id') self.assertEqual(config.biosimulators_api_client_secret, 'biosimulators_client_secret') - self.assertEqual(config.biosimulators_api_endpoint, 'https://api.biosimulators.org') + self.assertEqual(config.biosimulators_prod_api_endpoint, 'https://api.biosimulators.org') + self.assertEqual(config.biosimulators_dev_api_endpoint, 'https://api.biosimulators.dev') self.assertEqual(config.biosimulators_curator_gh_ids, ['user']) self.assertEqual(config.biosimulators_default_specifications_version, '1.0.0') self.assertEqual(config.biosimulators_default_image_version, '1.0.0') diff --git a/tests/test_exec_gh_action.py b/tests/test_exec_gh_action.py index fba29ad..beab037 100644 --- a/tests/test_exec_gh_action.py +++ b/tests/test_exec_gh_action.py @@ -318,14 +318,14 @@ def post(self, url, json=None, headers=None): self.parent.assertIn('grant_type', json) return mock.Mock(raise_for_status=lambda: None, json=lambda: {'token_type': 'Bearer', 'access_token': '******'}) else: - self.parent.assertEqual(url, Config().biosimulators_api_endpoint + 'simulators') + self.parent.assertEqual(url, Config().biosimulators_prod_api_endpoint + 'simulators') self.parent.assertEqual(headers, {'Authorization': 'Bearer ******'}) self.parent.assertEqual(json, {'id': 'tellurium', 'version': '2.1.6'}) return mock.Mock(raise_for_status=lambda: None) def put(self, url, json=None, headers=None): self.n_put = self.n_put + 1 - self.parent.assertEqual(url, Config().biosimulators_api_endpoint + 'simulators/tellurium/2.1.6') + self.parent.assertEqual(url, Config().biosimulators_prod_api_endpoint + 'simulators/tellurium/2.1.6') self.parent.assertEqual(json, {'id': 'tellurium', 'version': '2.1.6'}) return mock.Mock(raise_for_status=lambda: None) @@ -335,14 +335,14 @@ def put(self, url, json=None, headers=None): existing_version_specs = [{'id': 'tellurium', 'version': '2.1.6'}] with mock.patch('requests.post', side_effect=requests_mock.post): with mock.patch('requests.put', side_effect=requests_mock.put): - action.post_entry_to_biosimulators_api(specs, existing_version_specs) + action.post_entry_to_biosimulators_api(specs, existing_version_specs, action.config.biosimulators_prod_api_endpoint) self.assertEqual(requests_mock.n_post, 1) self.assertEqual(requests_mock.n_put, 1) existing_version_specs = [{'id': 'tellurium', 'version': '2.1.5'}] with mock.patch('requests.post', side_effect=requests_mock.post): with mock.patch('requests.put', side_effect=requests_mock.put): - action.post_entry_to_biosimulators_api(specs, existing_version_specs) + action.post_entry_to_biosimulators_api(specs, existing_version_specs, action.config.biosimulators_prod_api_endpoint) self.assertEqual(requests_mock.n_post, 3) self.assertEqual(requests_mock.n_put, 1) @@ -368,22 +368,22 @@ def put(self, url, json=None, headers=None, auth=None): existing_version_specs = [{'id': 'tellurium', 'version': '2.1.5'}] with mock.patch('requests.post', side_effect=requests_mock.post): action.commit_simulator(SimulatorSubmission(validate_image=False), specs, existing_version_specs, []) - self.assertEqual(requests_mock.n_post, 2) + self.assertEqual(requests_mock.n_post, 2 + 2) existing_version_specs = [{'id': 'tellurium', 'version': '2.1.5'}] with mock.patch.dict(os.environ, self.env): with mock.patch('requests.post', side_effect=requests_mock.post): with mock.patch.object(exec_gh_action.ValidateCommitSimulatorGitHubAction, 'push_image', return_value=None): action.commit_simulator(SimulatorSubmission(validate_image=True), specs, existing_version_specs, []) - self.assertEqual(requests_mock.n_post, 6) + self.assertEqual(requests_mock.n_post, 6 + 4) existing_version_specs = [{'id': 'tellurium', 'version': '2.1.6'}] with mock.patch.dict(os.environ, self.env): with mock.patch('requests.post', side_effect=requests_mock.post): with mock.patch('requests.put', side_effect=requests_mock.put): action.commit_simulator(SimulatorSubmission(validate_image=False), specs, existing_version_specs, []) - self.assertEqual(requests_mock.n_post, 7) - self.assertEqual(requests_mock.n_put, 1) + self.assertEqual(requests_mock.n_post, 7 + 6) + self.assertEqual(requests_mock.n_put, 1 + 0) def test_run(self): # validate specs of valid new simulator, fail on invalid specs @@ -716,7 +716,10 @@ def get(self, url, json=None, auth=None, headers=None): } elif url == 'https://api.github.com/repos/biosimulators/Biosimulators/issues/11/labels': response = [{'name': label} for label in self.issue_labels] - elif url == Config().biosimulators_api_endpoint + 'simulators/tellurium': + elif url in [ + Config().biosimulators_prod_api_endpoint + 'simulators/tellurium', + Config().biosimulators_dev_api_endpoint + 'simulators/tellurium', + ]: response = self.simulator_versions else: raise ValueError('Invalid url: {}'.format(url)) @@ -726,11 +729,17 @@ def post(self, url, json=None, auth=None, headers=None): if url == Config().biosimulators_auth_endpoint: error = None response = {'token_type': 'Bearer', 'access_token': '******'} - elif url == Config().biosimulators_api_endpoint + 'simulators': + elif url in [ + Config().biosimulators_prod_api_endpoint + 'simulators', + Config().biosimulators_dev_api_endpoint + 'simulators', + ]: self.simulator_versions.append(json) error = None response = None - elif url == Config().biosimulators_api_endpoint + 'simulators/validate': + elif url in [ + Config().biosimulators_prod_api_endpoint + 'simulators/validate', + Config().biosimulators_dev_api_endpoint + 'simulators/validate', + ]: if self.specs_valid: error = None response = None @@ -775,7 +784,10 @@ def patch(self, url, json=None, auth=None, headers=None): return mock.Mock(raise_for_status=lambda: None, json=lambda: response) def put(self, url, json=None, auth=None, headers=None): - if url.startswith(Config().biosimulators_api_endpoint + 'simulators/tellurium/'): + if ( + url.startswith(Config().biosimulators_prod_api_endpoint + 'simulators/tellurium/') or + url.startswith(Config().biosimulators_dev_api_endpoint + 'simulators/tellurium/') + ): for i_version, version in enumerate(self.simulator_versions): if version['version'] == json['version']: self.simulator_versions[i_version] = json From b12c389ff33de0a49074384a25fd1b013c05ad08 Mon Sep 17 00:00:00 2001 From: Jonathan Karr Date: Fri, 11 Feb 2022 11:56:25 -0500 Subject: [PATCH 2/2] feat: improve alignment for output directories (i.e. not pre-create them) between test conditions and runBioSimulations --- biosimulators_test_suite/test_case/published_project.py | 3 --- tests/test_case/test_published_project.py | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/biosimulators_test_suite/test_case/published_project.py b/biosimulators_test_suite/test_case/published_project.py index 5a44712..35718b6 100644 --- a/biosimulators_test_suite/test_case/published_project.py +++ b/biosimulators_test_suite/test_case/published_project.py @@ -320,9 +320,6 @@ def eval(self, specifications, working_dirname, synthetic_archives_dir=None, dry if dry_run: return - if not os.path.isdir(working_dirname): - os.makedirs(working_dirname) - # pull image and execute COMBINE/OMEX archive for case try: self.exec_sedml_docs_in_archive(specifications, working_dirname, cli=cli) diff --git a/tests/test_case/test_published_project.py b/tests/test_case/test_published_project.py index 19687e0..f13caf1 100644 --- a/tests/test_case/test_published_project.py +++ b/tests/test_case/test_published_project.py @@ -218,6 +218,9 @@ def exec_archive(error, missing_report, extra_report, missing_data_set, extra_da data[0][0] = -1 data[1][0] = -1 + if not os.path.isdir(out_dir): + os.makedirs(out_dir) + if extra_report: report = Report(id='report', data_sets=[DataSet(id=i, label=l) for i, l in zip(ids, labels)]) data_set_results = DataSetResults({i: d for i, d in zip(ids, data)}) @@ -229,6 +232,7 @@ def exec_archive(error, missing_report, extra_report, missing_data_set, extra_da ReportWriter().run(report, data_set_results, out_dir, 'BIOMD0000000912_sim.sedml/report', ReportFormat.h5) plot_file = os.path.join(out_dir, 'plot.pdf') + with open(plot_file, 'w'): pass