Skip to content

Commit 3edac60

Browse files
authored
doc: basic tutorial (#26)
* Basic tutorial * new reference * @KZzizzle review: adds suggestions and fixes links * Adapted to version 2.0.2 * Minor doc in opeanpi specs
1 parent fa41510 commit 3edac60

File tree

3 files changed

+318
-37
lines changed

3 files changed

+318
-37
lines changed

api/openapi.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@
528528
"solvers"
529529
],
530530
"summary": "List Jobs",
531-
"description": "List of all jobs in a specific released solver ",
531+
"description": "List of all jobs in a specific released solver",
532532
"operationId": "list_jobs",
533533
"parameters": [
534534
{
@@ -656,7 +656,7 @@
656656
"solvers"
657657
],
658658
"summary": "Get Job",
659-
"description": "Gets job of a given solver ",
659+
"description": "Gets job of a given solver",
660660
"operationId": "get_job",
661661
"parameters": [
662662
{

docs/README.md

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -80,52 +80,56 @@ with osparc.ApiClient(cfg) as api_client:
8080

8181
```
8282

83+
## Tutorials
84+
85+
- [Basic tutorial](md/tutorials/BasicTutorial.md)
86+
8387

8488
## Documentation for API Endpoints
8589

8690
All URIs are relative to *https://api.osparc.io*
8791

8892
Class | Method | HTTP request | Description
8993
-------------|-----------------------------------------------------------------------|----------------------------------------------------------------------------|-------------------------------
90-
*FilesApi* | [**download_file**](docs/FilesApi.md#download_file) | **GET** /v0/files/{file_id}/content | Download File
91-
*FilesApi* | [**get_file**](docs/FilesApi.md#get_file) | **GET** /v0/files/{file_id} | Get File
92-
*FilesApi* | [**list_files**](docs/FilesApi.md#list_files) | **GET** /v0/files | List Files
93-
*FilesApi* | [**upload_file**](docs/FilesApi.md#upload_file) | **PUT** /v0/files/content | Upload File
94-
*MetaApi* | [**get_service_metadata**](docs/MetaApi.md#get_service_metadata) | **GET** /v0/meta | Get Service Metadata
95-
*SolversApi* | [**create_job**](docs/SolversApi.md#create_job) | **POST** /v0/solvers/{solver_key}/releases/{version}/jobs | Create Job
96-
*SolversApi* | [**get_job**](docs/SolversApi.md#get_job) | **GET** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id} | Get Job
97-
*SolversApi* | [**get_job_outputs**](docs/SolversApi.md#get_job_outputs) | **GET** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}/outputs | Get Job Outputs
98-
*SolversApi* | [**get_solver**](docs/SolversApi.md#get_solver) | **GET** /v0/solvers/{solver_key}/latest | Get Latest Release of a Solver
99-
*SolversApi* | [**get_solver_release**](docs/SolversApi.md#get_solver_release) | **GET** /v0/solvers/{solver_key}/releases/{version} | Get Solver Release
100-
*SolversApi* | [**inspect_job**](docs/SolversApi.md#inspect_job) | **POST** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}:inspect | Inspect Job
101-
*SolversApi* | [**list_jobs**](docs/SolversApi.md#list_jobs) | **GET** /v0/solvers/{solver_key}/releases/{version}/jobs | List Jobs
102-
*SolversApi* | [**list_solver_releases**](docs/SolversApi.md#list_solver_releases) | **GET** /v0/solvers/{solver_key}/releases | List Solver Releases
103-
*SolversApi* | [**list_solvers**](docs/SolversApi.md#list_solvers) | **GET** /v0/solvers | List Solvers
104-
*SolversApi* | [**list_solvers_releases**](docs/SolversApi.md#list_solvers_releases) | **GET** /v0/solvers/releases | Lists All Releases
105-
*SolversApi* | [**start_job**](docs/SolversApi.md#start_job) | **POST** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}:start | Start Job
106-
*SolversApi* | [**stop_job**](docs/SolversApi.md#stop_job) | **POST** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}:stop | Stop Job
107-
*UsersApi* | [**get_my_profile**](docs/UsersApi.md#get_my_profile) | **GET** /v0/me | Get My Profile
108-
*UsersApi* | [**update_my_profile**](docs/UsersApi.md#update_my_profile) | **PUT** /v0/me | Update My Profile
94+
*FilesApi* | [**download_file**](md/FilesApi.md#download_file) | **GET** /v0/files/{file_id}/content | Download File
95+
*FilesApi* | [**get_file**](md/FilesApi.md#get_file) | **GET** /v0/files/{file_id} | Get File
96+
*FilesApi* | [**list_files**](md/FilesApi.md#list_files) | **GET** /v0/files | List Files
97+
*FilesApi* | [**upload_file**](md/FilesApi.md#upload_file) | **PUT** /v0/files/content | Upload File
98+
*MetaApi* | [**get_service_metadata**](md/MetaApi.md#get_service_metadata) | **GET** /v0/meta | Get Service Metadata
99+
*SolversApi* | [**create_job**](md/SolversApi.md#create_job) | **POST** /v0/solvers/{solver_key}/releases/{version}/jobs | Create Job
100+
*SolversApi* | [**get_job**](md/SolversApi.md#get_job) | **GET** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id} | Get Job
101+
*SolversApi* | [**get_job_outputs**](md/SolversApi.md#get_job_outputs) | **GET** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}/outputs | Get Job Outputs
102+
*SolversApi* | [**get_solver**](md/SolversApi.md#get_solver) | **GET** /v0/solvers/{solver_key}/latest | Get Latest Release of a Solver
103+
*SolversApi* | [**get_solver_release**](md/SolversApi.md#get_solver_release) | **GET** /v0/solvers/{solver_key}/releases/{version} | Get Solver Release
104+
*SolversApi* | [**inspect_job**](md/SolversApi.md#inspect_job) | **POST** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}:inspect | Inspect Job
105+
*SolversApi* | [**list_jobs**](md/SolversApi.md#list_jobs) | **GET** /v0/solvers/{solver_key}/releases/{version}/jobs | List Jobs
106+
*SolversApi* | [**list_solver_releases**](md/SolversApi.md#list_solver_releases) | **GET** /v0/solvers/{solver_key}/releases | List Solver Releases
107+
*SolversApi* | [**list_solvers**](md/SolversApi.md#list_solvers) | **GET** /v0/solvers | List Solvers
108+
*SolversApi* | [**list_solvers_releases**](md/SolversApi.md#list_solvers_releases) | **GET** /v0/solvers/releases | Lists All Releases
109+
*SolversApi* | [**start_job**](md/SolversApi.md#start_job) | **POST** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}:start | Start Job
110+
*SolversApi* | [**stop_job**](md/SolversApi.md#stop_job) | **POST** /v0/solvers/{solver_key}/releases/{version}/jobs/{job_id}:stop | Stop Job
111+
*UsersApi* | [**get_my_profile**](md/UsersApi.md#get_my_profile) | **GET** /v0/me | Get My Profile
112+
*UsersApi* | [**update_my_profile**](md/UsersApi.md#update_my_profile) | **PUT** /v0/me | Update My Profile
109113

110114

111115
## Documentation For Models
112116

113-
- [BodyUploadFileV0FilesContentPut](docs/BodyUploadFileV0FilesContentPut.md)
114-
- [File](docs/File.md)
115-
- [Groups](docs/Groups.md)
116-
- [HTTPValidationError](docs/HTTPValidationError.md)
117-
- [Job](docs/Job.md)
118-
- [JobInputs](docs/JobInputs.md)
119-
- [JobOutputs](docs/JobOutputs.md)
120-
- [JobStatus](docs/JobStatus.md)
121-
- [Meta](docs/Meta.md)
122-
- [Profile](docs/Profile.md)
123-
- [ProfileUpdate](docs/ProfileUpdate.md)
124-
- [Solver](docs/Solver.md)
125-
- [TaskStates](docs/TaskStates.md)
126-
- [UserRoleEnum](docs/UserRoleEnum.md)
127-
- [UsersGroup](docs/UsersGroup.md)
128-
- [ValidationError](docs/ValidationError.md)
117+
- [BodyUploadFileV0FilesContentPut](md/BodyUploadFileV0FilesContentPut.md)
118+
- [File](md/File.md)
119+
- [Groups](md/Groups.md)
120+
- [HTTPValidationError](md/HTTPValidationError.md)
121+
- [Job](md/Job.md)
122+
- [JobInputs](md/JobInputs.md)
123+
- [JobOutputs](md/JobOutputs.md)
124+
- [JobStatus](md/JobStatus.md)
125+
- [Meta](md/Meta.md)
126+
- [Profile](md/Profile.md)
127+
- [ProfileUpdate](md/ProfileUpdate.md)
128+
- [Solver](md/Solver.md)
129+
- [TaskStates](md/TaskStates.md)
130+
- [UserRoleEnum](md/UserRoleEnum.md)
131+
- [UsersGroup](md/UsersGroup.md)
132+
- [ValidationError](md/ValidationError.md)
129133

130134

131135
## Documentation For Authorization

docs/md/tutorials/BasicTutorial.md

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
# Basic Tutorial
2+
3+
4+
5+
## Installation
6+
Install the python client and check the installation as follows:
7+
8+
```command
9+
$ pip install osparc
10+
$ python -c "import osparc; print(osparc.__version__)"
11+
```
12+
13+
14+
## Setup
15+
16+
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](http://docs.osparc.io/#/docs/platform_introduction/main_window_and_navigation/user_setup/profile?id=preferences). 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.
17+
18+
```python
19+
20+
import osparc
21+
22+
cfg = osparc.Configuration(
23+
username=os.environ["OSPARC_API_KEY"],
24+
password=os.environ["OSPARC_API_SECRET"],
25+
)
26+
print(cfg.host)
27+
28+
```
29+
30+
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
31+
32+
33+
The functions in the [osparc API] are grouped into sections such as *meta*, *users*, *files* or *solvers*. Each section address a different resource of the platform.
34+
35+
36+
37+
For example, the *users* section includes functions about the user (i.e. you) and can be accessed initializing a ``UsersApi``:
38+
39+
```python
40+
import osparc
41+
from osparc.api import UsersApi
42+
43+
with osparc.ApiClient(cfg) as api_client:
44+
45+
users_api = UsersApi(api_client)
46+
47+
profile = users_api.get_my_profile()
48+
print(profile)
49+
50+
#
51+
# {'first_name': 'foo',
52+
# 'gravatar_id': 'aa33fssec77ea434c2ea4fb92d0fd379e',
53+
# 'groups': {'all': {'description': 'all users',
54+
# 'gid': '1',
55+
# 'label': 'Everyone'},
56+
# 'me': {'description': 'primary group',
57+
# 'gid': '2',
58+
# 'label': 'foo'},
59+
# 'organizations': []},
60+
# 'last_name': '',
61+
# 'login': '[email protected]',
62+
# 'role': 'USER'}
63+
#
64+
```
65+
66+
67+
## Solvers Workflow
68+
69+
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 [Discover Tab](http://docs.osparc.io/#/docs/platform_introduction/core_elements/Discover?id=discover-tab) 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.
70+
71+
72+
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.
73+
74+
75+
```python
76+
import osparc
77+
78+
from osparc.api import FilesApi, SolversApi
79+
from osparc.models import File, Job, JobInputs, JobOutputs, JobStatus, Solver
80+
81+
82+
with osparc.ApiClient(cfg) as api_client:
83+
84+
files_api = FilesApi(api_client)
85+
input_file: File = files_api.upload_file(file="file_with_number.txt")
86+
87+
solvers_api = SolversApi(api_client)
88+
solver: Solver = solvers_api.get_solver_release(
89+
"simcore/services/comp/itis/sleeper", "2.0.2"
90+
)
91+
92+
job: Job = solvers_api.create_job(
93+
solver.id,
94+
solver.version,
95+
JobInputs(
96+
{
97+
"input_3": 0,
98+
"input_2": 3.0,
99+
"input_1": input_file,
100+
}
101+
),
102+
)
103+
104+
status: JobStatus = solvers_api.start_job(solver.id, solver.version, job.id)
105+
while not status.stopped_at:
106+
time.sleep(3)
107+
status = solvers_api.inspect_job(solver.id, solver.version, job.id)
108+
print("Solver progress", f"{status.progress}/100", flush=True)
109+
#
110+
# Solver progress 0/100
111+
# Solver progress 100/100
112+
113+
outputs: JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id)
114+
115+
print(f"Job {outputs.job_id} got these results:")
116+
for output_name, result in outputs.results.items():
117+
print(output_name, "=", result)
118+
119+
#
120+
# Job 19fc28f7-46fb-4e96-9129-5e924801f088 got these results:
121+
#
122+
# output_1 = {'checksum': '859fda0cb82fc4acb4686510a172d9a9-1',
123+
# 'content_type': 'text/plain',
124+
# 'filename': 'single_number.txt',
125+
# 'id': '9fb4f70e-3589-3e9e-991e-3059086c3aae'}
126+
# output_2 = 4.0
127+
128+
129+
results_file: File = outputs.results["output_1"]
130+
download_path: str = files_api.download_file(file_id=results_file.id)
131+
print(Path(download_path).read_text())
132+
#
133+
# 7
134+
135+
```
136+
137+
The script above
138+
139+
1. Uploads a file ``file_with_number.txt``
140+
2. Selects version ``2.0.2`` of the ``sleeper``
141+
3. Runs the ``sleeper`` and provides a reference to the uploaded file and other values as input parameters
142+
4. Monitors the status of the solver while it is running in the platform
143+
5. When the execution completes, it checks the outputs
144+
6. One of the outputs is a file and it is downloaded
145+
146+
147+
#### Files
148+
149+
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.
150+
In order to use a file as input, it has to be uploaded first and the reference used in the corresponding solver's input.
151+
152+
153+
```python
154+
files_api = FilesApi(api_client)
155+
input_file: File = files_api.upload_file(file="file_with_number.txt")
156+
157+
158+
# ...
159+
160+
161+
outputs: JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id)
162+
results_file: File = outputs.results["output_1"]
163+
download_path: str = files_api.download_file(file_id=results_file.id)
164+
165+
```
166+
167+
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.
168+
169+
170+
#### Solvers, Inputs and Outputs
171+
172+
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 Discover Tab, selecting the service card > More Info > raw metadata. For instance, the ``sleeper`` version ``2.0.2`` has the following ``raw-metadata``:
173+
174+
```json
175+
{
176+
inputs: {
177+
'input_1': {'description': 'Pick a file containing only one '
178+
'integer',
179+
'displayOrder': 1,
180+
'fileToKeyMap': {'single_number.txt': 'input_1'},
181+
'label': 'File with int number',
182+
'type': 'data:text/plain'},
183+
'input_2': {'defaultValue': 2,
184+
'description': 'Choose an amount of time to sleep',
185+
'displayOrder': 2,
186+
'label': 'Sleep interval',
187+
'type': 'integer',
188+
'unit': 'second'},
189+
'input_3': {'defaultValue': False,
190+
'description': 'If set to true will cause service to '
191+
'fail after it sleeps',
192+
'displayOrder': 3,
193+
'label': 'Fail after sleep',
194+
'type': 'boolean'},
195+
}
196+
}
197+
```
198+
So, the inputs can be set as follows
199+
200+
```python
201+
# ...
202+
job = solvers_api.create_job(
203+
solver.id,
204+
solver.version,
205+
job_inputs=JobInputs(
206+
{
207+
"input_1": uploaded_input_file,
208+
"input_2": 3 * n, # sleep time in secs
209+
"input_3": bool(n % 2), # fail after sleep?
210+
}
211+
),
212+
)
213+
214+
```
215+
216+
And the metadata for the outputs are
217+
```json
218+
{
219+
'outputs': {'output_1': {'description': 'Integer is generated in range [1-9]',
220+
'displayOrder': 1,
221+
'fileToKeyMap': {'single_number.txt': 'output_1'},
222+
'label': 'File containing one random integer',
223+
'type': 'data:text/plain'},
224+
'output_2': {'description': 'Interval is generated in range '
225+
'[1-9]',
226+
'displayOrder': 2,
227+
'label': 'Random sleep interval',
228+
'type': 'integer',
229+
'unit': 'second'}},
230+
}
231+
```
232+
so this information determines which output corresponds to a number or a file in the following snippet
233+
234+
```python
235+
# ...
236+
237+
outputs: JobOutputs = solvers_api.get_job_outputs(solver.id, solver.version, job.id)
238+
239+
output_file = outputs.results["output_1"]
240+
number = outputs.results["output_2"]
241+
242+
assert status.state == "SUCCESS"
243+
244+
245+
assert isinstance(output_file, File)
246+
assert isinstance(number, float)
247+
248+
# output file exists
249+
assert files_api.get_file(output_file.id) == output_file
250+
251+
# can download and open
252+
download_path: str = files_api.download_file(file_id=output_file.id)
253+
assert float(Path(download_path).read_text()), "contains a random number"
254+
```
255+
256+
#### Job Status
257+
258+
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.
259+
260+
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
261+
262+
```python
263+
status: JobStatus = solvers_api.start_job(solver.id, solver.version, job.id)
264+
while not status.stopped_at:
265+
time.sleep(3)
266+
status = solvers_api.inspect_job(solver.id, solver.version, job.id)
267+
print("Solver progress", f"{status.progress}/100", flush=True)
268+
```
269+
270+
## References
271+
272+
- [osparc API python client] documentation
273+
- [osparc API] documentation
274+
- A full script with this tutorial: [``sleeper.py``](https://github.com/ITISFoundation/osparc-simcore/blob/master/tests/public-api/examples/sleeper.py)
275+
276+
[osparc API python client]:https://itisfoundation.github.io/osparc-simcore-python-client
277+
[osparc API]:https://api.staging.osparc.io/doc

0 commit comments

Comments
 (0)