add more tests, flag to exclude own supports

This commit is contained in:
Alex Grintsvayg 2020-10-16 14:14:38 -04:00 committed by Lex Berezhny
parent 4bb0344e05
commit abebc0d878
7 changed files with 98 additions and 27 deletions

View file

@ -288,8 +288,8 @@ class Database:
async def search_supports(self, **constraints) -> Result[Output]:
return await self.fetch_result(q.search_supports, **constraints)
async def sum_supports(self, claim_hash, include_channel_content=False) -> List[Dict]:
return await self.run(q.sum_supports, claim_hash, include_channel_content)
async def sum_supports(self, claim_hash, include_channel_content=False, exclude_own_supports=False) -> List[Dict]:
return await self.run(q.sum_supports, claim_hash, include_channel_content, exclude_own_supports)
async def resolve(self, urls, **kwargs) -> Dict[str, Output]:
return await self.run(q.resolve, urls, **kwargs)

View file

@ -2,7 +2,7 @@ import struct
import logging
from decimal import Decimal
from binascii import unhexlify
from typing import Tuple, List, Optional
from typing import Tuple, List, Optional, Dict
from sqlalchemy import func, case, text
from sqlalchemy.future import select, Select
@ -62,30 +62,39 @@ def search_supports(**constraints) -> Tuple[List[Output], Optional[int]]:
return txos, total
def sum_supports(claim_hash, include_channel_content = False) -> Tuple[List[Output], Optional[int]]:
def sum_supports(claim_hash, include_channel_content=False, exclude_own_supports=False) -> List[Dict]:
supporter = Claim.alias("supporter")
content = Claim.alias("content")
where_condition = (content.c.claim_hash == claim_hash)
if include_channel_content:
where_condition |= (content.c.channel_hash == claim_hash)
support_join_condition = TXO.c.channel_hash == supporter.c.claim_hash
if exclude_own_supports:
support_join_condition &= TXO.c.channel_hash != claim_hash
q = select(
supporter.c.claim_name.label("supporter"),
supporter.c.short_url.label("supporter"),
func.sum(TXO.c.amount).label("staked"),
).select_from(
TXO
.join(content, TXO.c.claim_hash == content.c.claim_hash)
.join(supporter, TXO.c.channel_hash == supporter.c.claim_hash)
.join(supporter, support_join_condition)
).where(
where_condition &
(TXO.c.txo_type == TXO_TYPES["support"]) &
((TXO.c.address == content.c.address) | ((TXO.c.address != content.c.address) & (TXO.c.spent_height == 0)))
).group_by(
supporter.c.claim_name
supporter.c.short_url
).order_by(
text("staked DESC")
text("staked DESC, supporter ASC")
)
return context().fetchall(q)
result = context().fetchall(q)
total = sum([row['staked'] for row in result])
for row in result:
row['percent'] = round(row['staked']/total*100, 4)
return result
def search_support_count(**constraints) -> int:

View file

@ -2579,20 +2579,22 @@ class API:
claim_id: str, # id of claim to calculate support stats for
include_channel_content: bool = False, # if claim_id is for a channel, include supports for
# claims in that channel
exclude_own_supports: bool = False, # exclude supports signed by claim_id (i.e. self-supports)
**pagination_kwargs
) -> Paginated[Dict]: # supports grouped by channel
# TODO: add unsigned supports to the output so the numbers add up. just a left join on identity
"""
List total staked supports for a claim, grouped by the channel that signed the support.
If claim_id is a channel claim, you can use --include_channel_content to also include supports for
content claims in the channel.
If claim_id is a channel claim:
Use --include_channel_content to include supports for content claims in the channel.
Use --exclude_own_supports to exclude supports from the channel to itself.
Usage:
support sum <claim_id> [--inculde_channel_content]
{kwargs}
"""
return await self.service.sum_supports(hex_str_to_hash(claim_id), include_channel_content)
return await self.service.sum_supports(hex_str_to_hash(claim_id), include_channel_content, exclude_own_supports)
async def support_abandon(
self,

View file

@ -152,7 +152,8 @@ class Service:
async def search_transactions(self, txids):
raise NotImplementedError
async def sum_supports(self, claim_hash: bytes, include_channel_content=False) -> List[Dict]:
async def sum_supports(self, claim_hash: bytes, include_channel_content=False, exclude_own_supports=False) \
-> List[Dict]:
raise NotImplementedError
async def announce_addresses(self, address_manager, addresses: List[str]):

View file

@ -69,5 +69,6 @@ class FullNode(Service):
async def protobuf_resolve(self, urls, **kwargs):
return await self.db.protobuf_resolve(urls, **kwargs)
async def sum_supports(self, claim_hash: bytes, include_channel_content=False) -> List[Dict]:
return await self.db.sum_supports(claim_hash, include_channel_content)
async def sum_supports(self, claim_hash: bytes, include_channel_content=False, exclude_own_supports=False) \
-> List[Dict]:
return await self.db.sum_supports(claim_hash, include_channel_content, exclude_own_supports)

View file

@ -46,5 +46,6 @@ class LightClient(Service):
async def search_supports(self, accounts, **kwargs):
pass
async def sum_supports(self, claim_hash: bytes, include_channel_content=False) -> List[Dict]:
return await self.client.sum_supports(claim_hash, include_channel_content)
async def sum_supports(self, claim_hash: bytes, include_channel_content=False, exclude_own_supports=False) \
-> List[Dict]:
return await self.client.sum_supports(claim_hash, include_channel_content, exclude_own_supports)

View file

@ -945,34 +945,91 @@ class TestGeneralBlockchainSync(SyncingBlockchainTestCase):
self.assertEqual(claim.claim_id, results[0].claim_id)
async def test_claim_search_sum(self):
# print("DB URL: " + self.chain.ledger.conf.db_url_or_default)
await self.generate(100)
# create a few channels with unique addresses
channel_a = await self.get_claim(await self.create_claim(name="@A", is_channel=True))
self.address = await self.chain.get_new_address()
channel_b = await self.get_claim(await self.create_claim(name="@B", is_channel=True))
self.address = await self.chain.get_new_address()
channel_c = await self.get_claim(await self.create_claim(name="@C", is_channel=True))
self.address = await self.chain.get_new_address()
await self.generate(1)
ch_c, ch_b, ch_a = await self.db.search_claims(order_by=['name'], claim_type="channel", limit=3)
await self.support_claim(channel_a, '10.0', sign=channel_b)
await self.support_claim(channel_a, '4.0', sign=channel_c)
# make some tips and supports from channels B and C to channel A
support_b = await self.support_claim(channel_a, '5.0', sign=channel_b)
tip_b = await self.support_claim(channel_a, '5.0', sign=channel_b, address=channel_a.get_address(self.chain.ledger))
await self.support_claim(channel_a, '2.0', sign=channel_c)
await self.support_claim(channel_a, '2.0', sign=channel_c)
tip_c = await self.support_claim(channel_a, '2.0', sign=channel_c, address=channel_a.get_address(self.chain.ledger))
await self.generate(1)
# check that supports sum correctly
results = await self.db.sum_supports(channel_a.claim_hash)
self.assertEqual(results, [{'supporter': '@B', 'staked': 1000000000}, {'supporter': '@C', 'staked': 600000000}])
self.assertEqual(results, [
{'supporter': ch_b.meta['short_url'], 'staked': 1000000000, 'percent': 62.5},
{'supporter': ch_c.meta['short_url'], 'staked': 600000000, 'percent': 37.5},
])
# create a claim in channel A and have channel B support that claim
claim_a = await self.get_claim(await self.create_claim(name="bob", amount='2.0', sign=channel_a))
await self.support_claim(claim_a, '1.0', sign=channel_b)
await self.generate(1)
# supports for just the channel claim should be unaffected ...
results = await self.db.sum_supports(channel_a.claim_hash)
self.assertEqual(results, [{'supporter': '@B', 'staked': 1000000000}, {'supporter': '@C', 'staked': 600000000}])
self.assertEqual(results, [
{'supporter': ch_b.meta['short_url'], 'staked': 1000000000, 'percent': 62.5},
{'supporter': ch_c.meta['short_url'], 'staked': 600000000, 'percent': 37.5},
])
# ... but when you include supports for content in the channel, the support for claim_a is added in
results = await self.db.sum_supports(channel_a.claim_hash, include_channel_content=True)
self.assertEqual(results, [
{'supporter': ch_b.meta['short_url'], 'staked': 1100000000, 'percent': 64.7059},
{'supporter': ch_c.meta['short_url'], 'staked': 600000000, 'percent': 35.2941},
])
results = await self.db.sum_supports(channel_a.claim_hash, True)
self.assertEqual(results, [{'supporter': '@B', 'staked': 1100000000}, {'supporter': '@C', 'staked': 600000000}])
# check that sum_supports works as expected for a non-channel claim (with and without including channel content)
results = await self.db.sum_supports(claim_a.claim_hash, include_channel_content=False)
self.assertEqual(results, [{'supporter': ch_b.meta['short_url'], 'staked': 100000000, 'percent': 100}])
results = await self.db.sum_supports(claim_a.claim_hash, include_channel_content=True)
self.assertEqual(results, [{'supporter': ch_b.meta['short_url'], 'staked': 100000000, 'percent': 100}])
results = await self.db.sum_supports(claim_a.claim_hash, False)
self.assertEqual(results, [{'supporter': '@B', 'staked': 100000000}])
results = await self.db.sum_supports(claim_a.claim_hash, True)
self.assertEqual(results, [{'supporter': '@B', 'staked': 100000000}])
# if a support is abandoned, it stops counting
await self.abandon_support(support_b)
await self.generate(1)
results = await self.db.sum_supports(channel_a.claim_hash)
self.assertEqual(results, [
{'supporter': ch_c.meta['short_url'], 'staked': 600000000, 'percent': 54.5455},
{'supporter': ch_b.meta['short_url'], 'staked': 500000000, 'percent': 45.4545},
])
# but if a creator unlocks a tip, that still counts as the tipping channel's contribution
await self.abandon_support(tip_b)
await self.abandon_support(tip_c)
await self.generate(1)
results = await self.db.sum_supports(channel_a.claim_hash)
self.assertEqual(results, [
{'supporter': ch_c.meta['short_url'], 'staked': 600000000, 'percent': 54.5455},
{'supporter': ch_b.meta['short_url'], 'staked': 500000000, 'percent': 45.4545},
])
# a channel's own supports don't count if you exclude them
await self.support_claim(channel_a, '10.0', sign=channel_a)
await self.generate(1)
results = await self.db.sum_supports(channel_a.claim_hash, exclude_own_supports=False)
self.assertEqual(results, [
{'supporter': ch_a.meta['short_url'], 'staked': 1000000000, 'percent': 47.6190},
{'supporter': ch_c.meta['short_url'], 'staked': 600000000, 'percent': 28.5714},
{'supporter': ch_b.meta['short_url'], 'staked': 500000000, 'percent': 23.8095},
])
results = await self.db.sum_supports(channel_a.claim_hash, exclude_own_supports=True)
self.assertEqual(results, [
{'supporter': ch_c.meta['short_url'], 'staked': 600000000, 'percent': 54.5455},
{'supporter': ch_b.meta['short_url'], 'staked': 500000000, 'percent': 45.4545},
])
async def test_meta_fields_are_translated_to_protobuf(self):
chan_ab = await self.get_claim(