Skip to main content

Python toolkit for Conseil blockchain indexer

Project description

ConseilPy

PyPI version Build Status Made With License: MIT

Python toolkit for Conseil blockchain indexer

Installation

Python 3.6+ required

$ pip install conseil

Usage

ConseilPy is a lot like Sqlalchemy, so if you're familiar with it, you can easily cook queries.

It's time to cook

Quickstart

Get top 5 delegators by balance

from conseil import conseil

Account = conseil.tezos.alphanet.accounts
Account.query(Account.acccount_id, Account.balance) \
    .filter(Account.script.is_(None), 
            Account.account_id.startswith('KT1')) \
    .order_by(Account.balance.desc()) \
    .limit(5) \
    .all()

See more examples

Client initialization

If using a default conseil client is not an option you can instantiate it yourself:

from conseil.api import ConseilApi
from conseil.core import Client

conseil = Client(ConseilApi(
    api_key='<API_KEY>',
    api_host='<API_HOST>',
    api_version=2
))

Exploring database schema

Conseil metadata has the following tree structure:
platform / network / entity / attribute / value

So you can simply access any node by name:

>>> from conseil import conseil
>>> print(conseil.tezos.alphanet.operations.kind.transaction)
transaction

Autocompletion Shift + Tab and docstrings are available in Jupyter:

>>> conseil
Path
metadata/platforms

Platforms
.tezos

>>> conseil.tezos.alphanet
Path
metadata/tezos/alphanet/entities

Entities
.accounts
.balance_updates
.ballots
.blocks
.delegates
.fees
.operation_groups
.operations
.proposals
.rolls

Alternatively you can check full SQL schema

Selecting fields

Conseil doesn't support joins at the moment so you can request attributes for a single entity only.

from conseil import conseil

c = conseil.tezos.alphanet

# select all fields
c.query(c.accounts)
c.accounts.query()

# select specific fields
c.query(c.accounts.balance, c.accounts.account_id)
c.accounts.query(c.accounts.balance, c.accounts.account_id)

# select single field
c.accounts.balance.query()

Filtering results

Conseil receives a conjunction of predicates, which can be inverted by one, but not together. Predicate syntax is similar to Sqlalchemy, but has less operations.

from conseil import conseil
from conseil.core import not_

Account = conseil.tezos.alphanet.accounts
Account.query() \
    .filter(not_(Account.account_id.startswith('tz')),
            Account.script.is_(None),
            Account.balance > 0)

Here is a full list of supported operations:

Conseil operation Filter Inversed
in x.in_(a, b, ...) x.notin_(a, b, ...)
between x.between(a, b) not_(x.between(a, b))
like x.like(a) x.notlike(a)
lt x < a x >= a
gt x > a x <= a
eq x == a x != a
startsWith x.startswith(a) not_(x.startsWith(a))
endsWith x.endswith(a) not_(x.endswith(a))
isnull x.is_(None) x.isnot(None)

You can also use filter_by for simple queries:

from conseil import conseil

conseil.tezos.alphanet.accounts.query() \
    .filter_by(account_id='tzkt')

Data aggregation

This 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:

from conseil import conseil

Block = conseil.tezos.alphanet.blocks
Block.query(Block.baker, Block.level.count(), Block.timestamp.max())  
# will be compiled to SELECT baker, COUNT(level), MAX(timestamp) FROM blocks GROUP BY baker

Additionally, you can specify HAVING predicates if you want to filter results by aggregated column:

from conseil import conseil

Block = conseil.tezos.alphanet.blocks
Block.query(Block.baker, Block.level.count()) \
    .having(Block.level.count() > 1)  # you have to specify aggregation function here as well

Here is the list of supported aggregation functions:

  • count
  • sum
  • avg
  • min
  • max

If you want to group by some fields but not include them in the result use group_by method:

from conseil import conseil

Block = conseil.tezos.alphanet.blocks
Block.query(Block.level.count()) \
	.group_by(Block.baker)

Sorting and limiting results

This is similar to Sqlalchemy as well, you can specify one or multiple sort columns with optional descending modifier.

from conseil import conseil

Account = conseil.tezos.alphanet.accounts
Account.query() \
    .order_by(Account.balance.desc(), Account.account_id) \
    .limit(20)

You can sort by aggregated column too:

from conseil import conseil

Operation = conseil.tezos.alphanet.operations
Operation.query(Operation.source, Operation.amount.avg()) \
    .order_by(Operation.amount.avg().desc()) \
    .limit(50)

Query preview

So you have cooked a simple query and want to see the resulting Conseil request body.

from conseil import conseil

Account = conseil.tezos.alphanet.accounts
query = Account.query() \
    .order_by(Account.balance.desc()) \
    .limit(1)

Then you can simply:

>>> query
Path
data/tezos/alphanet/accounts

Query
{"aggregation": [],
 "fields": [],
 "limit": 1,
 "orderBy": [{"direction": "desc", "field": "balance"}],
 "output": "json",
 "predicates": []}

Execution

It's time to submit our query and get some data.

from conseil import conseil

Account = conseil.tezos.alphanet.accounts

Return multiple rows

query = Account.query()

query.all()  # will return List[dict] (default output type)
query.all(output='csv')  # will return string (csv)

Return single row

query = Account.query() \
	.filter_by(account_id='tzkt')

query.one()  # will fail if there is no account with such id or there are many
query.one_or_none()  # will handle the exception and return None

Return scalar

query = Account.balance.query() \
	.order_by(Account.balance.desc()) \
    .limit(1)

query.scalar()  # will return single numeric value

Return vector

query = Operation.query(Operation.timestamp) \
    .filter_by(source='tzkt')
    
query.vector()  # will return flat list of timestamps

Precision

Conseil allows to specify numeric column precision. In order to use this functionality use decimal type. For example:

from conseil import conseil
from decimal import Decimal

Account = conseil.tezos.alphanet.accounts
Account.query(Account.balance) \
    .filter(Account.balance > Decimal('0.1'), 
            Account.balance < Decimal('0.01'))  # precision will be 2 (max)

Renaming fields

You can change names of requested fields in the resulting json/csv:

from conseil import conseil

Account = conseil.tezos.alphanet.accounts
Account.query(Account.account_id.label('address'))

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

conseil-0.1.4.tar.gz (12.5 kB view details)

Uploaded Source

Built Distribution

conseil-0.1.4-py3-none-any.whl (10.6 kB view details)

Uploaded Python 3

File details

Details for the file conseil-0.1.4.tar.gz.

File metadata

  • Download URL: conseil-0.1.4.tar.gz
  • Upload date:
  • Size: 12.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/0.12.17 CPython/3.7.0 Linux/4.15.0-54-generic

File hashes

Hashes for conseil-0.1.4.tar.gz
Algorithm Hash digest
SHA256 a3b2b6de5003c18c66090bdff2e7f27914e79373df716d2314ab3244623d8333
MD5 94f33e09d84e70d67b53b0ece029527c
BLAKE2b-256 3343f5b0746c979748144b9226b6f42bb70e7f3f2f6801744ce8aeb7014d1672

See more details on using hashes here.

File details

Details for the file conseil-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: conseil-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 10.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/0.12.17 CPython/3.7.0 Linux/4.15.0-54-generic

File hashes

Hashes for conseil-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 c87d0cd0104086535d70574ab70acb0a995ddf4889da747b1738e081235f9c0f
MD5 8425a11e1d9c48c5165012c8e89a911c
BLAKE2b-256 f7767da787c8f2a62a1bde6d06720499c090c5672c6ce39f08c3597dcf5d75c3

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page