{ "info": { "author": "Michael Zaikin", "author_email": "mz@baking-bad.org", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7" ], "description": "# ConseilPy\n[![PyPI version](https://badge.fury.io/py/conseil.svg?)](https://badge.fury.io/py/conseil)\n[![Build Status](https://travis-ci.org/baking-bad/conseilpy.svg?branch=master)](https://travis-ci.org/baking-bad/conseilpy)\n[![Made With](https://img.shields.io/badge/made%20with-python-blue.svg?)](https://www.python.org)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nPython toolkit for [Conseil](https://cryptonomic.github.io/Conseil) blockchain indexer\n\n## Installation\n\nPython 3.6+ required\n\n```bash\n$ pip install conseil\n```\n\n## Usage\n\nConseilPy is a lot like Sqlalchemy, so if you're familiar with it, you can easily cook queries.\n\n![It's time to cook](https://i.imgflip.com/35csjl.jpg?)\n\n### Quickstart\nGet top 5 delegators by balance\n```python\nfrom conseil import conseil\n\nAccount = conseil.tezos.alphanet.accounts\nAccount.query(Account.acccount_id, Account.balance) \\\n .filter(Account.script.is_(None), \n Account.account_id.startswith('KT1')) \\\n .order_by(Account.balance.desc()) \\\n .limit(5) \\\n .all()\n```\n\nSee more [examples](https://github.com/baking-bad/conseilpy/tree/master/examples)\n\n### Client initialization\nIf using a default conseil client is not an option you can instantiate it yourself:\n```python\nfrom conseil.api import ConseilApi\nfrom conseil.core import Client\n\nconseil = Client(ConseilApi(\n api_key='',\n api_host='',\n api_version=2\n))\n```\n\n### Exploring database schema\nConseil metadata has the following tree structure: \nplatform / network / entity / attribute / value\n\nSo you can simply access any node by name:\n```python\n>>> from conseil import conseil\n>>> print(conseil.tezos.alphanet.operations.kind.transaction)\ntransaction\n```\n\nAutocompletion `Shift + Tab` and docstrings are available in Jupyter:\n```python\n>>> conseil\nPath\nmetadata/platforms\n\nPlatforms\n.tezos\n\n>>> conseil.tezos.alphanet\nPath\nmetadata/tezos/alphanet/entities\n\nEntities\n.accounts\n.balance_updates\n.ballots\n.blocks\n.delegates\n.fees\n.operation_groups\n.operations\n.proposals\n.rolls\n```\n\nAlternatively you can check full [SQL schema](https://github.com/Cryptonomic/Conseil/blob/master/doc/conseil.sql)\n\n### Selecting fields\nConseil doesn't support joins at the moment so you can request attributes for a single entity only. \n\n```python\nfrom conseil import conseil\n\nc = conseil.tezos.alphanet\n\n# select all fields\nc.query(c.accounts)\nc.accounts.query()\n\n# select specific fields\nc.query(c.accounts.balance, c.accounts.account_id)\nc.accounts.query(c.accounts.balance, c.accounts.account_id)\n\n# select single field\nc.accounts.balance.query()\n```\n\n### Filtering results\nConseil receives a conjunction of predicates, which can be inverted by one, but not together.\nPredicate syntax is similar to Sqlalchemy, but has less operations.\n\n```python\nfrom conseil import conseil\nfrom conseil.core import not_\n\nAccount = conseil.tezos.alphanet.accounts\nAccount.query() \\\n .filter(not_(Account.account_id.startswith('tz')),\n Account.script.is_(None),\n Account.balance > 0)\n```\n\nHere is a full list of supported operations:\n\n| Conseil operation | Filter | Inversed |\n| ----------------- | ------------------ | ----------------------- |\n| in | `x.in_(a, b, ...)` | `x.notin_(a, b, ...)` |\n| between | `x.between(a, b)` | `not_(x.between(a, b))` |\n| like | `x.like(a)` | `x.notlike(a)` |\n| lt | `x < a` | `x >= a` |\n| gt | `x > a` | `x <= a` |\n| eq | `x == a` | `x != a` |\n| startsWith | `x.startswith(a)` | `not_(x.startsWith(a))` |\n| endsWith | `x.endswith(a)` | `not_(x.endswith(a))` |\n| isnull | `x.is_(None)` | `x.isnot(None)` |\n\nYou can also use `filter_by` for simple queries:\n\n```python\nfrom conseil import conseil\n\nconseil.tezos.alphanet.accounts.query() \\\n .filter_by(account_id='tzkt')\n```\n\n### Data aggregation\n\nThis is an important concept to understand. In Conseil you specify which columns will be aggregated and the rest of them are used in `GROUP BY` clause. Here is an example:\n\n```python\nfrom conseil import conseil\n\nBlock = conseil.tezos.alphanet.blocks\nBlock.query(Block.baker, Block.level.count(), Block.timestamp.max()) \n# will be compiled to SELECT baker, COUNT(level), MAX(timestamp) FROM blocks GROUP BY baker\n```\n\nAdditionally, you can specify `HAVING` predicates if you want to filter results by aggregated column:\n\n```python\nfrom conseil import conseil\n\nBlock = conseil.tezos.alphanet.blocks\nBlock.query(Block.baker, Block.level.count()) \\\n .having(Block.level.count() > 1) # you have to specify aggregation function here as well\n```\n\nHere is the list of supported aggregation functions:\n\n* `count`\n* `sum`\n* `avg`\n* `min`\n* `max`\n\nIf you want to group by some fields but not include them in the result use `group_by` method:\n\n```python\nfrom conseil import conseil\n\nBlock = conseil.tezos.alphanet.blocks\nBlock.query(Block.level.count()) \\\n\t.group_by(Block.baker)\n```\n\n### Sorting and limiting results\n\nThis is similar to Sqlalchemy as well, you can specify one or multiple sort columns with optional descending modifier.\n\n```python\nfrom conseil import conseil\n\nAccount = conseil.tezos.alphanet.accounts\nAccount.query() \\\n .order_by(Account.balance.desc(), Account.account_id) \\\n .limit(20)\n```\n\nYou can sort by aggregated column too:\n\n```python\nfrom conseil import conseil\n\nOperation = conseil.tezos.alphanet.operations\nOperation.query(Operation.source, Operation.amount.avg()) \\\n .order_by(Operation.amount.avg().desc()) \\\n .limit(50)\n```\n\n### Query preview\n\nSo you have cooked a simple query and want to see the resulting Conseil request body.\n\n```python\nfrom conseil import conseil\n\nAccount = conseil.tezos.alphanet.accounts\nquery = Account.query() \\\n .order_by(Account.balance.desc()) \\\n .limit(1)\n```\n\nThen you can simply:\n\n```python\n>>> query\nPath\ndata/tezos/alphanet/accounts\n\nQuery\n{\"aggregation\": [],\n \"fields\": [],\n \"limit\": 1,\n \"orderBy\": [{\"direction\": \"desc\", \"field\": \"balance\"}],\n \"output\": \"json\",\n \"predicates\": []}\n```\n\n### Execution\n\nIt's time to submit our query and get some data.\n\n```python\nfrom conseil import conseil\n\nAccount = conseil.tezos.alphanet.accounts\n```\n\n#### Return multiple rows\n\n```python\nquery = Account.query()\n\nquery.all() # will return List[dict] (default output type)\nquery.all(output='csv') # will return string (csv)\n```\n\n#### Return single row\n\n```python\nquery = Account.query() \\\n\t.filter_by(account_id='tzkt')\n\nquery.one() # will fail if there is no account with such id or there are many\nquery.one_or_none() # will handle the exception and return None\n```\n\n#### Return scalar\n\n```python\nquery = Account.balance.query() \\\n\t.order_by(Account.balance.desc()) \\\n .limit(1)\n\nquery.scalar() # will return single numeric value\n```\n\n#### Return vector\n\n```python\nquery = Operation.query(Operation.timestamp) \\\n .filter_by(source='tzkt')\n \nquery.vector() # will return flat list of timestamps\n```\n\n### Precision\nConseil allows to specify numeric column precision. In order to use this functionality use `decimal` type. For example:\n\n```python\nfrom conseil import conseil\nfrom decimal import Decimal\n\nAccount = conseil.tezos.alphanet.accounts\nAccount.query(Account.balance) \\\n .filter(Account.balance > Decimal('0.1'), \n Account.balance < Decimal('0.01')) # precision will be 2 (max)\n```\n\n### Renaming fields\nYou can change names of requested fields in the resulting json/csv:\n\n```python\nfrom conseil import conseil\n\nAccount = conseil.tezos.alphanet.accounts\nAccount.query(Account.account_id.label('address'))\n```\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/baking-bad/conseilpy", "keywords": "tezos,blockchain", "license": "MIT", "maintainer": "Michael Zaikin", "maintainer_email": "mz@baking-bad.org", "name": "conseil", "package_url": "https://pypi.org/project/conseil/", "platform": "", "project_url": "https://pypi.org/project/conseil/", "project_urls": { "Homepage": "https://github.com/baking-bad/conseilpy", "Repository": "https://github.com/baking-bad/conseilpy" }, "release_url": "https://pypi.org/project/conseil/0.1.4/", "requires_dist": [ "requests" ], "requires_python": ">=3.6,<4.0", "summary": "Python toolkit for Conseil blockchain indexer", "version": "0.1.4" }, "last_serial": 5760254, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "e347b949ff20e311425016b6c92bc78a", "sha256": "a8241399c2ff5d006161365aadd2ce6ac57b3c57439e98e80c91b5ca85819553" }, "downloads": -1, "filename": "conseil-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "e347b949ff20e311425016b6c92bc78a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 18727, "upload_time": "2019-07-03T18:19:37", "url": "https://files.pythonhosted.org/packages/b5/55/01f2232ddaeb10fbe58fd8efe8d2f6d2c4b751339024067b78de5e499131/conseil-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7473684b96956094189d295068bf3c9d", "sha256": "0dd0162b43ba0b28208616998439620f60fadc486f86757ad5ef65ab9f582f9d" }, "downloads": -1, "filename": "conseil-0.1.0.tar.gz", "has_sig": false, "md5_digest": "7473684b96956094189d295068bf3c9d", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 7948, "upload_time": "2019-07-03T18:19:40", "url": "https://files.pythonhosted.org/packages/3a/2b/de31b707547756697dcc2160b15e473fdc54ec5213e4e7193d8215a14a1b/conseil-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "150ebcc148470968c78e7fa08e906d4f", "sha256": "0175f4f1f5dec9cea7ea4ec8c40dd86e527c1f1a78c5f5cc3a2f55e634e43c48" }, "downloads": -1, "filename": "conseil-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "150ebcc148470968c78e7fa08e906d4f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 8816, "upload_time": "2019-07-08T14:22:24", "url": "https://files.pythonhosted.org/packages/88/c9/8a0e0742168662ebb9a36384db03579be448b2b4ba4a21f25f10ba591143/conseil-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "bd67701cb1fc499f8a190756fd18beae", "sha256": "fe83060a563d0a9a304cb407f47116ac3f20e5dc7cbca9fd7bf007240a927912" }, "downloads": -1, "filename": "conseil-0.1.1.tar.gz", "has_sig": false, "md5_digest": "bd67701cb1fc499f8a190756fd18beae", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 10736, "upload_time": "2019-07-08T14:22:26", "url": "https://files.pythonhosted.org/packages/4e/db/322a0a85be990074ae681831de22a756ec081a6b55da186f64f7fb0f684c/conseil-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "c1d1e5fd2134b551b6c4381a47f8567a", "sha256": "203ade887d0a7c73155bf68f9a3b3bec7660ec16a587838cafc15e93d0fe0a24" }, "downloads": -1, "filename": "conseil-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "c1d1e5fd2134b551b6c4381a47f8567a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 8943, "upload_time": "2019-07-10T15:37:00", "url": "https://files.pythonhosted.org/packages/46/f3/0a3a0279d7c3dc222fdc490935775a584f11eba4786cef903e72cbe9c9c9/conseil-0.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "18e2ed3d334f2f4d11c2042a1af0c11c", "sha256": "f2c8080e24a10cd41a433c40855bbcaa354542ec51bdca5f8e245f4b8de5b071" }, "downloads": -1, "filename": "conseil-0.1.2.tar.gz", "has_sig": false, "md5_digest": "18e2ed3d334f2f4d11c2042a1af0c11c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 11046, "upload_time": "2019-07-10T15:37:01", "url": "https://files.pythonhosted.org/packages/f2/79/1b6383924f52ec8d7784383c20e00f649346c8e6bbccaf9b1de685fd4f40/conseil-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "faae05db09befaa51ece44fca597ce41", "sha256": "f94554db8831c77f88bc989edf67a494dce2689b563a551d02dd20360944f1ed" }, "downloads": -1, "filename": "conseil-0.1.3-py3-none-any.whl", "has_sig": false, "md5_digest": "faae05db09befaa51ece44fca597ce41", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 8952, "upload_time": "2019-07-11T08:20:57", "url": "https://files.pythonhosted.org/packages/1d/3b/31e132fcd7230442d980799c195fefe1485e0e27f082af70ff261b8bbd50/conseil-0.1.3-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "6fba447db4d14ebdcf4c619356e3313c", "sha256": "292b1fd5463b7fad973e36f2ac656b7a7d05c4f7414838ae8bccb8fb4535fd3d" }, "downloads": -1, "filename": "conseil-0.1.3.tar.gz", "has_sig": false, "md5_digest": "6fba447db4d14ebdcf4c619356e3313c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 11038, "upload_time": "2019-07-11T08:20:58", "url": "https://files.pythonhosted.org/packages/ba/6a/28aa300b2991214efbd8ff76bf4e4573975304ddac94fe421c47ad79507c/conseil-0.1.3.tar.gz" } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "8425a11e1d9c48c5165012c8e89a911c", "sha256": "c87d0cd0104086535d70574ab70acb0a995ddf4889da747b1738e081235f9c0f" }, "downloads": -1, "filename": "conseil-0.1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "8425a11e1d9c48c5165012c8e89a911c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 10559, "upload_time": "2019-08-30T13:08:36", "url": "https://files.pythonhosted.org/packages/f7/76/7da787c8f2a62a1bde6d06720499c090c5672c6ce39f08c3597dcf5d75c3/conseil-0.1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "94f33e09d84e70d67b53b0ece029527c", "sha256": "a3b2b6de5003c18c66090bdff2e7f27914e79373df716d2314ab3244623d8333" }, "downloads": -1, "filename": "conseil-0.1.4.tar.gz", "has_sig": false, "md5_digest": "94f33e09d84e70d67b53b0ece029527c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 12521, "upload_time": "2019-08-30T13:08:38", "url": "https://files.pythonhosted.org/packages/33/43/f5b0746c979748144b9226b6f42bb70e7f3f2f6801744ce8aeb7014d1672/conseil-0.1.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8425a11e1d9c48c5165012c8e89a911c", "sha256": "c87d0cd0104086535d70574ab70acb0a995ddf4889da747b1738e081235f9c0f" }, "downloads": -1, "filename": "conseil-0.1.4-py3-none-any.whl", "has_sig": false, "md5_digest": "8425a11e1d9c48c5165012c8e89a911c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 10559, "upload_time": "2019-08-30T13:08:36", "url": "https://files.pythonhosted.org/packages/f7/76/7da787c8f2a62a1bde6d06720499c090c5672c6ce39f08c3597dcf5d75c3/conseil-0.1.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "94f33e09d84e70d67b53b0ece029527c", "sha256": "a3b2b6de5003c18c66090bdff2e7f27914e79373df716d2314ab3244623d8333" }, "downloads": -1, "filename": "conseil-0.1.4.tar.gz", "has_sig": false, "md5_digest": "94f33e09d84e70d67b53b0ece029527c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 12521, "upload_time": "2019-08-30T13:08:38", "url": "https://files.pythonhosted.org/packages/33/43/f5b0746c979748144b9226b6f42bb70e7f3f2f6801744ce8aeb7014d1672/conseil-0.1.4.tar.gz" } ] }