From 714bbc9568261c719f2b2e11b83afdd7f65e659b Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Fri, 19 Apr 2024 11:53:59 +0100 Subject: [PATCH 01/10] chore: update docstring [skip ci] --- tests/test_annotations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_annotations.py b/tests/test_annotations.py index a58d8848..12b835a9 100644 --- a/tests/test_annotations.py +++ b/tests/test_annotations.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 """ -Test NSGR related methods +Test annotations related methods -File: tests/utils/test_nsgr.py +File: tests/test_annotations.py Copyright 2024 NeuroML contributors """ From 0f6a7ca1eaffde563af5ccb4de8a004d658b9802 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Fri, 19 Apr 2024 11:54:44 +0100 Subject: [PATCH 02/10] chore: update docstring (again) [skip ci] --- tests/test_annotations.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/test_annotations.py b/tests/test_annotations.py index 12b835a9..deaf4686 100644 --- a/tests/test_annotations.py +++ b/tests/test_annotations.py @@ -19,13 +19,10 @@ class TestAnnotations(BaseTestCase): - """Test utils module""" + """Test annotations module""" def tests_create_annotation(self): - """Test create_annotations - :returns: TODO - - """ + """Test create_annotations""" annotation = create_annotation( "model.nml", "A tests model", From 713d7162ae1d0e11c2bdec63d0640b869efbb4f9 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Fri, 19 Apr 2024 11:57:12 +0100 Subject: [PATCH 03/10] chore(annotations): update test method name [skip ci] --- tests/test_annotations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_annotations.py b/tests/test_annotations.py index deaf4686..e8679e40 100644 --- a/tests/test_annotations.py +++ b/tests/test_annotations.py @@ -21,7 +21,7 @@ class TestAnnotations(BaseTestCase): """Test annotations module""" - def tests_create_annotation(self): + def test_create_annotation(self): """Test create_annotations""" annotation = create_annotation( "model.nml", From 174b701412a5747d9f58ef1aeb212692be1bd99d Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Fri, 19 Apr 2024 14:37:34 +0100 Subject: [PATCH 04/10] feat(biosimulations): add method to get latest versions .. of the few simulators that we support --- pyneuroml/biosimulations.py | 54 ++++++++++++++++++++++++++++++++++++ tests/test_biosimulations.py | 42 ++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 pyneuroml/biosimulations.py create mode 100644 tests/test_biosimulations.py diff --git a/pyneuroml/biosimulations.py b/pyneuroml/biosimulations.py new file mode 100644 index 00000000..8878a36b --- /dev/null +++ b/pyneuroml/biosimulations.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +Functions related to Biosimulations.org + +File: pyneuroml/biosimulations.py + +Copyright 2024 NeuroML contributors +""" + + +import logging +import typing + +import requests + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + +biosimulators_api_url = "https://api.biosimulators.org" +biosimulations_api_url = "https://api.biosimulations.org" + + +def get_simulator_versions( + simulators: typing.Union[str, typing.List[str]] = [ + "neuron", + "netpyne", + "tellurium", + "pyneuroml", + "pyneuroml", + "xpp", + "brian2", + "copasi", + ] +): + """Get simulator list from biosimulators. + + :param simulators: a simulator or list of simulators to get versions for + :type simulators: str or list(str) + :returns: json response from API + :rtype: str + """ + if type(simulators) is str: + simulators = [simulators] + all_siminfo = {} # type: typing.Dict[str, typing.List[str]] + for sim in simulators: + resp = requests.get(f"{biosimulators_api_url}/simulators/{sim}") + siminfo = resp.json() + for s in siminfo: + try: + all_siminfo[s["id"]].append(s["version"]) + except KeyError: + all_siminfo[s["id"]] = [s["version"]] + + return all_siminfo diff --git a/tests/test_biosimulations.py b/tests/test_biosimulations.py new file mode 100644 index 00000000..f830da24 --- /dev/null +++ b/tests/test_biosimulations.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +""" +Test biosimulations related methods + +File: tests/test_biosimulations.py + +Copyright 2024 NeuroML contributors +""" + + +import logging + +from pyneuroml.biosimulations import get_simulator_versions + +from . import BaseTestCase + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + + +class TestBiosimulations(BaseTestCase): + """Test biosimulations module""" + + def test_get_simulator_versions(self): + """Test get_simulators""" + simulators = get_simulator_versions() + + print(simulators) + + for s in [ + "neuron", + "netpyne", + "tellurium", + "pyneuroml", + "pyneuroml", + "xpp", + "brian2", + "copasi", + ]: + self.assertIn(s, simulators.keys()) + versions = simulators[s] + self.assertGreater(len(versions), 0) From b4ef1d63139b900e741f4c949acd27cd1a7070b4 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Fri, 19 Apr 2024 16:32:27 +0100 Subject: [PATCH 05/10] feat(archive): allow adding extra files to archive --- pyneuroml/archive/__init__.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pyneuroml/archive/__init__.py b/pyneuroml/archive/__init__.py index fb46f5d1..cc04007d 100644 --- a/pyneuroml/archive/__init__.py +++ b/pyneuroml/archive/__init__.py @@ -121,6 +121,7 @@ def create_combine_archive( zipfile_name: typing.Optional[str] = None, zipfile_extension=".neux", filelist: typing.List[str] = [], + extra_files: typing.List[str] = [], ): """Create a combine archive that includes all files referred to (included recursively) by the provided rootfile. If a file list is provided, it will @@ -143,7 +144,10 @@ def create_combine_archive( :param zipfile_extension: extension for zip file, starting with ".". :type zipfile_extension: str :param filelist: explicit list of files to create archive of + if given, the function will not attempt to list model files itself :type filelist: list of strings + :param extra_files: extra files to include in archive + :type extra_files: list of strings :returns: None :raises ValueError: if a root file is not provided """ @@ -169,7 +173,7 @@ def create_combine_archive( if len(filelist) == 0: lems_def_dir = get_model_file_list(rootfile, filelist, rootdir, lems_def_dir) - create_combine_archive_manifest(rootfile, filelist, rootdir) + create_combine_archive_manifest(rootfile, filelist + extra_files, rootdir) filelist.append("manifest.xml") # change to directory of rootfile @@ -177,7 +181,7 @@ def create_combine_archive( os.chdir(rootdir) with ZipFile(zipfile_name + zipfile_extension, "w") as archive: - for f in filelist: + for f in filelist + extra_files: archive.write(f) os.chdir(thispath) @@ -216,6 +220,8 @@ def create_combine_archive_manifest( ) for f in filelist: + format_string = None + logger.info(f"Processing file: {f}") if f.endswith(".xml") and f.startswith("LEMS"): # TODO: check what the string for LEMS should be format_string = "http://identifiers.org/combine.specifications/neuroml" @@ -223,14 +229,15 @@ def create_combine_archive_manifest( format_string = "http://identifiers.org/combine.specifications/neuroml" elif f.endswith(".sedml"): format_string = "http://identifiers.org/combine.specifications/sed-ml" - - if f == rootfile: - master_string = 'master="true"' - else: - master_string = "" + elif f.endswith(".rdf"): + format_string = ( + "http://identifiers.org/combine.specifications/omex-metadata" + ) + elif f.endswith(".pdf"): + format_string = "http://purl.org/NET/mediatypes/application/pdf" print( - f"""\t""", + f"""\t""", file=mf, ) From 6e7a8015e53d21bac1a4d4618d7a052ba400d0fc Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Fri, 19 Apr 2024 18:06:02 +0100 Subject: [PATCH 06/10] feat(biosimulations): implement sending via API WIP: sending to API needs more testing. --- pyneuroml/biosimulations.py | 148 ++++++++++++++++++++++++++++++++++- tests/test_biosimulations.py | 41 +++++++++- 2 files changed, 187 insertions(+), 2 deletions(-) diff --git a/pyneuroml/biosimulations.py b/pyneuroml/biosimulations.py index 8878a36b..a9699124 100644 --- a/pyneuroml/biosimulations.py +++ b/pyneuroml/biosimulations.py @@ -8,10 +8,18 @@ """ +import json import logging import typing +from datetime import datetime import requests +from requests_toolbelt.multipart.encoder import MultipartEncoder + +from pyneuroml import __version__ +from pyneuroml.annotations import create_annotation +from pyneuroml.archive import create_combine_archive +from pyneuroml.runners import run_jneuroml logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) @@ -31,9 +39,11 @@ def get_simulator_versions( "brian2", "copasi", ] -): +) -> typing.Dict[str, typing.List[str]]: """Get simulator list from biosimulators. + .. versionadded:: 1.2.10 + :param simulators: a simulator or list of simulators to get versions for :type simulators: str or list(str) :returns: json response from API @@ -52,3 +62,139 @@ def get_simulator_versions( all_siminfo[s["id"]] = [s["version"]] return all_siminfo + + +def submit_simulation( + rootfile: str, + metadata_file: typing.Optional[str] = None, + sim_dict: typing.Dict[str, typing.Union[int, str, typing.List[typing.Any]]] = {}, + dry_run: bool = True, +): + """Submit a simulation to Biosimulations using its REST API + + .. versionadded:: 1.2.10 + + :param rootfile: main LEMS or SEDML simulation file + If it is a LEMS file, a SEDML file will be generated for it + :type rootfile: str + :param metadata_file: path to a RDF metadata file to be included in the + OMEX archive. If not provided, a generic one with a title and + description will be generated. + :type metadata_file: str + :param sim_dict: dictionary holding parameters required to send the + simulation to biosimulations + + .. code-block:: json + + { + "name": "Kockout of gene A", + "simulator": "tellurium", + "simulatorVersion": "2.2.1", + "cpus": 1, + "memory": 8, + "maxTime": 20, + "envVars": [], + "purpose": "academic", + "email": "info@biosimulations.org", + } + + Here, the "name", "simulator", and "simulatorVersion" fields are + required. You can use the py::func`get_simulator_versions` function to + query Biosimulations or visit https://biosimulators.org/simulators + + See also: "SimulationRun" on this page (at the bottom) + https://api.biosimulations.org/#/Simulations/SimulationRunController_createRun + + :type sim_dict: dict + + :returns: the requests.post response object from the submission, or True if dry_run + + """ + if metadata_file is None: + logger.info("No metadata file given, generating one.") + metadata_file = "metadata.rdf" + with open(metadata_file, "w") as f: + annotation = create_annotation( + rootfile + ".omex", + title=f"Biosimulation of {rootfile} created using PyNeuroML version {__version__}", + description=f"Biosimulation of {rootfile} created using PyNeuroML version {__version__}", + creation_date=datetime.now().strftime("%Y-%m-%d"), + ) + print(annotation, file=f) + + if rootfile.startswith("LEMS") and rootfile.endswith(".xml"): + logger.info("Generating SED-ML file from LEMS file") + run_jneuroml("", rootfile, "-sedml") + rootfile = rootfile.replace(".xml", ".sedml") + + create_combine_archive( + rootfile, zipfile_extension=".omex", extra_files=[metadata_file] + ) + + return submit_simulation_archive(f"{rootfile}.omex", sim_dict, dry_run=dry_run) + + +def submit_simulation_archive( + archive_file: str, + sim_dict: typing.Dict[str, typing.Union[int, str, typing.List[str]]] = {}, + dry_run: bool = False, +) -> object: + """Submit an OMEX archive to biosimulations using the provided simulation run dictionary + + .. versionadded:: 1.2.10 + + Note that this function does not validate either the OMEX archive nor the + simulation dictionary. It simply submits it to the API. + + :param archive_file: OMEX archive file to submit + :type archive_file: str + :param sim_dict: dictionary holding parameters required to send the + simulation to biosimulations + + .. code-block:: json + + { + "name": "Kockout of gene A", + "simulator": "tellurium", + "simulatorVersion": "2.2.1", + "cpus": 1, + "memory": 8, + "maxTime": 20, + "envVars": [], + "purpose": "academic", + "email": "info@biosimulations.org", + } + + Here, the "name", "simulator", and "simulatorVersion" fields are + required. You can use the py::func`get_simulator_versions` function to + query Biosimulations or visit https://biosimulators.org/simulators + + See also: "SimulationRun" on this page (at the bottom) + https://api.biosimulations.org/#/Simulations/SimulationRunController_createRun + + :type sim_dict: dict + :returns: the requests.post response object, or True if dry_run + + """ + api_url = f"{biosimulations_api_url}/runs" + data_dict = {} # type: typing.Dict[str, typing.Any] + data_dict["file"] = (archive_file, open(archive_file, "rb")) + data_dict["simulationRun"] = json.dumps(sim_dict) + multipart_data = MultipartEncoder(fields=data_dict) + print(f"data is is:\n{multipart_data.to_string()}") + + if dry_run is False: + logger.info("Submitting archive to biosimulations") + response = requests.post( + api_url, + data=multipart_data, + headers={"Content-Type": multipart_data.content_type}, + ) # type: requests.Response + if response.status_code != requests.codes.ok: + response.raise_for_status() + else: + response = True + logger.info("Dry run, not submitting") + print(f"Simulation dictionary: {sim_dict}") + + return response diff --git a/tests/test_biosimulations.py b/tests/test_biosimulations.py index f830da24..56ab237f 100644 --- a/tests/test_biosimulations.py +++ b/tests/test_biosimulations.py @@ -9,8 +9,13 @@ import logging +import os -from pyneuroml.biosimulations import get_simulator_versions +from pyneuroml.biosimulations import ( + get_simulator_versions, + submit_simulation, + submit_simulation_archive, +) from . import BaseTestCase @@ -40,3 +45,37 @@ def test_get_simulator_versions(self): self.assertIn(s, simulators.keys()) versions = simulators[s] self.assertGreater(len(versions), 0) + + def test_submit_simulation(self): + """Test submit_simulation""" + os.chdir("examples") + response = submit_simulation( + "LEMS_NML2_Ex5_DetCell.xml", sim_dict={}, dry_run=True + ) + self.assertTrue(response) + + def test_submit_simulation_archive(self): + """Test submit_simulation_archive""" + dry_run = True + os.chdir("examples") + sim_dict = { + "name": "PyNeuroML test simulation", + "simulator": "neuron", + "simulatorVersion": "8.0.2", + "cpus": "1", + "memory": "8", + "maxTime": "20", + "purpose": "other", + "email": "something@something.com", + "envVars": None, + "projectId": None, + } + response = submit_simulation( + "LEMS_NML2_Ex5_DetCell.xml", sim_dict=sim_dict, dry_run=dry_run + ) + + if dry_run: + pass + else: + print(response.json()) + self.assertEqual(response.status_code, 201) From f6af0993ac59751d1bf5d1254128a3ac361b2ace Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Mon, 22 Apr 2024 18:24:43 +0100 Subject: [PATCH 07/10] feat(biosimulations): complete implementation Note that we only dry run because we do not want to bombard the prod instance with our tests. We'll ask them for a dev instance to test against. --- pyneuroml/biosimulations.py | 90 +++++++++++++++++++++++++++--------- tests/test_biosimulations.py | 14 +++--- 2 files changed, 75 insertions(+), 29 deletions(-) diff --git a/pyneuroml/biosimulations.py b/pyneuroml/biosimulations.py index a9699124..03517981 100644 --- a/pyneuroml/biosimulations.py +++ b/pyneuroml/biosimulations.py @@ -8,12 +8,12 @@ """ -import json import logging import typing from datetime import datetime import requests +from pydantic import BaseModel from requests_toolbelt.multipart.encoder import MultipartEncoder from pyneuroml import __version__ @@ -28,6 +28,27 @@ biosimulations_api_url = "https://api.biosimulations.org" +class _SimulationRunApiRequest(BaseModel): + """class for runSimulation data + + Based on + https://github.com/biosimulations/biosimulations-runutils/blob/dev/biosimulations_runutils/biosim_pipeline/biosim_api.py + + Once biosimulations-runutils is published, we will use their API instead + of replicating it ourselves. + """ + + name: str + simulator: str + simulatorVersion: str + maxTime: int + cpus: typing.Optional[int] = None + memory: typing.Optional[int] = None + purpose: typing.Optional[str] = "academic" + email: typing.Optional[str] = None + envVars: typing.Optional[typing.List[str]] = [] + + def get_simulator_versions( simulators: typing.Union[str, typing.List[str]] = [ "neuron", @@ -67,7 +88,9 @@ def get_simulator_versions( def submit_simulation( rootfile: str, metadata_file: typing.Optional[str] = None, - sim_dict: typing.Dict[str, typing.Union[int, str, typing.List[typing.Any]]] = {}, + sim_dict: typing.Dict[ + str, typing.Optional[typing.Union[int, str, typing.List[typing.Any]]] + ] = {}, dry_run: bool = True, ): """Submit a simulation to Biosimulations using its REST API @@ -177,24 +200,49 @@ def submit_simulation_archive( """ api_url = f"{biosimulations_api_url}/runs" - data_dict = {} # type: typing.Dict[str, typing.Any] - data_dict["file"] = (archive_file, open(archive_file, "rb")) - data_dict["simulationRun"] = json.dumps(sim_dict) - multipart_data = MultipartEncoder(fields=data_dict) - print(f"data is is:\n{multipart_data.to_string()}") - - if dry_run is False: - logger.info("Submitting archive to biosimulations") - response = requests.post( - api_url, - data=multipart_data, - headers={"Content-Type": multipart_data.content_type}, - ) # type: requests.Response - if response.status_code != requests.codes.ok: - response.raise_for_status() - else: - response = True - logger.info("Dry run, not submitting") - print(f"Simulation dictionary: {sim_dict}") + logger.debug(f"Sim dict is: {sim_dict}") + + simulation_run_request = _SimulationRunApiRequest(**sim_dict) + print(f"simulation_run_request is {simulation_run_request.json()}") + + with open(archive_file, "rb") as archive_file_handle: + multipart_form_data: dict[ + str, + typing.Union[typing.Tuple[str, typing.BinaryIO], typing.Tuple[None, str]], + ] = { + "file": (archive_file, archive_file_handle), + "simulationRun": (None, simulation_run_request.json()), + } + + print(f"data is:\n{multipart_form_data}") + + m = MultipartEncoder(fields=multipart_form_data) + + print(f"multipart encoded data is {m}") + print(f"with content type: {m.content_type}") + + if dry_run is False: + logger.info("Submitting archive to biosimulations") + response = requests.post( + api_url, data=m, headers={"Content-Type": m.content_type} + ) # type: requests.Response + if response.status_code != requests.codes.CREATED: + response.raise_for_status() + else: + serv_response = response.json() + print( + f"Submitted {archive_file} successfully with id: {serv_response['id']}" + ) + print(f"View: {biosimulations_api_url}/runs/{serv_response['id']}") + print( + f"Downloads: {biosimulations_api_url}/results/{serv_response['id']}/download" + ) + print( + f"Logs: {biosimulations_api_url}/logs/{serv_response['id']}?includeOutput=true" + ) + else: + response = True + logger.info("Dry run, not submitting") + print(f"Simulation dictionary: {sim_dict}") return response diff --git a/tests/test_biosimulations.py b/tests/test_biosimulations.py index 56ab237f..372f6b5b 100644 --- a/tests/test_biosimulations.py +++ b/tests/test_biosimulations.py @@ -56,20 +56,18 @@ def test_submit_simulation(self): def test_submit_simulation_archive(self): """Test submit_simulation_archive""" + # TODO: we don't want to use the prod instance for testing, so currently + # disabled. We'll point it at a dev instance for testingo + # Manually set to False to test. dry_run = True os.chdir("examples") sim_dict = { "name": "PyNeuroML test simulation", "simulator": "neuron", - "simulatorVersion": "8.0.2", - "cpus": "1", - "memory": "8", + "simulatorVersion": "latest", "maxTime": "20", - "purpose": "other", - "email": "something@something.com", - "envVars": None, - "projectId": None, } + response = submit_simulation( "LEMS_NML2_Ex5_DetCell.xml", sim_dict=sim_dict, dry_run=dry_run ) @@ -77,5 +75,5 @@ def test_submit_simulation_archive(self): if dry_run: pass else: - print(response.json()) + logger.debug(response.json()) self.assertEqual(response.status_code, 201) From 3ca0d59fbf2929807e08f2b05dad123552cbc3b7 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Mon, 22 Apr 2024 18:32:16 +0100 Subject: [PATCH 08/10] feat(setup.cfg): add deps for biosimulations API use Added these under the combine extra --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index 226a9ca5..ec414a2d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -121,6 +121,8 @@ combine = python-libsbml python-libsedml pyNeuroML[annotations] + pydantic + requests-toolbelt tellurium = tellurium From 811f0e9468008848cc713fa25dadd8c6b985c498 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Mon, 22 Apr 2024 18:42:00 +0100 Subject: [PATCH 09/10] chore(biosimulators): convert some prints to loggings [skip ci] --- pyneuroml/biosimulations.py | 10 +++++----- tests/test_biosimulations.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyneuroml/biosimulations.py b/pyneuroml/biosimulations.py index 03517981..18aa74a1 100644 --- a/pyneuroml/biosimulations.py +++ b/pyneuroml/biosimulations.py @@ -203,7 +203,7 @@ def submit_simulation_archive( logger.debug(f"Sim dict is: {sim_dict}") simulation_run_request = _SimulationRunApiRequest(**sim_dict) - print(f"simulation_run_request is {simulation_run_request.json()}") + logger.debug(f"simulation_run_request is {simulation_run_request.json()}") with open(archive_file, "rb") as archive_file_handle: multipart_form_data: dict[ @@ -214,12 +214,12 @@ def submit_simulation_archive( "simulationRun": (None, simulation_run_request.json()), } - print(f"data is:\n{multipart_form_data}") + logger.debug(f"data is:\n{multipart_form_data}") m = MultipartEncoder(fields=multipart_form_data) - print(f"multipart encoded data is {m}") - print(f"with content type: {m.content_type}") + logger.info(f"multipart encoded data is {m}") + logger.info(f"with content type: {m.content_type}") if dry_run is False: logger.info("Submitting archive to biosimulations") @@ -242,7 +242,7 @@ def submit_simulation_archive( ) else: response = True - logger.info("Dry run, not submitting") + print("Dry run, not submitting") print(f"Simulation dictionary: {sim_dict}") return response diff --git a/tests/test_biosimulations.py b/tests/test_biosimulations.py index 372f6b5b..ac404d5e 100644 --- a/tests/test_biosimulations.py +++ b/tests/test_biosimulations.py @@ -30,7 +30,7 @@ def test_get_simulator_versions(self): """Test get_simulators""" simulators = get_simulator_versions() - print(simulators) + logging.info(simulators) for s in [ "neuron", From c79613069139cf80604b3c99515c79bbfc3b42b5 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Tue, 23 Apr 2024 12:01:17 +0100 Subject: [PATCH 10/10] feat(biosimulations): update tests to correctly refer to test file --- pyneuroml/biosimulations.py | 6 ++++-- tests/test_biosimulations.py | 15 ++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pyneuroml/biosimulations.py b/pyneuroml/biosimulations.py index 18aa74a1..ce9bad50 100644 --- a/pyneuroml/biosimulations.py +++ b/pyneuroml/biosimulations.py @@ -203,7 +203,9 @@ def submit_simulation_archive( logger.debug(f"Sim dict is: {sim_dict}") simulation_run_request = _SimulationRunApiRequest(**sim_dict) - logger.debug(f"simulation_run_request is {simulation_run_request.json()}") + logger.debug( + f"simulation_run_request is {simulation_run_request.model_dump_json()}" + ) with open(archive_file, "rb") as archive_file_handle: multipart_form_data: dict[ @@ -211,7 +213,7 @@ def submit_simulation_archive( typing.Union[typing.Tuple[str, typing.BinaryIO], typing.Tuple[None, str]], ] = { "file": (archive_file, archive_file_handle), - "simulationRun": (None, simulation_run_request.json()), + "simulationRun": (None, simulation_run_request.model_dump_json()), } logger.debug(f"data is:\n{multipart_form_data}") diff --git a/tests/test_biosimulations.py b/tests/test_biosimulations.py index ac404d5e..8443afc1 100644 --- a/tests/test_biosimulations.py +++ b/tests/test_biosimulations.py @@ -10,6 +10,7 @@ import logging import os +import pathlib from pyneuroml.biosimulations import ( get_simulator_versions, @@ -46,21 +47,16 @@ def test_get_simulator_versions(self): versions = simulators[s] self.assertGreater(len(versions), 0) - def test_submit_simulation(self): - """Test submit_simulation""" - os.chdir("examples") - response = submit_simulation( - "LEMS_NML2_Ex5_DetCell.xml", sim_dict={}, dry_run=True - ) - self.assertTrue(response) - def test_submit_simulation_archive(self): """Test submit_simulation_archive""" # TODO: we don't want to use the prod instance for testing, so currently # disabled. We'll point it at a dev instance for testingo # Manually set to False to test. dry_run = True - os.chdir("examples") + thispath = pathlib.Path(__file__) + dirname = str(thispath.parent.parent) + cwd = os.getcwd() + os.chdir(dirname + "/examples") sim_dict = { "name": "PyNeuroML test simulation", "simulator": "neuron", @@ -71,6 +67,7 @@ def test_submit_simulation_archive(self): response = submit_simulation( "LEMS_NML2_Ex5_DetCell.xml", sim_dict=sim_dict, dry_run=dry_run ) + os.chdir(cwd) if dry_run: pass