# -*- coding: utf-8 -*-
from distutils.core import setup

packages = \
['conseil']

package_data = \
{'': ['*']}

install_requires = \
['requests']

setup_kwargs = {
    'name': 'conseil',
    'version': '0.1.1',
    'description': 'Python toolkit for Conseil blockchain indexer',
    'long_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://memegenerator.net/img/instances/48258954.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=\'<API_KEY>\',\n    api_host=\'<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())  \n# will be compiled to SELECT baker, COUNT(level) 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 > 1)  # or Block.level.count(), no difference\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\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### 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\n\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\n\nAccount.query(Account.account_id.label(\'address\'))\n```',
    'author': 'Michael Zaikin',
    'author_email': 'mz@baking-bad.org',
    'url': 'https://github.com/baking-bad/conseilpy',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
