{ "info": { "author": "Agrimanagement, Inc.", "author_email": "pmarshall@agrimgt.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Framework :: AsyncIO", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3 :: Only" ], "description": "===================\npython-fieldclimate\n===================\n\nA client for the iMetos FieldClimate API: https://api.fieldclimate.com/v1/docs/\n\nTo use this, you'll need HMAC credentials provided by iMetos. See their docs for more info.\n\nRequires Python 3.5 or better. Tested on Python 3.6. Depends on asks_ and pycryptodome_.\n\n.. _asks: https://github.com/theelous3/asks\n.. _pycryptodome: https://github.com/Legrandin/pycryptodome\n\n\nInstallation\n------------\n\nUse ``pip`` to install the current release, version 1.3, from PyPI_::\n\n pip install python-fieldclimate\n\n.. _PyPI: https://pypi.org/project/python-fieldclimate/\n\n\nUsage\n-----\n\nHere's a simple example that returns the associated user's account info:\n\n.. code-block:: python\n\n from asyncio import run\n from fieldclimate import FieldClimateClient\n\n async def main():\n async with FieldClimateClient(private_key=\"YOUR\", public_key=\"KEYS\") as client:\n return await client.get_user()\n\n if __name__ == \"__main__\":\n run(main)\n\n\nEvent Loops\n~~~~~~~~~~~\n\n**New in version 1.3.**\n\nThe same FieldClimateClient class can be used to make asynchronous API requests under any modern event loop.\nThis is thanks to asks being written with anyio_, which currently supports asyncio_, curio_, and trio_.\n\n.. _anyio: https://github.com/agronholm/anyio\n.. _asyncio: https://docs.python.org/3/library/asyncio.html\n.. _curio: https://github.com/dabeaz/curio\n.. _trio: https://github.com/python-trio/trio\n\n\nAuthentication\n~~~~~~~~~~~~~~\n\nHMAC credentials can be provided in several ways:\n\n1. Via the init constructor:\n\n >>> FieldClimateClient(public_key=\"YOUR\", private_key=\"KEYS\")\n\n2. Environment variables ``FIELDCLIMATE_PUBLIC_KEY`` and ``FIELDCLIMATE_PRIVATE_KEY``.\n\n3. Subclassing FieldClimateClient:\n\n >>> class MyClient(FieldClimateClient):\n ... private_key = \"YOUR\"\n ... public_key = \"KEYS\"\n\n4. If you use Django, you can use ``fieldclimate.django.DjangoFieldClimateClient`` in place of FieldClimateClient.\n This subclass will grab ``FIELDCLIMATE_PUBLIC_KEY`` and ``FIELDCLIMATE_PRIVATE_KEY`` from django's settings.\n\n\nMethods\n~~~~~~~\n\nThe client has methods for each of the corresponding routes listed in the api docs.\nThere's a lot of them, so see the full list of methods in ``fieldclimate/__init__.py`` for more details.\nEvery method returns a JSON-like python object upon being awaited, like a dictionary or a list.\n\nSome methods will clean up their arguments in order to make working with the API in python easier.\nHere are some examples:\n\n- ``get_data_last()`` accepts the ``time_period`` parameter.\n The API docs specify this to be a string like ``'6h'`` or ``'7d'``, meaning 6 hours or 7 days.\n FieldClimateClient additionally accepts timedelta objects for this parameter,\n and will convert them to their equivalent strings for the API\n (i.e. ``timedelta(hours=6)`` is converted to ``'21600'`` seconds).\n\n- Many methods require a ``station`` parameter, like ``get_data_range()`` does in the examples above.\n This can be a raw Station ID string, which you can dig out of a station dictionary returned by ``get_user_stations()``.\n Or, you can pass that dictionary directly in as the station parameter, and the ID will be extracted.\n\nThese methods do not all have test coverage (testing ``delete_user()`` might be a bad idea).\nHowever, the underlying connection and cleaning utilities they use are all tested.\n\n\nConnection Limits\n~~~~~~~~~~~~~~~~~\n\n**New in version 1.3.**\n\nThe connection limit can be raised by setting the connections argument when calling the FieldClimateClient constructor.\n\nFrom `asks' docs`_:\n\n You *will* want to change the number of connections to a value that suits your needs and the server\u2019s limitations.\n If no data is publicly available to guide you here, err on the low side.\n\n **The default number of connections in the pool for a Session is a measly ONE.**\n\n.. _asks' docs: https://asks.readthedocs.io/en/latest/a-look-at-sessions.html#important-connection-un-limiting\n\nExample:\n\n.. code-block:: python\n\n async with FieldClimateClient(connections=10) as client:\n ...\n\n\nAccording to FieldClimate's docs, they do not yet enforce rate limiting server-side.\nUsing FieldClimateClient with a high connection limit allows you to create *a lot* of requests at once.\nDuring my testing, I noticed the API starting to raise 502 errors when I overloaded it too much.\n\nPlease be courteous with your resource consumption!\n\n\nAdvanced Example\n~~~~~~~~~~~~~~~~\n\nThis function asks for some user data and gets the list of all user stations, at the same time.\nAs soon as the stations come back, it counts them and sends off another request for each of the first 10 stations.\nThen each of those 10 station responses is printed, sorted by server reply time.\n\n.. code-block:: python\n\n from asyncio import gather, run\n from fieldclimate import FieldClimateClient\n\n async def main():\n async with FieldClimateClient(\n private_key=\"YOUR\",\n public_key=\"KEYS\",\n connections=20\n ) as client:\n async def print_user_json():\n print(await client.get_user())\n\n async def print_station_dates(station):\n print(await client.get_data_range(station))\n\n async def count_stations_then_print_ranges():\n stations = await client.get_user_stations()\n print(len(stations))\n await gather(*[\n print_station_dates(station)\n for station in stations[:10]\n ])\n\n await gather(\n print_user_json(),\n count_stations_then_print_ranges(),\n )\n\n if __name__ == \"__main__\":\n run(main())\n\n\nAlternate curio and trio implementations are the ``tests`` directory,\nif you want to see how to use FieldClimateClient in those event loops (it's much of the same).\n\n\nSynchronous Usage\n~~~~~~~~~~~~~~~~~\n\n**Removed in version 1.3.**\n\nIn version 1.2, FieldClimateClient would automatically set up an asyncio event loop when methods were\nbeing called outside of an ``async with`` block.\nThis way, callers could use the library without having to write any scary async/await code.\n\nHaving this mix of syntax ended up being confusing and unnecessary, in addition to leading to messy code here.\nSo, with the switch to the ``asks`` backend, support for the old synchronous use case was removed.\n\nIf you were using FieldClimateClient's older 'synchronous usage' mode, you were already using a version of Python that\nallowed for async/await. The difference is that now you have to set up an event loop yourself.\n\nIf you still *really* don't want to write any coroutines, the simplest way to make your code compatible with version 1.3\nis to just wrap each method call with ``asyncio.run()``:\n\n.. code-block:: python\n\n import asyncio\n from fieldclimate import FieldClimateClient\n\n def main():\n client = FieldClimateClient(private_key=\"YOUR\", public_key=\"KEYS\")\n # print user json\n print(asyncio.run(client.get_user()))\n # count stations\n stations = asyncio.run(client.get_user_stations())\n print(len(stations))\n # print ranges\n for station in stations[:10]:\n print(asyncio.run(client.get_data_range(station)))\n\n if __name__ == \"__main__\":\n main()\n\n\nThis 'synchronous' example takes 3 times longer to complete than the equivalent \"Advanced Example\" above, because the\nmain() function is blocked during each request sent to the server.\nThe asynchronous code, on the other hand, only blocks when there's nothing to do *but* wait for the server.\nConsider this when deciding whether or not to convert your code to use coroutine functions.\n\n\nContributing\n------------\n\nPull requests are welcome. Please clean your code with black_, write tests, and document.\n\n.. _black: https://github.com/ambv/black\n\nIdeas for PRs:\n\n- Exhaustive mocking to achieve full method test coverage.\n- OAuth 2.0 authentication.\n\n\n=======\nChanges\n=======\n\n\nTODO\n----\n\n- Add support for Metos' API v2: https://api.fieldclimate.com/v2/docs/\n - How should we best support both users of v2 and v1, which should still be supported?\n - Need to assess how different the new API is before deciding on how to tackle this.\n - Increment major version to track with upstream.\n\n\n1.3 (2019-09-23)\n----------------\n\nHigh-level changes:\n\n- Dropped ``aiohttp`` library in favor of using ``asks``.\n- This adds support for asyncio, trio, and curio async loops.\n- Dropped synchronous interface on FieldClimateClient.\n **This means all client methods must now be awaited.**\n\nImplementation changes:\n\n- Moved url validation functions from ``fieldclimate.utils`` to ``fieldclimate.clean``.\n These functions now raise ``AssertionError`` explicitly, as ``assert`` statements can be switched off.\n- FieldClimateClient now inherits from ``asks.Session``,\n which provides async context manager usage and connection rate limiting.\n- Removed BaseClient and HmacClient classes, unifying their functionality in FieldClimateClient.\n- Added tests for trio and curio event loops.\n\nBonus changes:\n\n- Added DjangoFieldClimateClient.\n This subclass gets your HMAC authentication keys from django's settings,\n which can save you a few lines of code if you already use django.\n\n\n1.2 (2018-10-26)\n----------------\n\n- Dropped ``requests`` library in favor of using ``aiohttp`` for both sync and async interfaces.\n\n\n1.1 (2018-10-25)\n----------------\n\n- Renamed all ``station_id`` method parameters to ``station``, possibly breaking your code.\n- This argument can now handle an entire station dictionary, and will extract the station_id automatically.\n\n\n1.0 (2018-10-24)\n----------------\n\n- Initial PyPI release. \ud83c\udf89\n\n\n=======\nAuthors\n=======\n\n- Phillip Marshall \n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/agrimgt/python-fieldclimate", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "python-fieldclimate", "package_url": "https://pypi.org/project/python-fieldclimate/", "platform": "", "project_url": "https://pypi.org/project/python-fieldclimate/", "project_urls": { "API Documentation": "https://api.fieldclimate.com/v1/docs/", "Homepage": "https://github.com/agrimgt/python-fieldclimate" }, "release_url": "https://pypi.org/project/python-fieldclimate/1.3/", "requires_dist": [ "asks", "pycryptodome" ], "requires_python": ">=3.6", "summary": "A client for the iMetos FieldClimate API.", "version": "1.3" }, "last_serial": 5877393, "releases": { "1.0": [ { "comment_text": "", "digests": { "md5": "20bc9ad9a8af7da9ed069b4f1fdf74ba", "sha256": "05a666200196d5a63267518d50fcad0bb02af0ddb545fbe11d4a6c84d13e058f" }, "downloads": -1, "filename": "python_fieldclimate-1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "20bc9ad9a8af7da9ed069b4f1fdf74ba", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 9774, "upload_time": "2018-10-25T00:39:12", "url": "https://files.pythonhosted.org/packages/2f/31/6d1dbf7936e9b73613350450725e35b7bfbf73d5baa12a773976f94ff14a/python_fieldclimate-1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c5ba9b08724d3eb933898460aedc73b2", "sha256": "6cf39415314dc749e833e0d474fefab1296026de4e0a65d52678557a46967d7c" }, "downloads": -1, "filename": "python-fieldclimate-1.0.tar.gz", "has_sig": false, "md5_digest": "c5ba9b08724d3eb933898460aedc73b2", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 11367, "upload_time": "2018-10-25T00:39:14", "url": "https://files.pythonhosted.org/packages/e1/8a/5e3365f16baf1f22cbcce857daf1eea418bdae3e43421cf10bd3c6103e6b/python-fieldclimate-1.0.tar.gz" } ], "1.1": [ { "comment_text": "", "digests": { "md5": "d5ed5625a8dae1386d5d338cc1b42f3d", "sha256": "a6a8694179700c600bc5f321102abbf5cf02392f6c2f7dad86dec50bb4789a00" }, "downloads": -1, "filename": "python_fieldclimate-1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "d5ed5625a8dae1386d5d338cc1b42f3d", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 10121, "upload_time": "2018-10-26T00:14:33", "url": "https://files.pythonhosted.org/packages/2c/95/1930e0576a618722d7682bdef74b78e8618f20f3a154c3d4e87e6a2117ea/python_fieldclimate-1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f6d86b37b169a2809c68fd09d020dc26", "sha256": "0dc9eb6c99eddc410ff67c29b61e427bc465c97667783f2a6e7498025a7d7474" }, "downloads": -1, "filename": "python-fieldclimate-1.1.tar.gz", "has_sig": false, "md5_digest": "f6d86b37b169a2809c68fd09d020dc26", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 11778, "upload_time": "2018-10-26T00:14:34", "url": "https://files.pythonhosted.org/packages/5a/71/65d7d57cd519d79d9ba75dc46b66e0af9c66030c7ae8fb6ce40898a293ee/python-fieldclimate-1.1.tar.gz" } ], "1.2": [ { "comment_text": "", "digests": { "md5": "9c813c0985e204b48a51e21cbcf2ebb8", "sha256": "533bbe54c5cec8c3fca94ba89f1eda2b19a36e6cd03ec20044cc509a0308ef4a" }, "downloads": -1, "filename": "python_fieldclimate-1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "9c813c0985e204b48a51e21cbcf2ebb8", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 10189, "upload_time": "2018-10-26T23:59:49", "url": "https://files.pythonhosted.org/packages/54/36/7a39aab4dab8b1781b41e53e684e07a3bc646a71d7c9b149146a0bd7b958/python_fieldclimate-1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5f34b72cca8103f1e254b8a8180d982c", "sha256": "4af9acd8ee901a70ab02ade0e4da83f28f8d4d84bd24adc60fbda6ce11101499" }, "downloads": -1, "filename": "python-fieldclimate-1.2.tar.gz", "has_sig": false, "md5_digest": "5f34b72cca8103f1e254b8a8180d982c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 11720, "upload_time": "2018-10-26T23:59:51", "url": "https://files.pythonhosted.org/packages/0c/9b/6307d01190b87489194e4ec10d5145ddc987c7c676ff06826b2e9de0305d/python-fieldclimate-1.2.tar.gz" } ], "1.3": [ { "comment_text": "", "digests": { "md5": "d83b61234923bc9f02218357c55f060b", "sha256": "b78aed419413339a1c65a595335d7c9227d4c87faaf64063c1849e6d8c5d0300" }, "downloads": -1, "filename": "python_fieldclimate-1.3-py3-none-any.whl", "has_sig": false, "md5_digest": "d83b61234923bc9f02218357c55f060b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 10794, "upload_time": "2019-09-24T04:07:10", "url": "https://files.pythonhosted.org/packages/b1/66/ee1045624c21184892d4c971f866f60e4cbe78eea7e56a096abfddcd3b80/python_fieldclimate-1.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "726f61483761a4496396a8ddc605bac6", "sha256": "2d21a3b6b501ce029ff50c562384798e646e5a68840d600187af0ffed723439a" }, "downloads": -1, "filename": "python-fieldclimate-1.3.tar.gz", "has_sig": false, "md5_digest": "726f61483761a4496396a8ddc605bac6", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 10439, "upload_time": "2019-09-24T04:07:12", "url": "https://files.pythonhosted.org/packages/e6/90/f5f11bd34f9b12263e60e58ee0d8f4e459896a619b0480d17d22a999d000/python-fieldclimate-1.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "d83b61234923bc9f02218357c55f060b", "sha256": "b78aed419413339a1c65a595335d7c9227d4c87faaf64063c1849e6d8c5d0300" }, "downloads": -1, "filename": "python_fieldclimate-1.3-py3-none-any.whl", "has_sig": false, "md5_digest": "d83b61234923bc9f02218357c55f060b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 10794, "upload_time": "2019-09-24T04:07:10", "url": "https://files.pythonhosted.org/packages/b1/66/ee1045624c21184892d4c971f866f60e4cbe78eea7e56a096abfddcd3b80/python_fieldclimate-1.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "726f61483761a4496396a8ddc605bac6", "sha256": "2d21a3b6b501ce029ff50c562384798e646e5a68840d600187af0ffed723439a" }, "downloads": -1, "filename": "python-fieldclimate-1.3.tar.gz", "has_sig": false, "md5_digest": "726f61483761a4496396a8ddc605bac6", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 10439, "upload_time": "2019-09-24T04:07:12", "url": "https://files.pythonhosted.org/packages/e6/90/f5f11bd34f9b12263e60e58ee0d8f4e459896a619b0480d17d22a999d000/python-fieldclimate-1.3.tar.gz" } ] }