|
| 1 | +Introduction |
| 2 | +============ |
| 3 | + |
| 4 | +Slivka python client is a convenience wrapper around the popular [requests](https://requests.readthedocs.io/en/latest/) library for Python. |
| 5 | +It is designed to provide an interface for communicating with the slivka REST API using Python. |
| 6 | + |
| 7 | +It includes: |
| 8 | + |
| 9 | +- Programmatic access to slivka API with simple objects |
| 10 | +- Command-line interface |
| 11 | +- Interactive widgets for Jupyter notebooks |
| 12 | + |
| 13 | +Installation |
| 14 | +============ |
| 15 | + |
| 16 | +The easiest way to install the slivka client is through the conda package manager. |
| 17 | +Install it from the *slivka* channel using |
| 18 | + |
| 19 | +``` |
| 20 | +conda install -c slivka slivka-client |
| 21 | +``` |
| 22 | + |
| 23 | +Alternatively, you can install it from sources. Clone the git repository to your |
| 24 | +machine and run |
| 25 | + |
| 26 | +``` |
| 27 | +python setup.py install |
| 28 | +``` |
| 29 | + |
| 30 | +After the installation has completed successfully, you can import slivka_client |
| 31 | +from Python or run `slivka-cli` command line tool. |
| 32 | + |
| 33 | +Usage |
| 34 | +===== |
| 35 | + |
| 36 | +To begin, import `slivka_client` library and create a `SlivkaClient` instance using the server URL. |
| 37 | + |
| 38 | +```python |
| 39 | +import slivka_client |
| 40 | + |
| 41 | +client = slivka_client.SlivkaClient("http://www.example.org/slivka/") |
| 42 | +``` |
| 43 | + |
| 44 | +> [!IMPORTANT] |
| 45 | +> Remember that locations typically end with a slash. Missing trailing slash may result |
| 46 | +> in resources not being located properly. |
| 47 | +
|
| 48 | +In order to verify the server was found successfully, you may query the version of slivka from the server. |
| 49 | + |
| 50 | +```python |
| 51 | +>>> client.version |
| 52 | +Version(client='1.2.1b1', server='0.8.1', API='1.1') |
| 53 | +``` |
| 54 | + |
| 55 | +The version displays the current client version, server version and |
| 56 | +API compatibility version. |
| 57 | + |
| 58 | +If you see an `HTTPError: 404` exception, check the URL the client tries to access and |
| 59 | +make sure the server is available under that address. The error may be caused by a missing |
| 60 | +trailing slash in the URL. |
| 61 | + |
| 62 | +Services |
| 63 | +-------- |
| 64 | + |
| 65 | +A list of all available services can be retrieved using the `services` property of the `SlivkaClient` object. |
| 66 | + |
| 67 | +```python |
| 68 | +>>> client.services |
| 69 | +[Service(...), Service(...), Service(...), ...] |
| 70 | +``` |
| 71 | + |
| 72 | +Each element of that list represents a single service. It contains its id, name, description, parameters, etc. |
| 73 | +The list is retrieved from the server only once and is cached to improve performance on subsequent access to the services. |
| 74 | +If you expect the services list to change on the server side you can force reloading them with the `reload_services()` method. |
| 75 | + |
| 76 | +```python |
| 77 | +client.reload_services() |
| 78 | +``` |
| 79 | + |
| 80 | +For convenience, you can get a particular service by its id using the `get_service(id)` method or |
| 81 | +by a dictionary item access on a client object. |
| 82 | + |
| 83 | +```python |
| 84 | +>>> client.get_service('example') |
| 85 | +Service(id='example', name='Example', ...) |
| 86 | + |
| 87 | +>>> client['example'] |
| 88 | +Service(id='example', name='Example', ...) |
| 89 | +``` |
| 90 | + |
| 91 | +The `Service` objects provide the following read-only properties: |
| 92 | + |
| 93 | +<dl> |
| 94 | +<dt>id</dt> |
| 95 | +<dd>identifier of the service</dd> |
| 96 | + |
| 97 | +<dt>url</dt> |
| 98 | +<dd>location of the service resource</dd> |
| 99 | + |
| 100 | +<dt>name</dt> |
| 101 | +<dd>name of the service</dd> |
| 102 | + |
| 103 | +<dt>description</dt> |
| 104 | +<dd>long description of the service</dd> |
| 105 | + |
| 106 | +<dt>author</dt> |
| 107 | +<dd>one or more authors of the service</dd> |
| 108 | + |
| 109 | +<dt>version</dt> |
| 110 | +<dd>version of the service</dd> |
| 111 | + |
| 112 | +<dt>license</dt> |
| 113 | +<dd>license of the service<dd> |
| 114 | + |
| 115 | +<dt>classifiers</dt> |
| 116 | +<dd>list of classifiers, tags or categories that help identify or group services</dd> |
| 117 | + |
| 118 | +<dt>parameters</dt> |
| 119 | +<dd>list of service parameters represented as <code>Parameter</code> objects (explained below)</dd> |
| 120 | + |
| 121 | +<dt>presets</dt> |
| 122 | +<dd> |
| 123 | +list of parameter presets offered for this service |
| 124 | + |
| 125 | +<dl> |
| 126 | +<dt>Preset.id</dt> |
| 127 | +<dd>preset identifier</dd> |
| 128 | +<dt>Preset.name</dt> |
| 129 | +<dd>name of the preset</dd> |
| 130 | +<dt>Preset.description</dt> |
| 131 | +<dd>long description of the preset</dd> |
| 132 | +<dt>Preset.values</dt> |
| 133 | +<dd>dictionary of parameter value <code>Dict[str, Any]</code></dd> |
| 134 | +</dl> |
| 135 | +</dd> |
| 136 | + |
| 137 | +<dt>status</dt> |
| 138 | +<dd> |
| 139 | +service operation status |
| 140 | +<dl> |
| 141 | +<dt>Status.status</dt> |
| 142 | +<dd>status name; one of: <em>OK</em>, <em>WARNING</em>, <em>DOWN</em></dd> |
| 143 | +<dt>Status.message</dt> |
| 144 | +<dd>error message</dd> |
| 145 | +<dt>Status.timestamp</dt> |
| 146 | +<dd>the time the status was last updated</dd> |
| 147 | +</dl> |
| 148 | +</dd> |
| 149 | +</dl> |
| 150 | + |
| 151 | +### Parameters |
| 152 | + |
| 153 | +Each object in the *parameters* list describes a parameter that can be provided |
| 154 | +for the service. For each parameter present in the list, you can provide one |
| 155 | +(sometimes multiple) value when submitting the job. |
| 156 | +All parameter objects offer the following read-only properties: |
| 157 | + |
| 158 | +<dl> |
| 159 | +<dt>id</dt> |
| 160 | +<dd>parameter identifier; use it as a key in the data dictionary when submitting jobs.</dd> |
| 161 | +<dt>type</dt> |
| 162 | +<dd> |
| 163 | +parameter type; one of: <em>integer</em>, <em>decimal</em>, <em>text</em>, <em>flag</em>, <em>choice</em>, <em>file</em>, <em>undefined</em>, <em>unknown</em> |
| 164 | +</dd> |
| 165 | +<dt>name</dt> |
| 166 | +<dd>human-friendly name of the parameter</dd> |
| 167 | +<dt>description</dt> |
| 168 | +<dd>longer description of the parameter</dd> |
| 169 | +<dt>required</dt> |
| 170 | +<dd>if the parameter is required</dd> |
| 171 | +<dt>array</dt> |
| 172 | +<dd>if multiple values are allowed for the parameter</dd> |
| 173 | +<dt>default</dt> |
| 174 | +<dd>default value for the parameter that is used if none is provided</dd> |
| 175 | +</dl> |
| 176 | + |
| 177 | +More specialised data structures exist for each type and provide additional properties |
| 178 | +specific to that type. |
| 179 | + |
| 180 | +Starting jobs |
| 181 | +------------- |
| 182 | + |
| 183 | +New jobs are submitted to the server using `submit_job()` method of the *Service* |
| 184 | +object. The method takes two arguments *data* and *files* which are both |
| 185 | +dictionaries containing parameter ids and corresponding values. |
| 186 | +The values are properly encoded and sent as a POST request to the server. |
| 187 | +Values for the parameters that require a file as an input need to be provided |
| 188 | +through the *files* argument to ensure the HTTP request includes the contents |
| 189 | +of the file. |
| 190 | + |
| 191 | +Values provided in the *data* dictionary are converted to strings automatically. |
| 192 | +Values provided in the *files* dictionary can be open files or streams, |
| 193 | +in which case the content of the stream is sent to the server, or bytes, which are sent directly. |
| 194 | + |
| 195 | +> [!IMPORTANT] |
| 196 | +> Files and streams should be opened in binary mode to avoid potential issues with |
| 197 | +> non-ascii characters. |
| 198 | +
|
| 199 | +On successful job submission the method returns a *Job* object containing |
| 200 | +submission data. This object is used to check job status and retrieve |
| 201 | +results. |
| 202 | +If the input parameter are not valid, the method throws a *SubmissionError* |
| 203 | +containing a list of errors encountered during input processing. |
| 204 | + |
| 205 | +Example: |
| 206 | + |
| 207 | +```python |
| 208 | +>>> service = client['example'] |
| 209 | +>>> job = service.submit_job( |
| 210 | +... data={ |
| 211 | +... 'param0': 13, |
| 212 | +... 'param1': 'foobar' |
| 213 | +... }, |
| 214 | +... files={ |
| 215 | +... 'input0': open("input-file.txt", "rb"), |
| 216 | +... 'input1': b"data data data data\n" |
| 217 | +... } |
| 218 | +... ) |
| 219 | +``` |
| 220 | + |
| 221 | +Polling jobs and retrieving results |
| 222 | +----------------------------------- |
| 223 | + |
| 224 | +The *Job* object returned by the `submit_job()` method provides the capability |
| 225 | +to inspect submission data and poll the status of the submitted job and |
| 226 | +retrieve result files from the server. |
| 227 | + |
| 228 | +The 'id' property of the job object holds a server-generated identifier, uniquely denoting the job within the server's context. |
| 229 | +The job additionally holds *service*, *parameters* and *submission_time* information. |
| 230 | + |
| 231 | +The current job status can be obtained using a *status* property which gets updated |
| 232 | +from the server every time it is accessed but no more frequently than once every five seconds. |
| 233 | + |
| 234 | +The result files can be accessed with a *results* property. Just like *status*, the *results* |
| 235 | +is updated from the server every time it is accessed. It returns a list of *slivka_client.File* |
| 236 | +objects which can be used to inspect file metadata and download their content. |
| 237 | + |
| 238 | +> [!NOTE] |
| 239 | +> If you lose the *Job* object either by deleting a variable or restarting |
| 240 | +> the Python interpreter, you can re-create that object using *Client.get_job()* |
| 241 | +> method providing it with the job id. |
| 242 | +
|
| 243 | +### File |
| 244 | + |
| 245 | +The file objects you receive from the slivka client are not actual files you could |
| 246 | +read from. Instead, they are referencing the resources located on the server and can |
| 247 | +be dumped to actual files or streams instead using `dump()` method. |
| 248 | +The argument to the `dump()` method can be a stream or a file opened in either |
| 249 | +text or binary mode, or a file name. In case of streams or files, the result |
| 250 | +is downloaded from the server and appended to the stream. If a file name is |
| 251 | +provided, a new file is created, replacing the existing one if exists, and |
| 252 | +the content is written to that file. |
| 253 | + |
| 254 | +Example: |
| 255 | + |
| 256 | +```python |
| 257 | +>>> result = job.results[0] |
| 258 | + |
| 259 | +>>> # dumping to open file |
| 260 | +>>> result.dump(open("out.txt", "w")) |
| 261 | + |
| 262 | +>>> # dumping to in-memory stream |
| 263 | +>>> input_stream = io.BytesIO() |
| 264 | +>>> result.dump(input_stream) |
| 265 | + |
| 266 | +>>> # creating new file |
| 267 | +>>> result.dump("out.txt") |
| 268 | +``` |
| 269 | + |
| 270 | +Additionally, the *File* object provides the following properties: |
| 271 | + |
| 272 | +| Property | Description | |
| 273 | +|---------------|------------------------------------------------------------------| |
| 274 | +| *url* | Location of the resource | |
| 275 | +| *content_url* | Location of the file content | |
| 276 | +| *id* | Identifier of the file which can be used as input for other jobs | |
| 277 | +| *job_id* | Id of the job this file is a result of | |
| 278 | +| *path* | Real path and name of the file | |
| 279 | +| *label* | Name describing the file | |
| 280 | +| *media_type* | Media type of the file | |
| 281 | + |
0 commit comments