From e636152950cf588d618014ccd43fe5744c9cb90c Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 10:00:28 +0200 Subject: [PATCH 01/14] fix links --- clients/python/docs/v0.5.0/README.md | 2 +- clients/python/docs/v0.6.0/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/python/docs/v0.5.0/README.md b/clients/python/docs/v0.5.0/README.md index e02cdc9c..161f1699 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")) +- [Basic tutorial](clients/python/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..f5917d11 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")) +- [Basic tutorial](clients/python/docs/BasicTutorial_v0.6.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.6.0.ipynb ":ignore title")) From 66a435c4ccd7084d2bca5bcb03967ea8352815f8 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 10:04:36 +0200 Subject: [PATCH 02/14] add doc for 0.8.0 --- clients/python/docs/v0.8.0/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 clients/python/docs/v0.8.0/README.md 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..a972634d --- /dev/null +++ b/clients/python/docs/v0.8.0/README.md @@ -0,0 +1,8 @@ +# OSPARC Python package + +- API version: 0.5.0 +- Package version: 0.8.0 + +## Tutorials + +- [Basic tutorial](clients/python/docs/BasicTutorial_v0.6.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.6.0.ipynb ":ignore title")) From 9e2a7016d036df5d6ca6a0a7669eb15abc9edda8 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 10:18:18 +0200 Subject: [PATCH 03/14] start adding docs for 0.8.0 --- .../python/docs/BasicTutorial_v0.8.0.ipynb | 575 ++++++++++++++++++ clients/python/docs/v0.5.0/README.md | 2 +- clients/python/docs/v0.6.0/README.md | 2 +- clients/python/docs/v0.8.0/README.md | 2 +- docs/doc_entrypoint.md | 1 + 5 files changed, 579 insertions(+), 3 deletions(-) create mode 100644 clients/python/docs/BasicTutorial_v0.8.0.ipynb 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..929292e3 --- /dev/null +++ b/clients/python/docs/BasicTutorial_v0.8.0.ipynb @@ -0,0 +1,575 @@ +{ + "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. 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." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "85ac5228", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from osparc import Configuration\n", + "\n", + "cfg = Configuration(\n", + " host=os.environ[\"OSPARC_API_HOST\"],\n", + " username=os.environ[\"OSPARC_API_KEY\"],\n", + " password=os.environ[\"OSPARC_API_SECRET\"],\n", + ")\n", + "print(cfg.host)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "630c5926", + "metadata": {}, + "source": [ + "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\n", + "\n", + "\n", + "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.\n", + "\n", + "\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(cfg) 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(cfg) 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.10" + } + }, + "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 161f1699..e02cdc9c 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/docs/BasicTutorial_v0.5.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.5.0.ipynb ":ignore title")) +- [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")) diff --git a/clients/python/docs/v0.6.0/README.md b/clients/python/docs/v0.6.0/README.md index f5917d11..a81e2dea 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/docs/BasicTutorial_v0.6.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.6.0.ipynb ":ignore title")) +- [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")) diff --git a/clients/python/docs/v0.8.0/README.md b/clients/python/docs/v0.8.0/README.md index a972634d..121f33cf 100644 --- a/clients/python/docs/v0.8.0/README.md +++ b/clients/python/docs/v0.8.0/README.md @@ -5,4 +5,4 @@ ## Tutorials -- [Basic tutorial](clients/python/docs/BasicTutorial_v0.6.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.6.0.ipynb ":ignore title")) +- [Basic tutorial](clients/python/artifacts/docs/BasicTutorial_v0.8.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.8.0.ipynb ":ignore title")) 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) From c521e30136f07192b120c87ef2dcabdf7ff5c936 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 11:09:07 +0200 Subject: [PATCH 04/14] automatically instantiate the api-client --- .../python/docs/BasicTutorial_v0.8.0.ipynb | 33 +++---------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/clients/python/docs/BasicTutorial_v0.8.0.ipynb b/clients/python/docs/BasicTutorial_v0.8.0.ipynb index 929292e3..670b7811 100644 --- a/clients/python/docs/BasicTutorial_v0.8.0.ipynb +++ b/clients/python/docs/BasicTutorial_v0.8.0.ipynb @@ -43,25 +43,7 @@ "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. 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." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "85ac5228", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from osparc import Configuration\n", - "\n", - "cfg = Configuration(\n", - " host=os.environ[\"OSPARC_API_HOST\"],\n", - " username=os.environ[\"OSPARC_API_KEY\"],\n", - " password=os.environ[\"OSPARC_API_SECRET\"],\n", - ")\n", - "print(cfg.host)" + "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\"." ] }, { @@ -70,12 +52,7 @@ "id": "630c5926", "metadata": {}, "source": [ - "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\n", - "\n", - "\n", - "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.\n", - "\n", - "\n", + "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``:" ] @@ -89,7 +66,7 @@ "source": [ "from osparc import ApiClient, UsersApi\n", "\n", - "with ApiClient(cfg) as api_client:\n", + "with ApiClient() as api_client:\n", " users_api = UsersApi(api_client)\n", "\n", " profile = users_api.get_my_profile()\n", @@ -141,7 +118,7 @@ "\n", "Path(\"file_with_number.txt\").write_text(\"3\")\n", "\n", - "with osparc.ApiClient(cfg) as api_client:\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", @@ -567,7 +544,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.10" + "version": "3.10.11" } }, "nbformat": 4, From 17b3643fd0f29826c9dbddb4e68ea68e4a2e7187 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 11:19:25 +0200 Subject: [PATCH 05/14] add wallet tutorial --- .../python/docs/WalletTutorial_v0.8.0.ipynb | 113 ++++++++++++++++++ clients/python/docs/v0.8.0/README.md | 1 + 2 files changed, 114 insertions(+) create mode 100644 clients/python/docs/WalletTutorial_v0.8.0.ipynb 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..54780d32 --- /dev/null +++ b/clients/python/docs/WalletTutorial_v0.8.0.ipynb @@ -0,0 +1,113 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "f15de720", + "metadata": {}, + "source": [ + "# Wallet 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", + " #" + ] + } + ], + "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.8.0/README.md b/clients/python/docs/v0.8.0/README.md index 121f33cf..10e99115 100644 --- a/clients/python/docs/v0.8.0/README.md +++ b/clients/python/docs/v0.8.0/README.md @@ -6,3 +6,4 @@ ## Tutorials - [Basic tutorial](clients/python/artifacts/docs/BasicTutorial_v0.8.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.8.0.ipynb ":ignore title")) +- [Wallet tutorial](clients/python/artifacts/docs/WalletTutorial_v0.8.0.md) ([Download as WalletTutorial.ipynb](clients/python/docs/WalletTutorial_v0.8.0.ipynb ":ignore title")) From e92b667e8017869015b370172dded53bc44d206b Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 11:37:32 +0200 Subject: [PATCH 06/14] further improvements --- clients/python/docs/WalletTutorial_v0.8.0.ipynb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/clients/python/docs/WalletTutorial_v0.8.0.ipynb b/clients/python/docs/WalletTutorial_v0.8.0.ipynb index 54780d32..856403d5 100644 --- a/clients/python/docs/WalletTutorial_v0.8.0.ipynb +++ b/clients/python/docs/WalletTutorial_v0.8.0.ipynb @@ -28,11 +28,12 @@ }, "outputs": [], "source": [ - "import importlib\n", + "import osparc\n", + "from packaging.version import Version\n", "\n", - "if importlib.util.find_spec(\"osparc\") is not None:\n", - " ! pip install osparc\n", - "! python -c \"import osparc; print(osparc.__version__)\"" + "assert Version(osparc.__version__) > Version(\n", + " \"0.8.3.post0.dev10\"\n", + "), \"This tutorial requires a new version of osparc\"" ] }, { From 54334a2aa54ede0127aac4882553bc0942c7777d Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 11:51:48 +0200 Subject: [PATCH 07/14] hit wallet endpoints --- .../python/docs/WalletTutorial_v0.8.0.ipynb | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/clients/python/docs/WalletTutorial_v0.8.0.ipynb b/clients/python/docs/WalletTutorial_v0.8.0.ipynb index 856403d5..d7bc20f2 100644 --- a/clients/python/docs/WalletTutorial_v0.8.0.ipynb +++ b/clients/python/docs/WalletTutorial_v0.8.0.ipynb @@ -9,9 +9,8 @@ "# Wallet Tutorial\n", "\n", "\n", - "\n", - "## Installation\n", - "Install the python client and check the installation as follows:" + "## Check osparc version\n", + "Sanity check that the installed version of `osparc` supports accessing the wallets and credits api." ] }, { @@ -31,31 +30,18 @@ "import osparc\n", "from packaging.version import Version\n", "\n", - "assert Version(osparc.__version__) > Version(\n", + "assert Version(osparc.__version__) >= Version(\n", " \"0.8.3.post0.dev10\"\n", "), \"This tutorial requires a new version of osparc\"" ] }, - { - "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``:" + "To access the *wallets* and *credits* sections of oSPARC's public api one uses the `osparc` Python package's `WalletsApi` and `CreditsApi` respectively." ] }, { @@ -65,29 +51,45 @@ "metadata": {}, "outputs": [], "source": [ - "from osparc import ApiClient, UsersApi\n", + "from osparc import ApiClient, WalletsApi\n", "\n", "with ApiClient() as api_client:\n", - " users_api = UsersApi(api_client)\n", + " wallets_api = WalletsApi(api_client)\n", + "\n", + " default_wallet = wallets_api.get_default_wallet()\n", + " print(default_wallet)\n", "\n", - " profile = users_api.get_my_profile()\n", - " print(profile)\n", + " wallet = wallets_api.get_wallet(wallet_id=default_wallet.wallet_id)\n", + " print(wallet)\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", - " #" + "## 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": "code", + "execution_count": null, + "id": "dbee3271", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From 6f53b002ad7f14058dca35797c2f6fef65db452f Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 11:56:20 +0200 Subject: [PATCH 08/14] add credits api tutorial --- .../python/docs/WalletTutorial_v0.8.0.ipynb | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/clients/python/docs/WalletTutorial_v0.8.0.ipynb b/clients/python/docs/WalletTutorial_v0.8.0.ipynb index d7bc20f2..6fcbfaf6 100644 --- a/clients/python/docs/WalletTutorial_v0.8.0.ipynb +++ b/clients/python/docs/WalletTutorial_v0.8.0.ipynb @@ -41,7 +41,9 @@ "id": "630c5926", "metadata": {}, "source": [ - "To access the *wallets* and *credits* sections of oSPARC's public api one uses the `osparc` Python package's `WalletsApi` and `CreditsApi` respectively." + "## Wallets api\n", + "\n", + "To access the *wallets* section of oSPARC's public api one uses the `osparc` Python package's `WalletsApi`." ] }, { @@ -83,13 +85,33 @@ "# '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": "dbee3271", + "id": "c05491f6", "metadata": {}, "outputs": [], - "source": [] + "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": { From 3e4b991bdfc00abc210460635995b91dfa8e1263 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 11:58:27 +0200 Subject: [PATCH 09/14] add disclaimer --- clients/python/docs/WalletTutorial_v0.8.0.ipynb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clients/python/docs/WalletTutorial_v0.8.0.ipynb b/clients/python/docs/WalletTutorial_v0.8.0.ipynb index 6fcbfaf6..de8144f5 100644 --- a/clients/python/docs/WalletTutorial_v0.8.0.ipynb +++ b/clients/python/docs/WalletTutorial_v0.8.0.ipynb @@ -35,6 +35,14 @@ "), \"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", From 366a91d53be423078ae9dfba378d744e2ba2c873 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 12:45:14 +0200 Subject: [PATCH 10/14] remove artifact md files --- clients/python/docs/BasicTutorial_v0.5.0.md | 371 ------------------- clients/python/docs/BasicTutorial_v0.6.0.md | 376 -------------------- 2 files changed, 747 deletions(-) delete mode 100644 clients/python/docs/BasicTutorial_v0.5.0.md delete mode 100644 clients/python/docs/BasicTutorial_v0.6.0.md 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") From 4beb0f98aac2a79a500092af5424856e940ba30b Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 12:49:01 +0200 Subject: [PATCH 11/14] add new tutorials to tests --- clients/python/test/e2e/test_notebooks.py | 2 ++ 1 file changed, 2 insertions(+) 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"), } From 91fd4f8258ffdece36f7d2d60a968b6eb92a20c4 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 13:37:22 +0200 Subject: [PATCH 12/14] ensure uv is installed when generating docs --- .github/workflows/github-pages.yml | 1 + 1 file changed, 1 insertion(+) 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 From cc04cfdab1eff5dbb0bd28e559076468dfe61f64 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 13:45:54 +0200 Subject: [PATCH 13/14] improve text to tutorials --- clients/python/docs/v0.5.0/README.md | 2 +- clients/python/docs/v0.6.0/README.md | 2 +- clients/python/docs/v0.8.0/README.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) 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 index 10e99115..a426faff 100644 --- a/clients/python/docs/v0.8.0/README.md +++ b/clients/python/docs/v0.8.0/README.md @@ -5,5 +5,5 @@ ## Tutorials -- [Basic tutorial](clients/python/artifacts/docs/BasicTutorial_v0.8.0.md) ([Download as BasicTutorial.ipynb](clients/python/docs/BasicTutorial_v0.8.0.ipynb ":ignore title")) -- [Wallet tutorial](clients/python/artifacts/docs/WalletTutorial_v0.8.0.md) ([Download as WalletTutorial.ipynb](clients/python/docs/WalletTutorial_v0.8.0.ipynb ":ignore title")) +- [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")) From acdf13e81bd990341fca8deefd797ffeb22387f4 Mon Sep 17 00:00:00 2001 From: Mads Bisgaard Date: Wed, 23 Oct 2024 13:49:43 +0200 Subject: [PATCH 14/14] small fixes --- clients/python/docs/v0.8.0/README.md | 2 +- docs/navbar.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clients/python/docs/v0.8.0/README.md b/clients/python/docs/v0.8.0/README.md index a426faff..0b6b14d2 100644 --- a/clients/python/docs/v0.8.0/README.md +++ b/clients/python/docs/v0.8.0/README.md @@ -1,6 +1,6 @@ # OSPARC Python package -- API version: 0.5.0 +- API version: 0.7.0 - Package version: 0.8.0 ## Tutorials 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)