{ "info": { "author": "Seraphin Vandegar", "author_email": "svandegar@hotmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.6" ], "description": "# opisense_client\nPython package providing some functions to interact with the Opisense API. \nMore info about Opisense here: https://www.opinum.com/ \nOpisense API is described here: https://docs.opinum.com/restapi/index.html \n\n## Getting started\nThese instructions will guide you through the process of getting you ready to use the opisense_client package\n\n### Prerequisites\n#### 1. Get an Opisense account.\nContact info@opinum.com or see www.opinum.com to get an account\n\n#### 2. Get Opisense API credentials.\nContact support@opinum.com to get an API secret and API key\n\n#### 3. Install opisense_client package\nYou can install opisense_client by typing `pip install opisense-client` in your terminal.\n\n### Configuration\n#### Credentials file\nAlthough you can define a dictonary containing your credentials in you script, it is more secure to store them in a separate file. \nThe file should be a .JSON format and the expected format is the following : \n```json\n{ \n \"user\": {\n \"username\": \"xxxxxxx\",\n \"password\": \"xxxxxxx\"\n },\n \"api\": {\n \"client_secret\": \"xxxxxx\",\n \"client_id\": \"xxxxxx\",\n \"scope\":\"opisense-api push-data\"\n }\n}\n```\nThe `xxxxxx` should be replaced by your own credentials (see Prerequisites 1. and 2.)\n\nThe following code will read the file and store data in a Python dictionary : \n```python\nimport json\nwith open('path/to/your/file/credentials.JSON', encoding='utf-8') as file: \n credentials = json.load(file) \nuser_credentials = credentials['user'] \napi_credentials = credentials['api'] \n```\n`path/to/your/file/` is the local path to your credentials file and `credentials.JSON` is the filename. \n\n### Functionalities\n#### objects \n* **ApiFilter**: \nThis object contains the API path and a dictionary containing all the filters names and values used to querry Opisense database via an http call to the API. This call is described under [http](#http) section. \nThis is only used is input for the `http.GET()` method.\nSee [Opisense API documentation](https://docs.opinum.com/restapi/index.html) for details on the path and filters availables for each type of Opisense Entities. \nThe `__add__` method of this class allows adding filters names and values to the existing ApiFilter object. \n\n* **DataPoints**:\nThis object contains pairs of datapoints datetimes and values to be used in the [StandardData](#StandardData) object construction. \nThe `__add__` method of this class allows adding datapoints datetimes and values to the existing ApiFilter object.\n\n* **StandardData**:\nThis object contains a [DataPoints](DataPoints) object and the relevant parameters to identify strictly one Opisense Variable that will store the datapoints. \nA combinaison of these optionals parameters can be used to identify the variable : *sourceId, sourceSerialNumber, meterNumber, sourceEan, mappingConfig*. An other option is to use the internal id of the Opisense variable: *variableId*. \nThe `POST` method of this class takes a bearer token as argument and uploads the StandardData object to Opisense API, using the http POST method. \n\n* **OpisenseObject**:\nThis object is a generic Opisense Object taking a type string and a dictionary containing all the Opisense parameters related to this kind of object. See [Opisense API documentation](https://docs.opinum.com/restapi/index.html) for more details on Opisense Objects. \nThe `POST`, `PUT` and `DELETE` methods of this class take a bearer token as argument and uploads the StandardData object to Opisense API, using the appropriate http method. \nThe `json` method of this function returns the object contain, serialized in JSON, using the built-in [json](https://docs.python.org/3/library/json.html) method.\n\n#### http\n* **authorize**: \nThis function takes user credentials and api credentials dictionaries as input and returns the OAuth2 bearer token required to interract with Opisense API. \nThe optional *feedback* argument returns in the console the Opisense account name and ID for which you got a token.\n \n* **GET**: \nThis function takes a bearer token and an ApiFilter object as inputs, perform an http call using GET method to Opisense API to get every element matching the filter and returns the response from the server. \nThe optionnal *json_output* argument changes the output to the json content of the server response. \nThe optional *feedback* argument returns the server response code in the console.\nThe optionnal *opisense_object* argument changes the output to a list of Opisense Objects created from the json response.\n \n* **POST**: \nThis function takes a bearer token and an OpisenseObject as inputs, creates the elements in Opisense database using the API POST method and returns the response from the server. \nThe *parent_id* argument is necessary to create some type of OpisenseObjects. See [Opisense API documentation](https://docs.opinum.com/restapi/index.html) for details. \nThe optional *feedback* argument returns the server response code in the console. \nThe optional *path* argument allows to change the default path appended to the url in the POST call.\nThe optional *force_path* argument allows to use a path not in the default list.\n \n* **PUT**: \nThis function takes a bearer token and an OpisenseObject as inputs, updates the elements in Opisense database using the API PUT method and returns the response from the server. \nThe *parent_id* argument is necessary to update some type of OpisenseObjects. See [Opisense API documentation](https://docs.opinum.com/restapi/index.html) for details. \nThe optional *feedback* argument returns the server response code in the console.\nThe optional *path* argument allows to change the default path appended to the url in the POST call.\nThe optional *force_path* argument allows to use a path not in the default list. \n \n* **DELETE**: \nThis function takes a bearer token and an OpisenseObject as inputs, deletes the elements in Opisense database using the API DELETE method and returns the response from the server. \nThe optional *feedback* argument returns the server response code in the console. \nThe optional *path* argument allows to change the default path appended to the url in the POST call.\nThe optional *force_path* argument allows to use a path not in the default list.\n\n### Objects and Methods description\nEvery object and method is described with docstrings. You can easily access it by running `help(method)`\n\n## Tests\nThe tests are located in `.\\opisense_client\\tests\\tests.py`. To run the tets, do the following: \n1. Install pytest: `pip install pytest`\n2. From the root folder of this repos, run `pytest .\\opisense_client\\tests\\tests.py -vv`\n\n\n## Changelog\n### 1.4.0:\n#### Fixes:\n- Remove the header \"X-Opisense-Api-Version\": \"1.1\" from http calls, to allow using the last version of the Opisense API.\n- Remove the `path` argument added in v1.3.0 as it was leading to some new errors. `force_path` from `PUT()`,`POST()` and `DELETE()` functions now accepts strings overriding default path. \n\n#### Features:\n- Add getter and setter to OpisenseObjects to allows getting and setting objects attributes by dot notation. (i.e.: source.siteId give the same result as source.content['siteId'])\n- Make `opisense_object` optional for `POST` and `DELETE` functions. Add `id` argument to `DELETE` function to enter the Opisense internal Id to delete.\n\n### 1.3.0:\n#### Fixes: \n- Add a rule to allow `from` and `to` filters in `ApiFilter` without interfering with python `from` reserved word. The arguments `date_from` and `date_to` are automatically converted to `from` and `to` filters in the object initialization. \n- Add a `path` argument and update the `force_path` argument to `PUT()`,`POST()` and `DELETE()` functions, to allow the usage of non-standard paths. \n\n### 1.2.1:\n#### Fixes: \nFix import problem\n\n### 1.2.0:\n#### Features: \nAdd `opisense_object` option to `http.GET()` function. This option changes the output to a list of Opisense Objects created from the json response. \n\n### 1.1.2:\n#### Fixes: \nFix StandardData object bug, using mapping_config instead of variableId to build the object. \n\n\n### 1.1.0: \n#### Features: \nAdd datapoints_list as optional input for DataPoints Objects. Allows to create a DataPoints object from a list of dictionaries `[{'date':datetime,'value':float}]`\n\n### 1.0.2: \n#### Fix auth bug linked to oauth lib library update\nset `auth = None` in `oauth.fetch_token()` in http.py to avoid getting an 'invalid_client' error\n\n### 1.0.0: \n#### First stable version\n#### StandardData\nAdded variableId as unique identifier option\n\n#### Documentation\nUpdated README.MD and documented every object and method\n\n### 0.3:\n#### force_path \nAdded force_path optional parameter to http.POST and http.PUT. \nOverwrites the default OpisenseObject.api_path in the http call.\n\n#### json_output\nAdded json_output optional parameter to http.GET\nIf True, Returns the JSON object from the http response if available.", "description_content_type": "text/markdown", "docs_url": null, "download_url": "https://github.com/opinum/opisense_client/archive/1.2.0.tar.gz", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/opinum/opisense_client", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "opisense-client", "package_url": "https://pypi.org/project/opisense-client/", "platform": "", "project_url": "https://pypi.org/project/opisense-client/", "project_urls": { "Download": "https://github.com/opinum/opisense_client/archive/1.2.0.tar.gz", "Homepage": "https://github.com/opinum/opisense_client" }, "release_url": "https://pypi.org/project/opisense-client/1.4.0/", "requires_dist": null, "requires_python": "", "summary": "Package to interact with the Opisense API", "version": "1.4.0" }, "last_serial": 5663229, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "56f6d31f7b578aefb501463d0653c3d7", "sha256": "0ce902a02ffd3628ac043c889c9e0d262bba864cc2fd3a87d54908d2f7554063" }, "downloads": -1, "filename": "opisense_client-0.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "56f6d31f7b578aefb501463d0653c3d7", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 3481, "upload_time": "2018-12-27T23:44:26", "url": "https://files.pythonhosted.org/packages/02/ba/242c645459e2ff7042c1afc722e76019697a41550e5244d5f6849a7abba4/opisense_client-0.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8305c3b823cf702ccf4dea5bdcd5a37e", "sha256": "012a3d32b852b717cc292ef785942818402b3dc61918779cc4eb48ff3fb58f71" }, "downloads": -1, "filename": "opisense_client-0.0.1.tar.gz", "has_sig": false, "md5_digest": "8305c3b823cf702ccf4dea5bdcd5a37e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2703, "upload_time": "2018-12-27T23:44:31", "url": "https://files.pythonhosted.org/packages/fe/64/85ce208032991c4b09c3c2390eecf8bf7b1ecc8f1199e8486759e24f2562/opisense_client-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "4051f7da1af652d8206b91646f59b374", "sha256": "a7723dcdf9f7e0f70f79393cac18d82974c6f459f269e9e5090da92a5c0879cc" }, "downloads": -1, "filename": "opisense_client-0.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "4051f7da1af652d8206b91646f59b374", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6287, "upload_time": "2018-12-27T23:44:28", "url": "https://files.pythonhosted.org/packages/5b/18/a4563c8679c6b49d582329cd2865070938845af88c550914fd2b5c572697/opisense_client-0.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4bf2ab64aab9bb43be874ca83bb23e2e", "sha256": "fa0e4f0afe9acd14c902c1781cca1c0fcfeabab11097031a873058008cd19518" }, "downloads": -1, "filename": "opisense_client-0.0.2.tar.gz", "has_sig": false, "md5_digest": "4bf2ab64aab9bb43be874ca83bb23e2e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3114, "upload_time": "2018-12-27T23:44:33", "url": "https://files.pythonhosted.org/packages/80/82/98b81182a44f213e17fbb7387f9121eacc5b6689fcbc2fdbc7e75aacf2bd/opisense_client-0.0.2.tar.gz" } ], "0.1.0": [ { "comment_text": "", "digests": { "md5": "3f7d8845771558813a858c88fd927b0b", "sha256": "77718911424e792182bcc543c06608f837d5a065cf716e5d9809c18fb0d31d00" }, "downloads": -1, "filename": "opisense_client-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "3f7d8845771558813a858c88fd927b0b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 6283, "upload_time": "2018-12-27T23:44:29", "url": "https://files.pythonhosted.org/packages/c5/40/16cbb379c07441ca1eff9f4d05a1e5cd8ba1f6684e86a6bb378549a15727/opisense_client-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7ee873169c68b9eeffa3a0a61df6fe6a", "sha256": "4b17a99fae0e900048f833d574e8afde2e4357dc84b78946ab3406422df6a2bf" }, "downloads": -1, "filename": "opisense_client-0.1.0.tar.gz", "has_sig": false, "md5_digest": "7ee873169c68b9eeffa3a0a61df6fe6a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3113, "upload_time": "2018-12-27T23:44:34", "url": "https://files.pythonhosted.org/packages/ab/81/a786614e02713cc2758ed124b56b4892d51666f7f955f8743c78121dde6c/opisense_client-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "669107c17d73d3357a0908a84c864b6f", "sha256": "9fcf2cf6b72f427964f97f23098e20f784973f16a23f1d5a13aef13b0f302705" }, "downloads": -1, "filename": "opisense_client-0.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "669107c17d73d3357a0908a84c864b6f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 4513, "upload_time": "2018-12-27T23:44:30", "url": "https://files.pythonhosted.org/packages/53/31/6666ccecbcc90f69def8999f6b458c12e73b3c7692e9e2dcea580dbab69b/opisense_client-0.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "aca3eef9b8bad589ba015b3f6c6b98e3", "sha256": "5b53fd4b0e79857d043c038d6914b874f62ebe583b684af8765015f8f6b2be2f" }, "downloads": -1, "filename": "opisense_client-0.2.0.tar.gz", "has_sig": false, "md5_digest": "aca3eef9b8bad589ba015b3f6c6b98e3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3259, "upload_time": "2018-12-27T23:44:35", "url": "https://files.pythonhosted.org/packages/2b/fa/f0d37402750ce4dc5ddde56c7ea1795c377477bc735b37a1b93a61d838f2/opisense_client-0.2.0.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "7aa477d155ee0a478448202a773b0c9d", "sha256": "5a9bd3040130ba0a2b52329626af4f35076a9e5ad2d0dec9aac317db9be173ed" }, "downloads": -1, "filename": "opisense_client-0.2.1.tar.gz", "has_sig": false, "md5_digest": "7aa477d155ee0a478448202a773b0c9d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3411, "upload_time": "2018-12-28T19:37:45", "url": "https://files.pythonhosted.org/packages/f5/87/fdea198f8d60e3276b467ba30a5a1a7d781390b614753abb7208d3f7d3d6/opisense_client-0.2.1.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "754bed3a23ea24f3540b07c0dbd5c2c4", "sha256": "d448d7e72a2c7f4517066fb0fb883b6ef77cc9a1b1ec65134273bd3042d5be4c" }, "downloads": -1, "filename": "opisense_client-0.3.0.tar.gz", "has_sig": false, "md5_digest": "754bed3a23ea24f3540b07c0dbd5c2c4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 3554, "upload_time": "2018-12-28T19:37:46", "url": "https://files.pythonhosted.org/packages/75/1f/a4fd21d71cfad067d6738e94d214f3c0b0853f74620071e6fdbb437da55e/opisense_client-0.3.0.tar.gz" } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "97e3dfad471852f36b6b4f506b1bcbe8", "sha256": "477c9deb070ae5fae0de042ad5a6afd8682ba3a9b403b6469743588d4840cacd" }, "downloads": -1, "filename": "opisense_client-1.0.0.tar.gz", "has_sig": false, "md5_digest": "97e3dfad471852f36b6b4f506b1bcbe8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 4362, "upload_time": "2019-01-07T21:05:26", "url": "https://files.pythonhosted.org/packages/34/9f/9a850ba0032f945ac63011c726194fa9b80fc0c145c646087019d849bafd/opisense_client-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "cc66b7bc29da297f7b1860b5de559c24", "sha256": "b95d75217e609de833d39ba4dc2b3f40e160783df07d7e42caf87267e5188f4f" }, "downloads": -1, "filename": "opisense_client-1.0.1.tar.gz", "has_sig": false, "md5_digest": "cc66b7bc29da297f7b1860b5de559c24", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5547, "upload_time": "2019-01-23T23:40:04", "url": "https://files.pythonhosted.org/packages/9d/87/5fdad3c2f61a9945d637c74245967ccc69f6b56f5f8b09b626fc7eb8a1f2/opisense_client-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "f81ec3f0eeb68064b32f81cf144e64b9", "sha256": "4b299b145a920adb7ce1d53d917657cc8d2e51ce04f740a2d6eae146fbe5f1b3" }, "downloads": -1, "filename": "opisense_client-1.0.2.tar.gz", "has_sig": false, "md5_digest": "f81ec3f0eeb68064b32f81cf144e64b9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6808, "upload_time": "2019-02-11T19:33:11", "url": "https://files.pythonhosted.org/packages/3c/50/6857b8ff3d0e674b8e2fdfa8545c0c0b53dc5334c0f272f439070d0c00df/opisense_client-1.0.2.tar.gz" } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "1d7d9f3ee4449cde73840911b034febd", "sha256": "701d737c80d170ad43ab828c606d391622a2124da688c4be4d349c9511cf14ec" }, "downloads": -1, "filename": "opisense_client-1.1.0.tar.gz", "has_sig": false, "md5_digest": "1d7d9f3ee4449cde73840911b034febd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7163, "upload_time": "2019-03-12T22:38:49", "url": "https://files.pythonhosted.org/packages/53/2d/5d24926813736d904818ff35161c88eab8c13f4c67e0e6e652553c09c126/opisense_client-1.1.0.tar.gz" } ], "1.1.1": [ { "comment_text": "", "digests": { "md5": "842f2167394f791cc8c2c9420adee1bf", "sha256": "6f8ea582346d44d60c3d35b7f02a191120e983d16943dbde29ac338e29ec888b" }, "downloads": -1, "filename": "opisense_client-1.1.1.tar.gz", "has_sig": false, "md5_digest": "842f2167394f791cc8c2c9420adee1bf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7174, "upload_time": "2019-03-14T19:35:27", "url": "https://files.pythonhosted.org/packages/2b/1f/d001b301031e9a0c6e29e9a8b960787e18aaf90a6aca341cdf9d12aa1945/opisense_client-1.1.1.tar.gz" } ], "1.1.2": [ { "comment_text": "", "digests": { "md5": "a91371ea9a17ab4bb5a0edae8c8c7ba4", "sha256": "1be421140e30c406461920d396911f834d36163cc325b512c82cc08cd512f00c" }, "downloads": -1, "filename": "opisense_client-1.1.2.tar.gz", "has_sig": false, "md5_digest": "a91371ea9a17ab4bb5a0edae8c8c7ba4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7186, "upload_time": "2019-03-14T19:58:02", "url": "https://files.pythonhosted.org/packages/c3/16/950159af29f7a1451abe4547bde9156bd3d65d3b20655e7f6f22fb5a9db9/opisense_client-1.1.2.tar.gz" } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "d8c279a934c0ff3b72cc46de1c5f1679", "sha256": "8d3b3494083669fde680407e42b58594f820182719d23c2d82deb1159547eb32" }, "downloads": -1, "filename": "opisense_client-1.2.0.tar.gz", "has_sig": false, "md5_digest": "d8c279a934c0ff3b72cc46de1c5f1679", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8641, "upload_time": "2019-05-10T20:05:33", "url": "https://files.pythonhosted.org/packages/76/41/4cfa9b4f5f57fa71fbef4c2a0155ef04629471ed968ae71cdaa65263d303/opisense_client-1.2.0.tar.gz" } ], "1.2.1": [ { "comment_text": "", "digests": { "md5": "5bce69e9e53040a49784d555543138ee", "sha256": "cc131669b99561aca83c4f80fb4c155a15ec5af1a4406a44e85225ce51fc72b8" }, "downloads": -1, "filename": "opisense_client-1.2.1.tar.gz", "has_sig": false, "md5_digest": "5bce69e9e53040a49784d555543138ee", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8596, "upload_time": "2019-05-10T21:00:47", "url": "https://files.pythonhosted.org/packages/87/52/7ca41debb73762707f0808120b0745afd370e7fe6024a48a07067bbed736/opisense_client-1.2.1.tar.gz" } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "7244b9d8f619d6a7b0a82253ccf7b6d3", "sha256": "bfc5ecc51abd38a14c8f2917c8b20c7f58d22addd8d29283ada54a25bdc4dd54" }, "downloads": -1, "filename": "opisense_client-1.3.0.tar.gz", "has_sig": false, "md5_digest": "7244b9d8f619d6a7b0a82253ccf7b6d3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8979, "upload_time": "2019-05-29T17:43:44", "url": "https://files.pythonhosted.org/packages/d7/45/2c419c498427245bd21c1e1ec64fc53a288caa3f925c97cbae309578b4c1/opisense_client-1.3.0.tar.gz" } ], "1.4.0": [ { "comment_text": "", "digests": { "md5": "428e1d9593743dc4fbc3b7d12cfbc33f", "sha256": "9107862f6a6fdec7b21d00bfb41c4341591a785cf9651cbf3db5cc34727c2202" }, "downloads": -1, "filename": "opisense_client-1.4.0.tar.gz", "has_sig": false, "md5_digest": "428e1d9593743dc4fbc3b7d12cfbc33f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10788, "upload_time": "2019-08-11T20:46:22", "url": "https://files.pythonhosted.org/packages/61/46/623005b42ed47dc78b4aea37bcfaf42ff21a39c8d4a4b59591236046a8b4/opisense_client-1.4.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "428e1d9593743dc4fbc3b7d12cfbc33f", "sha256": "9107862f6a6fdec7b21d00bfb41c4341591a785cf9651cbf3db5cc34727c2202" }, "downloads": -1, "filename": "opisense_client-1.4.0.tar.gz", "has_sig": false, "md5_digest": "428e1d9593743dc4fbc3b7d12cfbc33f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10788, "upload_time": "2019-08-11T20:46:22", "url": "https://files.pythonhosted.org/packages/61/46/623005b42ed47dc78b4aea37bcfaf42ff21a39c8d4a4b59591236046a8b4/opisense_client-1.4.0.tar.gz" } ] }