diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 311c4fbc..b804cbde 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -34,6 +34,7 @@ jobs: - name: Generate artifacts run: | python -m venv .venv + python -m pip install uv source .venv/bin/activate make docs - name: Setup Pages diff --git a/clients/python/docs/BasicTutorial_v0.5.0.md b/clients/python/docs/BasicTutorial_v0.5.0.md deleted file mode 100644 index f6f14212..00000000 --- a/clients/python/docs/BasicTutorial_v0.5.0.md +++ /dev/null @@ -1,371 +0,0 @@ -# Basic Tutorial - - - -## Installation -Install the python client and check the installation as follows: - - -```python -import importlib -if importlib.util.find_spec('osparc') is not None: - ! pip install osparc -! python -c "import osparc; print(osparc.__version__)" -``` - -## Setup - -To setup the client, we need to provide a username and password to the configuration. These can be obtained in the UI under [Preferences > API Settings > API Keys](https://docs.osparc.io/#/docs/platform_introduction/user_setup/security_details?id=generating-o%c2%b2s%c2%b2parc-tokens). Use the *API key* as username and the *API secret* as password. For security reasons, you should not write these values in your script but instead set them up via environment variables or read them from a separate file. In this example, we use environment variables which will be referred to as "OSPARC_API_KEY" and "OSPARC_API_SECRET" for the rest of the tutorial. - - -```python - -import os -from osparc import Configuration - -cfg = Configuration( - host=os.environ["OSPARC_API_HOST"], - username=os.environ["OSPARC_API_KEY"], - password=os.environ["OSPARC_API_SECRET"], -) -print(cfg.host) - -``` - -The configuration can now be used to create an instance of the API client. The API client is responsible of the communication with the osparc platform - - -The functions in the [osparc API](https://api.osparc.io/dev/doc#/) are grouped into sections such as *meta*, *users*, *files* or *solvers*. Each section address a different resource of the platform. - - - -For example, the *users* section includes functions about the user (i.e. you) and can be accessed initializing a ``UsersApi``: - - -```python -from osparc import ApiClient, UsersApi - -with ApiClient(cfg) as api_client: - - users_api = UsersApi(api_client) - - profile = users_api.get_my_profile() - print(profile) - - # - # {'first_name': 'foo', - # 'gravatar_id': 'aa33fssec77ea434c2ea4fb92d0fd379e', - # 'groups': {'all': {'description': 'all users', - # 'gid': '1', - # 'label': 'Everyone'}, - # 'me': {'description': 'primary group', - # 'gid': '2', - # 'label': 'foo'}, - # 'organizations': []}, - # 'last_name': '', - # 'login': 'foo@itis.swiss', - # 'role': 'USER'} - # -``` - -## Solvers Workflow - -The osparc API can be used to execute any computational service published in the platform. This means that any computational service listed in the UI under the [Services Tab](https://docs.osparc.io/#/docs/platform_introduction/services) is accessible from the API. Note that computational services are denoted as *solvers* in the API for convenience, but they refer to the same concept. - - -Let's use the sleepers computational service to illustrate a typical workflow. The sleepers computational service is a very basic service that simply waits (i.e. *sleeps*) a given time before producing some outputs. It takes as input one natural number, an optional text file input that contains another natural number and a boolean in the form of a checkbox. It also provides two outputs: one natural number and a file containing a single natural number. - - -```python -import time -from pathlib import Path -from zipfile import ZipFile -from tempfile import TemporaryDirectory - -import osparc - -Path("file_with_number.txt").write_text("3") - -with osparc.ApiClient(cfg) as api_client: - - files_api = osparc.FilesApi(api_client) - input_file: osparc.File = files_api.upload_file(file="file_with_number.txt") - - solvers_api = osparc.SolversApi(api_client) - solver: osparc.Solver = solvers_api.get_solver_release( - "simcore/services/comp/itis/sleeper", "2.1.6" - ) - - job: osparc.Job = solvers_api.create_job( - solver.id, - solver.version, - osparc.JobInputs( - { - "input_4": 2, - "input_3": "false", - "input_2": 3, - "input_1": input_file, - } - ), - ) - - status: osparc.JobStatus = solvers_api.start_job(solver.id, solver.version, job.id) - while not status.stopped_at: - time.sleep(3) - status = solvers_api.inspect_job(solver.id, solver.version, job.id) - print("Solver progress", f"{status.progress}/100", flush=True) - assert status.state == "SUCCESS" - # - # Solver progress 0/100 - # Solver progress 100/100 - - outputs: osparc.JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id) - - print(f"Job {outputs.job_id} got these results:") - for output_name, result in outputs.results.items(): - print(output_name, "=", result) - - # - # Job 19fc28f7-46fb-4e96-9129-5e924801f088 got these results: - # - # output_1 = {'checksum': '859fda0cb82fc4acb4686510a172d9a9-1', - # 'content_type': 'text/plain', - # 'filename': 'single_number.txt', - # 'id': '9fb4f70e-3589-3e9e-991e-3059086c3aae'} - # output_2 = 4.0 - - logfile_path: str = solvers_api.get_job_output_logfile( - solver.id, solver.version, job.id - ) - zip_path = Path(logfile_path) - - with TemporaryDirectory() as tmp_dir: - with ZipFile(f"{zip_path}") as fzip: - fzip.extractall(tmp_dir) - logfiles = list(Path(tmp_dir).glob("*.log*")) - print("Unzipped", logfiles[0], "contains:\n", logfiles[0].read_text()) - # - # Unzipped extracted/sleeper_2.0.2.logs contains: - # 2022-06-01T18:15:00.405035847+02:00 Entrypoint for stage production ... - # 2022-06-01T18:15:00.421279969+02:00 User : uid=0(root) gid=0(root) groups=0(root) - # 2022-06-01T18:15:00.421560331+02:00 Workdir : /home/scu - # ... - # 2022-06-01T18:15:00.864550043+02:00 - # 2022-06-01T18:15:03.923876794+02:00 Will sleep for 3 seconds - # 2022-06-01T18:15:03.924473521+02:00 [PROGRESS] 1/3... - # 2022-06-01T18:15:03.925021846+02:00 Remaining sleep time 0.9999995231628418 - # 2022-06-01T18:15:03.925558026+02:00 [PROGRESS] 2/3... - # 2022-06-01T18:15:03.926103062+02:00 Remaining sleep time 0.9999985694885254 - # 2022-06-01T18:15:03.926643184+02:00 [PROGRESS] 3/3... - # 2022-06-01T18:15:03.933544384+02:00 Remaining sleep time 0.9999983310699463 - - download_path: str = files_api.download_file(file_id=outputs.results["output_1"].id) - print(Path(download_path).read_text()) - # - # 7 - -``` - -The script above - -1. Uploads a file ``file_with_number.txt`` -2. Selects version ``2.0.2`` of the ``sleeper`` -3. Runs the ``sleeper`` and provides a reference to the uploaded file and other values as input parameters -4. Monitors the status of the solver while it is running in the platform -5. When the execution completes, it checks the outputs -6. The logs are downloaded, unzipped and saved to a new ```extracted``` directory -7. One of the outputs is a file and it is downloaded - - -#### Files - -Files used as input to solvers or produced by solvers in the platform are accessible in the **files** section and specifically with the ``FilesApi`` class. -In order to use a file as input, it has to be uploaded first and the reference used in the corresponding solver's input. - -```python -files_api = FilesApi(api_client) -input_file: File = files_api.upload_file(file="file_with_number.txt") - - -# ... - - -outputs: JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id) -results_file: File = outputs.results["output_1"] -download_path: str = files_api.download_file(file_id=results_file.id) -``` - -In the snippet above, ``input_file`` is a ``File`` reference to the uploaded file and that is passed as input to the solver. Analogously, ``results_file`` is a ``File`` produced by the solver and that can also be downloaded. - - -#### Solvers, Inputs and Outputs - -The inputs and outputs are specific for every solver. Every input/output has a name and an associated type that can be as simple as booleans, numbers, strings ... or more complex as files. You can find this information in the UI under Services Tab, selecting the service card > Information > Raw metadata. For instance, the ``sleeper`` version ``2.1.6`` has the following ``raw-metadata``: - -```json -{ - "inputs": { -  "input_1": { -   "displayOrder": 1, -   "label": "File with int number", -   "description": "Pick a file containing only one integer", -   "type": "data:text/plain", -   "fileToKeyMap": { -    "single_number.txt": "input_1" -   }, -   "keyId": "input_1" -  }, -  "input_2": { -   "unitLong": "second", -   "unitShort": "s", -   "label": "Sleep interval", -   "description": "Choose an amount of time to sleep in range [0-65]", -   "keyId": "input_2", -   "displayOrder": 2, -   "type": "ref_contentSchema", -   "contentSchema": { -    "title": "Sleep interval", -    "type": "integer", -    "x_unit": "second", -    "minimum": 0, -    "maximum": 65 -   }, -   "defaultValue": 2 -  }, -  "input_3": { -   "displayOrder": 3, -   "label": "Fail after sleep", -   "description": "If set to true will cause service to fail after it sleeps", -   "type": "boolean", -   "defaultValue": false, -   "keyId": "input_3" -  }, -  "input_4": { -   "unitLong": "meter", -   "unitShort": "m", -   "label": "Distance to bed", -   "description": "It will first walk the distance to bed", -   "keyId": "input_4", -   "displayOrder": 4, -   "type": "ref_contentSchema", -   "contentSchema": { -    "title": "Distance to bed", -    "type": "integer", -    "x_unit": "meter" -   }, -   "defaultValue": 0 -  } - } -``` - -So, the inputs can be set as follows - -```python -# ... -job: osparc.Job = solvers_api.create_job( - solver.id, - solver.version, - osparc.JobInputs( - { - "input_4": 2, - "input_3": "false", - "input_2": 3, - "input_1": input_file, - } - ), -) -``` - -And the metadata for the outputs are - -```json -  "output_1": { -   "displayOrder": 1, -   "label": "File containing one random integer", -   "description": "Integer is generated in range [1-9]", -   "type": "data:text/plain", -   "fileToKeyMap": { -    "single_number.txt": "output_1" -   }, -   "keyId": "output_1" -  }, -  "output_2": { -   "unitLong": "second", -   "unitShort": "s", -   "label": "Random sleep interval", -   "description": "Interval is generated in range [1-9]", -   "keyId": "output_2", -   "displayOrder": 2, -   "type": "ref_contentSchema", -   "contentSchema": { -    "title": "Random sleep interval", -    "type": "integer", -    "x_unit": "second" -   } -``` - -so this information determines which output corresponds to a number or a file in the following snippet - -```python -# ... - -outputs: JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id) - -output_file = outputs.results["output_1"] -number = outputs.results["output_2"] - -assert status.state == "SUCCESS" - - -assert isinstance(output_file, File) -assert isinstance(number, float) - -# output file exists -assert files_api.get_file(output_file.id) == output_file - -# can download and open -download_path: str = files_api.download_file(file_id=output_file.id) -assert float(Path(download_path).read_text()), "contains a random number" - -``` - -#### Job Status - -Once the client script triggers the solver, the solver runs in the platform and the script is freed. Sometimes, it is convenient to monitor the status of the run to see e.g. the progress of the execution or if the run was completed. - -A solver runs in a plaforma starts a ``Job``. Using the ``solvers_api``, allows us to inspect the ``Job`` and get a ``JobStatus`` with information about its status. For instance - -```python - status: JobStatus = solvers_api.start_job(solver.id, solver.version, job.id) - while not status.stopped_at: - time.sleep(3) - status = solvers_api.inspect_job(solver.id, solver.version, job.id) - print("Solver progress", f"{status.progress}/100", flush=True) -``` - -#### Logs - -When a solver runs, it will generate logs during execution which are then saved as .log files. Starting from the osparc Python Client version 0.5.0, The ``solvers_api`` also allows us to obtain the ``logfile_path`` associated with a particular ``Job``. This is a zip file that can then be extracted and saved. For instance - -```python -logfile_path: str = solvers_api.get_job_output_logfile( - solver.id, solver.version, job.id -) -zip_path = Path(logfile_path) - -extract_dir = Path("./extracted") -extract_dir.mkdir() - -with ZipFile(f"{zip_path}") as fzip: - fzip.extractall(f"{extract_dir}") -``` - -## References - -- [osparc API python client] documentation -- [osparc API] documentation -- A full script with this tutorial: [``sleeper.py``](https://github.com/ITISFoundation/osparc-simcore/blob/master/tests/public-api/examples/sleeper.py) - -[osparc API python client]:https://itisfoundation.github.io/osparc-simcore-clients/#/ -[osparc API]:https://api.osparc.io/dev/doc#/ -[Download as BasicTutorial_v0.5.0.ipynb](clients/python/docs/BasicTutorial_v0.5.0.ipynb ":ignore title") diff --git a/clients/python/docs/BasicTutorial_v0.6.0.md b/clients/python/docs/BasicTutorial_v0.6.0.md deleted file mode 100644 index a5e0c5dc..00000000 --- a/clients/python/docs/BasicTutorial_v0.6.0.md +++ /dev/null @@ -1,376 +0,0 @@ -# Basic Tutorial - - - -## Installation -Install the python client and check the installation as follows: - - -```python -import importlib -if importlib.util.find_spec('osparc') is not None: - ! pip install osparc -! python -c "import osparc; print(osparc.__version__)" -``` - -## Setup - -To setup the client, we need to provide a username and password to the configuration. These can be obtained in the UI under [Preferences > API Settings > API Keys](https://docs.osparc.io/#/docs/platform_introduction/user_setup/security_details?id=generating-o%c2%b2s%c2%b2parc-tokens). Use the *API key* as username and the *API secret* as password. For security reasons, you should not write these values in your script but instead set them up via environment variables or read them from a separate file. In this example, we use environment variables which will be referred to as "OSPARC_API_KEY" and "OSPARC_API_SECRET" for the rest of the tutorial. - - -```python - -import os -from osparc import Configuration - -cfg = Configuration( - host=os.environ["OSPARC_API_HOST"], - username=os.environ["OSPARC_API_KEY"], - password=os.environ["OSPARC_API_SECRET"], -) -print(cfg.host) - -``` - -The configuration can now be used to create an instance of the API client. The API client is responsible of the communication with the osparc platform - - -The functions in the [osparc API](https://api.osparc.io/dev/doc#/) are grouped into sections such as *meta*, *users*, *files* or *solvers*. Each section address a different resource of the platform. - - - -For example, the *users* section includes functions about the user (i.e. you) and can be accessed initializing a ``UsersApi``: - - -```python -from osparc import ApiClient, UsersApi - -with ApiClient(cfg) as api_client: - - users_api = UsersApi(api_client) - - profile = users_api.get_my_profile() - print(profile) - - # - # {'first_name': 'foo', - # 'gravatar_id': 'aa33fssec77ea434c2ea4fb92d0fd379e', - # 'groups': {'all': {'description': 'all users', - # 'gid': '1', - # 'label': 'Everyone'}, - # 'me': {'description': 'primary group', - # 'gid': '2', - # 'label': 'foo'}, - # 'organizations': []}, - # 'last_name': '', - # 'login': 'foo@itis.swiss', - # 'role': 'USER'} - # -``` - -## Solvers Workflow - -The osparc API can be used to execute any computational service published in the platform. This means that any computational service listed in the UI under the [Services Tab](https://docs.osparc.io/#/docs/platform_introduction/services) is accessible from the API. Note that computational services are denoted as *solvers* in the API for convenience, but they refer to the same concept. - - -Let's use the sleepers computational service to illustrate a typical workflow. The sleepers computational service is a very basic service that simply waits (i.e. *sleeps*) a given time before producing some outputs. It takes as input one natural number, an optional text file input that contains another natural number and a boolean in the form of a checkbox. It also provides two outputs: one natural number and a file containing a single natural number. - - -```python -import time -from pathlib import Path -from zipfile import ZipFile -from tempfile import TemporaryDirectory - -import osparc - -Path("file_with_number.txt").write_text("3") - -with osparc.ApiClient(cfg) as api_client: - - files_api = osparc.FilesApi(api_client) - input_file: osparc.File = files_api.upload_file(file="file_with_number.txt") - - solver_key: str = "simcore/services/comp/itis/sleeper" - solver_version: str = "2.1.6" - - solvers_api = osparc.SolversApi(api_client) - solver: osparc.Solver = solvers_api.get_solver_release(solver_key=solver_key, version=solver_version) - - solver_ports = solvers_api.list_solver_ports(solver.id, solver.version) - print(f"solver_ports: {solver_ports}") - - job: osparc.Job = solvers_api.create_job( - solver.id, - solver.version, - osparc.JobInputs( - { - "input_4": 2, - "input_3": "false", - "input_2": 3, - "input_1": input_file, - } - ), - ) - - status: osparc.JobStatus = solvers_api.start_job(solver.id, solver.version, job.id) - while not status.stopped_at: - time.sleep(3) - status = solvers_api.inspect_job(solver.id, solver.version, job.id) - print("Solver progress", f"{status.progress}/100", flush=True) - assert status.state == "SUCCESS" - - # - # Solver progress 0/100 - # Solver progress 100/100 - - outputs: osparc.JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id) - - print(f"Job {outputs.job_id} got these results:") - for output_name, result in outputs.results.items(): - print(output_name, "=", result) - - # - # Job 19fc28f7-46fb-4e96-9129-5e924801f088 got these results: - # - # output_1 = {'checksum': '859fda0cb82fc4acb4686510a172d9a9-1', - # 'content_type': 'text/plain', - # 'filename': 'single_number.txt', - # 'id': '9fb4f70e-3589-3e9e-991e-3059086c3aae'} - # output_2 = 4.0 - - logfile_path: str = solvers_api.get_job_output_logfile( - solver.id, solver.version, job.id - ) - zip_path = Path(logfile_path) - - with TemporaryDirectory() as tmp_dir: - with ZipFile(f"{zip_path}") as fzip: - fzip.extractall(tmp_dir) - logfiles = list(Path(tmp_dir).glob("*.log*")) - print("Unzipped", logfiles[0], "contains:\n", logfiles[0].read_text()) - # - # Unzipped extracted/sleeper_2.0.2.logs contains: - # 2022-06-01T18:15:00.405035847+02:00 Entrypoint for stage production ... - # 2022-06-01T18:15:00.421279969+02:00 User : uid=0(root) gid=0(root) groups=0(root) - # 2022-06-01T18:15:00.421560331+02:00 Workdir : /home/scu - # ... - # 2022-06-01T18:15:00.864550043+02:00 - # 2022-06-01T18:15:03.923876794+02:00 Will sleep for 3 seconds - # 2022-06-01T18:15:03.924473521+02:00 [PROGRESS] 1/3... - # 2022-06-01T18:15:03.925021846+02:00 Remaining sleep time 0.9999995231628418 - # 2022-06-01T18:15:03.925558026+02:00 [PROGRESS] 2/3... - # 2022-06-01T18:15:03.926103062+02:00 Remaining sleep time 0.9999985694885254 - # 2022-06-01T18:15:03.926643184+02:00 [PROGRESS] 3/3... - # 2022-06-01T18:15:03.933544384+02:00 Remaining sleep time 0.9999983310699463 - - download_path: str = files_api.download_file(file_id=outputs.results["output_1"].id) - print(Path(download_path).read_text()) - # - # 7 - -``` - -The script above - -1. Uploads a file ``file_with_number.txt`` -2. Selects version ``2.0.2`` of the ``sleeper`` -3. Runs the ``sleeper`` and provides a reference to the uploaded file and other values as input parameters -4. Monitors the status of the solver while it is running in the platform -5. When the execution completes, it checks the outputs -6. The logs are downloaded, unzipped and saved to a new ```extracted``` directory -7. One of the outputs is a file and it is downloaded - - -#### Files - -Files used as input to solvers or produced by solvers in the platform are accessible in the **files** section and specifically with the ``FilesApi`` class. -In order to use a file as input, it has to be uploaded first and the reference used in the corresponding solver's input. - -```python -files_api = FilesApi(api_client) -input_file: File = files_api.upload_file(file="file_with_number.txt") - - -# ... - - -outputs: JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id) -results_file: File = outputs.results["output_1"] -download_path: str = files_api.download_file(file_id=results_file.id) -``` - -In the snippet above, ``input_file`` is a ``File`` reference to the uploaded file and that is passed as input to the solver. Analogously, ``results_file`` is a ``File`` produced by the solver and that can also be downloaded. - - -#### Solvers, Inputs and Outputs - -The inputs and outputs are specific for every solver. Every input/output has a name and an associated type that can be as simple as booleans, numbers, strings ... or more complex as files. You can find this information in the UI under Services Tab, selecting the service card > Information > Raw metadata. For instance, the ``sleeper`` version ``2.0.2`` has the following ``raw-metadata``: - -```json -{ - "inputs": { -  "input_1": { -   "displayOrder": 1, -   "label": "File with int number", -   "description": "Pick a file containing only one integer", -   "type": "data:text/plain", -   "fileToKeyMap": { -    "single_number.txt": "input_1" -   }, -   "keyId": "input_1" -  }, -  "input_2": { -   "unitLong": "second", -   "unitShort": "s", -   "label": "Sleep interval", -   "description": "Choose an amount of time to sleep in range [0-65]", -   "keyId": "input_2", -   "displayOrder": 2, -   "type": "ref_contentSchema", -   "contentSchema": { -    "title": "Sleep interval", -    "type": "integer", -    "x_unit": "second", -    "minimum": 0, -    "maximum": 65 -   }, -   "defaultValue": 2 -  }, -  "input_3": { -   "displayOrder": 3, -   "label": "Fail after sleep", -   "description": "If set to true will cause service to fail after it sleeps", -   "type": "boolean", -   "defaultValue": false, -   "keyId": "input_3" -  }, -  "input_4": { -   "unitLong": "meter", -   "unitShort": "m", -   "label": "Distance to bed", -   "description": "It will first walk the distance to bed", -   "keyId": "input_4", -   "displayOrder": 4, -   "type": "ref_contentSchema", -   "contentSchema": { -    "title": "Distance to bed", -    "type": "integer", -    "x_unit": "meter" -   }, -   "defaultValue": 0 -  } - } -``` - -So, the inputs can be set as follows - -```python -# ... -job: osparc.Job = solvers_api.create_job( - solver.id, - solver.version, - osparc.JobInputs( - { - "input_4": 2, - "input_3": "false", - "input_2": 3, - "input_1": input_file, - } - ), -) -``` - -And the metadata for the outputs are - -```json -  "output_1": { -   "displayOrder": 1, -   "label": "File containing one random integer", -   "description": "Integer is generated in range [1-9]", -   "type": "data:text/plain", -   "fileToKeyMap": { -    "single_number.txt": "output_1" -   }, -   "keyId": "output_1" -  }, -  "output_2": { -   "unitLong": "second", -   "unitShort": "s", -   "label": "Random sleep interval", -   "description": "Interval is generated in range [1-9]", -   "keyId": "output_2", -   "displayOrder": 2, -   "type": "ref_contentSchema", -   "contentSchema": { -    "title": "Random sleep interval", -    "type": "integer", -    "x_unit": "second" -   } -``` - -so this information determines which output corresponds to a number or a file in the following snippet - -```python -# ... - -outputs: JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id) - -output_file = outputs.results["output_1"] -number = outputs.results["output_2"] - -assert status.state == "SUCCESS" - - -assert isinstance(output_file, File) -assert isinstance(number, float) - -# output file exists -assert files_api.get_file(output_file.id) == output_file - -# can download and open -download_path: str = files_api.download_file(file_id=output_file.id) -assert float(Path(download_path).read_text()), "contains a random number" - -``` - -#### Job Status - -Once the client script triggers the solver, the solver runs in the platform and the script is freed. Sometimes, it is convenient to monitor the status of the run to see e.g. the progress of the execution or if the run was completed. - -A solver runs in a plaforma starts a ``Job``. Using the ``solvers_api``, allows us to inspect the ``Job`` and get a ``JobStatus`` with information about its status. For instance - -```python - status: JobStatus = solvers_api.start_job(solver.id, solver.version, job.id) - while not status.stopped_at: - time.sleep(3) - status = solvers_api.inspect_job(solver.id, solver.version, job.id) - print("Solver progress", f"{status.progress}/100", flush=True) -``` - -#### Logs - -When a solver runs, it will generate logs during execution which are then saved as .log files. Starting from the osparc Python Client version 0.5.0, The ``solvers_api`` also allows us to obtain the ``logfile_path`` associated with a particular ``Job``. This is a zip file that can then be extracted and saved. For instance - -```python -logfile_path: str = solvers_api.get_job_output_logfile( - solver.id, solver.version, job.id -) -zip_path = Path(logfile_path) - -extract_dir = Path("./extracted") -extract_dir.mkdir() - -with ZipFile(f"{zip_path}") as fzip: - fzip.extractall(f"{extract_dir}") -``` - -## References - -- [osparc API python client] documentation -- [osparc API] documentation -- A full script with this tutorial: [``sleeper.py``](https://github.com/ITISFoundation/osparc-simcore/blob/master/tests/public-api/examples/sleeper.py) - -[osparc API python client]:https://itisfoundation.github.io/osparc-simcore-clients/#/ -[osparc API]:https://api.osparc.io/dev/doc#/ -[Download as BasicTutorial_v0.6.0.ipynb](clients/python/docs/BasicTutorial_v0.6.0.ipynb ":ignore title") diff --git a/clients/python/docs/BasicTutorial_v0.8.0.ipynb b/clients/python/docs/BasicTutorial_v0.8.0.ipynb new file mode 100644 index 00000000..670b7811 --- /dev/null +++ b/clients/python/docs/BasicTutorial_v0.8.0.ipynb @@ -0,0 +1,552 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "f15de720", + "metadata": {}, + "source": [ + "# Basic Tutorial\n", + "\n", + "\n", + "\n", + "## Installation\n", + "Install the python client and check the installation as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09653e21", + "metadata": { + "attributes": { + "classes": [ + "command" + ], + "id": "" + } + }, + "outputs": [], + "source": [ + "import importlib\n", + "\n", + "if importlib.util.find_spec(\"osparc\") is not None:\n", + " ! pip install osparc\n", + "! python -c \"import osparc; print(osparc.__version__)\"" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "34c8833d", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "To setup the client, we need to provide a username and password to the configuration. These can be obtained in the UI under [Preferences > API Settings > API Keys](https://docs.osparc.io/#/docs/platform_introduction/user_setup/security_details?id=generating-o%c2%b2s%c2%b2parc-tokens). Use the *API key* as username and the *API secret* as password. These should be specified as environment variables \"OSPARC_API_KEY\" and \"OSPARC_API_SECRET\" respectively. In addition you can specify the osparc endpoint you want to use (e.g. `https://api.osparc.io`) via the environment variable \"OSPARC_API_HOST\"." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "630c5926", + "metadata": {}, + "source": [ + "The functions in the [osparc API](https://api.osparc.io/dev/doc#/) are grouped into sections such as *meta*, *users*, *files*, *solvers*, *studies*, *wallets* and *credits*. Each section address a different resource of the platform.\n", + "\n", + "For example, the *users* section includes functions about the user (i.e. you) and can be accessed initializing a ``UsersApi``:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29337833", + "metadata": {}, + "outputs": [], + "source": [ + "from osparc import ApiClient, UsersApi\n", + "\n", + "with ApiClient() as api_client:\n", + " users_api = UsersApi(api_client)\n", + "\n", + " profile = users_api.get_my_profile()\n", + " print(profile)\n", + "\n", + " #\n", + " # {'first_name': 'foo',\n", + " # 'gravatar_id': 'aa33fssec77ea434c2ea4fb92d0fd379e',\n", + " # 'groups': {'all': {'description': 'all users',\n", + " # 'gid': '1',\n", + " # 'label': 'Everyone'},\n", + " # 'me': {'description': 'primary group',\n", + " # 'gid': '2',\n", + " # 'label': 'foo'},\n", + " # 'organizations': []},\n", + " # 'last_name': '',\n", + " # 'login': 'foo@itis.swiss',\n", + " # 'role': 'USER'}\n", + " #" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "6912889e", + "metadata": {}, + "source": [ + "## Solvers Workflow\n", + "\n", + "The osparc API can be used to execute any computational service published in the platform. This means that any computational service listed in the UI under the [Services Tab](https://docs.osparc.io/#/docs/platform_introduction/services) is accessible from the API. Note that computational services are denoted as *solvers* in the API for convenience, but they refer to the same concept.\n", + "\n", + "\n", + "Let's use the sleepers computational service to illustrate a typical workflow. The sleepers computational service is a very basic service that simply waits (i.e. *sleeps*) a given time before producing some outputs. It takes as input one natural number, an optional text file input that contains another natural number and a boolean in the form of a checkbox. It also provides two outputs: one natural number and a file containing a single natural number." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "398bcd09", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "from pathlib import Path\n", + "from zipfile import ZipFile\n", + "from tempfile import TemporaryDirectory\n", + "\n", + "import osparc\n", + "\n", + "Path(\"file_with_number.txt\").write_text(\"3\")\n", + "\n", + "with osparc.ApiClient() as api_client:\n", + " files_api = osparc.FilesApi(api_client)\n", + " input_file: osparc.File = files_api.upload_file(file=\"file_with_number.txt\")\n", + "\n", + " solver_key: str = \"simcore/services/comp/itis/sleeper\"\n", + " solver_version: str = \"2.1.6\"\n", + "\n", + " solvers_api = osparc.SolversApi(api_client)\n", + " solver: osparc.Solver = solvers_api.get_solver_release(\n", + " solver_key=solver_key, version=solver_version\n", + " )\n", + "\n", + " solver_ports = solvers_api.list_solver_ports(solver.id, solver.version)\n", + " print(f\"solver_ports: {solver_ports}\")\n", + "\n", + " job: osparc.Job = solvers_api.create_job(\n", + " solver.id,\n", + " solver.version,\n", + " osparc.JobInputs(\n", + " {\n", + " \"input_4\": 2,\n", + " \"input_3\": \"false\",\n", + " \"input_2\": 3,\n", + " \"input_1\": input_file,\n", + " }\n", + " ),\n", + " )\n", + "\n", + " status: osparc.JobStatus = solvers_api.start_job(solver.id, solver.version, job.id)\n", + " while not status.stopped_at:\n", + " time.sleep(3)\n", + " status = solvers_api.inspect_job(solver.id, solver.version, job.id)\n", + " print(\"Solver progress\", f\"{status.progress}/100\", flush=True)\n", + " assert status.state == \"SUCCESS\"\n", + "\n", + " #\n", + " # Solver progress 0/100\n", + " # Solver progress 100/100\n", + "\n", + " outputs: osparc.JobOutputs = solvers_api.get_job_outputs(\n", + " solver.id, solver.version, job.id\n", + " )\n", + "\n", + " print(f\"Job {outputs.job_id} got these results:\")\n", + " for output_name, result in outputs.results.items():\n", + " print(output_name, \"=\", result)\n", + "\n", + " #\n", + " # Job 19fc28f7-46fb-4e96-9129-5e924801f088 got these results:\n", + " #\n", + " # output_1 = {'checksum': '859fda0cb82fc4acb4686510a172d9a9-1',\n", + " # 'content_type': 'text/plain',\n", + " # 'filename': 'single_number.txt',\n", + " # 'id': '9fb4f70e-3589-3e9e-991e-3059086c3aae'}\n", + " # output_2 = 4.0\n", + "\n", + " logfile_path: str = solvers_api.get_job_output_logfile(\n", + " solver.id, solver.version, job.id\n", + " )\n", + " zip_path = Path(logfile_path)\n", + "\n", + " with TemporaryDirectory() as tmp_dir:\n", + " with ZipFile(f\"{zip_path}\") as fzip:\n", + " fzip.extractall(tmp_dir)\n", + " logfiles = list(Path(tmp_dir).glob(\"*.log*\"))\n", + " print(\"Unzipped\", logfiles[0], \"contains:\\n\", logfiles[0].read_text())\n", + " #\n", + " # Unzipped extracted/sleeper_2.0.2.logs contains:\n", + " # 2022-06-01T18:15:00.405035847+02:00 Entrypoint for stage production ...\n", + " # 2022-06-01T18:15:00.421279969+02:00 User : uid=0(root) gid=0(root) groups=0(root)\n", + " # 2022-06-01T18:15:00.421560331+02:00 Workdir : /home/scu\n", + " # ...\n", + " # 2022-06-01T18:15:00.864550043+02:00\n", + " # 2022-06-01T18:15:03.923876794+02:00 Will sleep for 3 seconds\n", + " # 2022-06-01T18:15:03.924473521+02:00 [PROGRESS] 1/3...\n", + " # 2022-06-01T18:15:03.925021846+02:00 Remaining sleep time 0.9999995231628418\n", + " # 2022-06-01T18:15:03.925558026+02:00 [PROGRESS] 2/3...\n", + " # 2022-06-01T18:15:03.926103062+02:00 Remaining sleep time 0.9999985694885254\n", + " # 2022-06-01T18:15:03.926643184+02:00 [PROGRESS] 3/3...\n", + " # 2022-06-01T18:15:03.933544384+02:00 Remaining sleep time 0.9999983310699463\n", + "\n", + " download_path: str = files_api.download_file(file_id=outputs.results[\"output_1\"].id)\n", + " print(Path(download_path).read_text())\n", + " #\n", + " # 7" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "c0092d84", + "metadata": {}, + "source": [ + "The script above\n", + "\n", + "1. Uploads a file ``file_with_number.txt``\n", + "2. Selects version ``2.0.2`` of the ``sleeper``\n", + "3. Runs the ``sleeper`` and provides a reference to the uploaded file and other values as input parameters\n", + "4. Monitors the status of the solver while it is running in the platform\n", + "5. When the execution completes, it checks the outputs\n", + "6. The logs are downloaded, unzipped and saved to a new ```extracted``` directory\n", + "7. One of the outputs is a file and it is downloaded\n", + "\n", + "\n", + "#### Files\n", + "\n", + "Files used as input to solvers or produced by solvers in the platform are accessible in the **files** section and specifically with the ``FilesApi`` class.\n", + "In order to use a file as input, it has to be uploaded first and the reference used in the corresponding solver's input." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d6d2dfdb", + "metadata": {}, + "source": [ + "```python\n", + "files_api = FilesApi(api_client)\n", + "input_file: File = files_api.upload_file(file=\"file_with_number.txt\")\n", + "\n", + "\n", + "# ...\n", + "\n", + "\n", + "outputs: JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id)\n", + "results_file: File = outputs.results[\"output_1\"]\n", + "download_path: str = files_api.download_file(file_id=results_file.id)\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e40ad2a5", + "metadata": {}, + "source": [ + "In the snippet above, ``input_file`` is a ``File`` reference to the uploaded file and that is passed as input to the solver. Analogously, ``results_file`` is a ``File`` produced by the solver and that can also be downloaded.\n", + "\n", + "\n", + "#### Solvers, Inputs and Outputs\n", + "\n", + "The inputs and outputs are specific for every solver. Every input/output has a name and an associated type that can be as simple as booleans, numbers, strings ... or more complex as files. You can find this information in the UI under Services Tab, selecting the service card > Information > Raw metadata. For instance, the ``sleeper`` version ``2.0.2`` has the following ``raw-metadata``:" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f5262250", + "metadata": { + "attributes": { + "classes": [ + "json" + ], + "id": "" + } + }, + "source": [ + "```json\n", + "{\n", + " \"inputs\": {\n", + "  \"input_1\": {\n", + "   \"displayOrder\": 1,\n", + "   \"label\": \"File with int number\",\n", + "   \"description\": \"Pick a file containing only one integer\",\n", + "   \"type\": \"data:text/plain\",\n", + "   \"fileToKeyMap\": {\n", + "    \"single_number.txt\": \"input_1\"\n", + "   },\n", + "   \"keyId\": \"input_1\"\n", + "  },\n", + "  \"input_2\": {\n", + "   \"unitLong\": \"second\",\n", + "   \"unitShort\": \"s\",\n", + "   \"label\": \"Sleep interval\",\n", + "   \"description\": \"Choose an amount of time to sleep in range [0-65]\",\n", + "   \"keyId\": \"input_2\",\n", + "   \"displayOrder\": 2,\n", + "   \"type\": \"ref_contentSchema\",\n", + "   \"contentSchema\": {\n", + "    \"title\": \"Sleep interval\",\n", + "    \"type\": \"integer\",\n", + "    \"x_unit\": \"second\",\n", + "    \"minimum\": 0,\n", + "    \"maximum\": 65\n", + "   },\n", + "   \"defaultValue\": 2\n", + "  },\n", + "  \"input_3\": {\n", + "   \"displayOrder\": 3,\n", + "   \"label\": \"Fail after sleep\",\n", + "   \"description\": \"If set to true will cause service to fail after it sleeps\",\n", + "   \"type\": \"boolean\",\n", + "   \"defaultValue\": false,\n", + "   \"keyId\": \"input_3\"\n", + "  },\n", + "  \"input_4\": {\n", + "   \"unitLong\": \"meter\",\n", + "   \"unitShort\": \"m\",\n", + "   \"label\": \"Distance to bed\",\n", + "   \"description\": \"It will first walk the distance to bed\",\n", + "   \"keyId\": \"input_4\",\n", + "   \"displayOrder\": 4,\n", + "   \"type\": \"ref_contentSchema\",\n", + "   \"contentSchema\": {\n", + "    \"title\": \"Distance to bed\",\n", + "    \"type\": \"integer\",\n", + "    \"x_unit\": \"meter\"\n", + "   },\n", + "   \"defaultValue\": 0\n", + "  }\n", + " }\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "1584bf07", + "metadata": {}, + "source": [ + "So, the inputs can be set as follows" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "5337fcae", + "metadata": {}, + "source": [ + "```python\n", + "# ...\n", + "job: osparc.Job = solvers_api.create_job(\n", + " solver.id,\n", + " solver.version,\n", + " osparc.JobInputs(\n", + " {\n", + " \"input_4\": 2,\n", + " \"input_3\": \"false\",\n", + " \"input_2\": 3,\n", + " \"input_1\": input_file,\n", + " }\n", + " ),\n", + ")\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8dfa35ad", + "metadata": {}, + "source": [ + "And the metadata for the outputs are" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d4222e74", + "metadata": { + "attributes": { + "classes": [ + "json" + ], + "id": "" + } + }, + "source": [ + "```json\n", + "  \"output_1\": {\n", + "   \"displayOrder\": 1,\n", + "   \"label\": \"File containing one random integer\",\n", + "   \"description\": \"Integer is generated in range [1-9]\",\n", + "   \"type\": \"data:text/plain\",\n", + "   \"fileToKeyMap\": {\n", + "    \"single_number.txt\": \"output_1\"\n", + "   },\n", + "   \"keyId\": \"output_1\"\n", + "  },\n", + "  \"output_2\": {\n", + "   \"unitLong\": \"second\",\n", + "   \"unitShort\": \"s\",\n", + "   \"label\": \"Random sleep interval\",\n", + "   \"description\": \"Interval is generated in range [1-9]\",\n", + "   \"keyId\": \"output_2\",\n", + "   \"displayOrder\": 2,\n", + "   \"type\": \"ref_contentSchema\",\n", + "   \"contentSchema\": {\n", + "    \"title\": \"Random sleep interval\",\n", + "    \"type\": \"integer\",\n", + "    \"x_unit\": \"second\"\n", + "   }\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "97e73630", + "metadata": {}, + "source": [ + "so this information determines which output corresponds to a number or a file in the following snippet" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "36e8f03b", + "metadata": {}, + "source": [ + "```python\n", + "# ...\n", + "\n", + "outputs: JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id)\n", + "\n", + "output_file = outputs.results[\"output_1\"]\n", + "number = outputs.results[\"output_2\"]\n", + "\n", + "assert status.state == \"SUCCESS\"\n", + "\n", + "\n", + "assert isinstance(output_file, File)\n", + "assert isinstance(number, float)\n", + "\n", + "# output file exists\n", + "assert files_api.get_file(output_file.id) == output_file\n", + "\n", + "# can download and open\n", + "download_path: str = files_api.download_file(file_id=output_file.id)\n", + "assert float(Path(download_path).read_text()), \"contains a random number\"\n", + "\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "a58035b0", + "metadata": {}, + "source": [ + "#### Job Status\n", + "\n", + "Once the client script triggers the solver, the solver runs in the platform and the script is freed. Sometimes, it is convenient to monitor the status of the run to see e.g. the progress of the execution or if the run was completed.\n", + "\n", + "A solver runs in a plaforma starts a ``Job``. Using the ``solvers_api``, allows us to inspect the ``Job`` and get a ``JobStatus`` with information about its status. For instance" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "93817d1e", + "metadata": {}, + "source": [ + "```python \n", + " status: JobStatus = solvers_api.start_job(solver.id, solver.version, job.id)\n", + " while not status.stopped_at:\n", + " time.sleep(3)\n", + " status = solvers_api.inspect_job(solver.id, solver.version, job.id)\n", + " print(\"Solver progress\", f\"{status.progress}/100\", flush=True)\n", + "``` " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "5180b589", + "metadata": {}, + "source": [ + "#### Logs\n", + "\n", + "When a solver runs, it will generate logs during execution which are then saved as .log files. Starting from the osparc Python Client version 0.5.0, The ``solvers_api`` also allows us to obtain the ``logfile_path`` associated with a particular ``Job``. This is a zip file that can then be extracted and saved. For instance" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "417d4663", + "metadata": {}, + "source": [ + "```python\n", + "logfile_path: str = solvers_api.get_job_output_logfile(\n", + " solver.id, solver.version, job.id\n", + ")\n", + "zip_path = Path(logfile_path)\n", + "\n", + "extract_dir = Path(\"./extracted\")\n", + "extract_dir.mkdir()\n", + "\n", + "with ZipFile(f\"{zip_path}\") as fzip:\n", + " fzip.extractall(f\"{extract_dir}\")\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "72d60050", + "metadata": {}, + "source": [ + "## References\n", + "\n", + "- [osparc API python client] documentation\n", + "- [osparc API] documentation\n", + "- A full script with this tutorial: [``sleeper.py``](https://github.com/ITISFoundation/osparc-simcore/blob/master/tests/public-api/examples/sleeper.py)\n", + "\n", + "[osparc API python client]:https://itisfoundation.github.io/osparc-simcore-clients/#/\n", + "[osparc API]:https://api.osparc.io/dev/doc#/" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "", + "language": "python", + "name": "" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/clients/python/docs/WalletTutorial_v0.8.0.ipynb b/clients/python/docs/WalletTutorial_v0.8.0.ipynb new file mode 100644 index 00000000..de8144f5 --- /dev/null +++ b/clients/python/docs/WalletTutorial_v0.8.0.ipynb @@ -0,0 +1,146 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "f15de720", + "metadata": {}, + "source": [ + "# Wallet Tutorial\n", + "\n", + "\n", + "## Check osparc version\n", + "Sanity check that the installed version of `osparc` supports accessing the wallets and credits api." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09653e21", + "metadata": { + "attributes": { + "classes": [ + "command" + ], + "id": "" + } + }, + "outputs": [], + "source": [ + "import osparc\n", + "from packaging.version import Version\n", + "\n", + "assert Version(osparc.__version__) >= Version(\n", + " \"0.8.3.post0.dev10\"\n", + "), \"This tutorial requires a new version of osparc\"" + ] + }, + { + "cell_type": "markdown", + "id": "6b6fab32", + "metadata": {}, + "source": [ + "N.B. This tutorial assumes you have your environment variables `OSPARC_API_HOST`, `OSPARC_API_KEY` and `OSPARC_API_SECRET` setup as explained in the basic tutorial." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "630c5926", + "metadata": {}, + "source": [ + "## Wallets api\n", + "\n", + "To access the *wallets* section of oSPARC's public api one uses the `osparc` Python package's `WalletsApi`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29337833", + "metadata": {}, + "outputs": [], + "source": [ + "from osparc import ApiClient, WalletsApi\n", + "\n", + "with ApiClient() as api_client:\n", + " wallets_api = WalletsApi(api_client)\n", + "\n", + " default_wallet = wallets_api.get_default_wallet()\n", + " print(default_wallet)\n", + "\n", + " wallet = wallets_api.get_wallet(wallet_id=default_wallet.wallet_id)\n", + " print(wallet)\n", + "\n", + "## Example output\n", + "# {'available_credits': 10,\n", + "# 'created': datetime.datetime(2020, 3, 14, 9, 29, 46, 795994, tzinfo=tzutc()),\n", + "# 'description': 'Credits purchased by Mr. Miyagi end up in here',\n", + "# 'modified': datetime.datetime(2020, 3, 14, 9, 29, 46, 795994, tzinfo=tzutc()),\n", + "# 'name': 'Miyagi Credits',\n", + "# 'owner': 1,\n", + "# 'status': 'ACTIVE',\n", + "# 'thumbnail': None,\n", + "# 'wallet_id': 18}\n", + "# {'available_credits': 10,\n", + "# 'created': datetime.datetime(2020, 3, 14, 9, 29, 46, 795994, tzinfo=tzutc()),\n", + "# 'description': 'Credits purchased by Mr. Miyagi end up in here',\n", + "# 'modified': datetime.datetime(2020, 3, 14, 9, 29, 46, 795994, tzinfo=tzutc()),\n", + "# 'name': 'Miyagi Credits',\n", + "# 'owner': 1,\n", + "# 'status': 'ACTIVE',\n", + "# 'thumbnail': None,\n", + "# 'wallet_id': 18}" + ] + }, + { + "cell_type": "markdown", + "id": "dbee3271", + "metadata": {}, + "source": [ + "## Credits api\n", + "\n", + "To access the *credits* sections of oSPARC's public api one uses the `osparc` Python package's `CreditsApi`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c05491f6", + "metadata": {}, + "outputs": [], + "source": [ + "from osparc import ApiClient, CreditsApi\n", + "\n", + "with ApiClient() as api_client:\n", + " credits_api = CreditsApi(api_client=api_client)\n", + " credits_price = credits_api.get_credits_price()\n", + " print(credits_price)\n", + "\n", + "## Example output\n", + "# {'min_payment_amount_usd': 10, 'product_name': 's4l', 'usd_per_credit': 0.1}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "", + "language": "python", + "name": "" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/clients/python/docs/v0.5.0/README.md b/clients/python/docs/v0.5.0/README.md index e02cdc9c..f250c31a 100644 --- a/clients/python/docs/v0.5.0/README.md +++ b/clients/python/docs/v0.5.0/README.md @@ -5,4 +5,4 @@ ## Tutorials -- [Basic tutorial](clients/python/artifacts/docs/BasicTutorial_v0.5.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.5.0.ipynb ":ignore title")) +- [How to run a job in oSPARC](clients/python/artifacts/docs/BasicTutorial_v0.5.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.5.0.ipynb ":ignore title")) diff --git a/clients/python/docs/v0.6.0/README.md b/clients/python/docs/v0.6.0/README.md index a81e2dea..5a1d2ca9 100644 --- a/clients/python/docs/v0.6.0/README.md +++ b/clients/python/docs/v0.6.0/README.md @@ -5,4 +5,4 @@ ## Tutorials -- [Basic tutorial](clients/python/artifacts/docs/BasicTutorial_v0.6.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.6.0.ipynb ":ignore title")) +- [How to run a job in oSPARC](clients/python/artifacts/docs/BasicTutorial_v0.6.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.6.0.ipynb ":ignore title")) diff --git a/clients/python/docs/v0.8.0/README.md b/clients/python/docs/v0.8.0/README.md new file mode 100644 index 00000000..0b6b14d2 --- /dev/null +++ b/clients/python/docs/v0.8.0/README.md @@ -0,0 +1,9 @@ +# OSPARC Python package + +- API version: 0.7.0 +- Package version: 0.8.0 + +## Tutorials + +- [How to run a job in oSPARC](clients/python/artifacts/docs/BasicTutorial_v0.8.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.8.0.ipynb ":ignore title")) +- [Access oSPARC wallets](clients/python/artifacts/docs/WalletTutorial_v0.8.0.md) ([Download as WalletTutorial.ipynb](clients/python/docs/WalletTutorial_v0.8.0.ipynb ":ignore title")) diff --git a/clients/python/test/e2e/test_notebooks.py b/clients/python/test/e2e/test_notebooks.py index d27e7216..9f4738fd 100644 --- a/clients/python/test/e2e/test_notebooks.py +++ b/clients/python/test/e2e/test_notebooks.py @@ -22,6 +22,8 @@ MIN_VERSION_REQS: Dict[str, Version] = { "BasicTutorial_v0.5.0.ipynb": Version("0.5.0"), "BasicTutorial_v0.6.0.ipynb": Version("0.6.0"), + "BasicTutorial_v0.8.0.ipynb": Version("0.8.0"), + "WalletTutorial_v0.8.0.ipynb": Version("0.8.3.post0.dev10"), } diff --git a/docs/doc_entrypoint.md b/docs/doc_entrypoint.md index 65c4835f..8d96a4c2 100644 --- a/docs/doc_entrypoint.md +++ b/docs/doc_entrypoint.md @@ -72,6 +72,7 @@ with ApiClient() as api_client: For more in-depth usage, refer to the following resources: - 📚 Documentation + - [Version 0.8](clients/python/docs/v0.8.0/README.md) - [Version 0.6](clients/python/docs/v0.6.0/README.md) - [Version 0.5](clients/python/docs/v0.5.0/README.md) - [Community-Contributed Scripts](https://github.com/topics/osparc-python) diff --git a/docs/navbar.md b/docs/navbar.md index af1b7640..446eaf70 100644 --- a/docs/navbar.md +++ b/docs/navbar.md @@ -2,3 +2,4 @@ * [v0.5.0](clients/python/docs/v0.5.0/README.md) * [v0.6.0](clients/python/docs/v0.6.0/README.md) + * [v0.8.0](clients/python/docs/v0.8.0/README.md)