forked from LBRYCommunity/lbry-sdk
add more tests, flag to exclude own supports
This commit is contained in:
parent
4bb0344e05
commit
abebc0d878
7 changed files with 98 additions and 27 deletions
|
@ -288,8 +288,8 @@ class Database:
|
||||||
async def search_supports(self, **constraints) -> Result[Output]:
|
async def search_supports(self, **constraints) -> Result[Output]:
|
||||||
return await self.fetch_result(q.search_supports, **constraints)
|
return await self.fetch_result(q.search_supports, **constraints)
|
||||||
|
|
||||||
async def sum_supports(self, claim_hash, include_channel_content=False) -> List[Dict]:
|
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)
|
return await self.run(q.sum_supports, claim_hash, include_channel_content, exclude_own_supports)
|
||||||
|
|
||||||
async def resolve(self, urls, **kwargs) -> Dict[str, Output]:
|
async def resolve(self, urls, **kwargs) -> Dict[str, Output]:
|
||||||
return await self.run(q.resolve, urls, **kwargs)
|
return await self.run(q.resolve, urls, **kwargs)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import struct
|
||||||
import logging
|
import logging
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from binascii import unhexlify
|
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 import func, case, text
|
||||||
from sqlalchemy.future import select, Select
|
from sqlalchemy.future import select, Select
|
||||||
|
@ -62,30 +62,39 @@ def search_supports(**constraints) -> Tuple[List[Output], Optional[int]]:
|
||||||
return txos, total
|
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")
|
supporter = Claim.alias("supporter")
|
||||||
content = Claim.alias("content")
|
content = Claim.alias("content")
|
||||||
where_condition = (content.c.claim_hash == claim_hash)
|
where_condition = (content.c.claim_hash == claim_hash)
|
||||||
if include_channel_content:
|
if include_channel_content:
|
||||||
where_condition |= (content.c.channel_hash == claim_hash)
|
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(
|
q = select(
|
||||||
supporter.c.claim_name.label("supporter"),
|
supporter.c.short_url.label("supporter"),
|
||||||
func.sum(TXO.c.amount).label("staked"),
|
func.sum(TXO.c.amount).label("staked"),
|
||||||
).select_from(
|
).select_from(
|
||||||
TXO
|
TXO
|
||||||
.join(content, TXO.c.claim_hash == content.c.claim_hash)
|
.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(
|
||||||
where_condition &
|
where_condition &
|
||||||
(TXO.c.txo_type == TXO_TYPES["support"]) &
|
(TXO.c.txo_type == TXO_TYPES["support"]) &
|
||||||
((TXO.c.address == content.c.address) | ((TXO.c.address != content.c.address) & (TXO.c.spent_height == 0)))
|
((TXO.c.address == content.c.address) | ((TXO.c.address != content.c.address) & (TXO.c.spent_height == 0)))
|
||||||
).group_by(
|
).group_by(
|
||||||
supporter.c.claim_name
|
supporter.c.short_url
|
||||||
).order_by(
|
).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:
|
def search_support_count(**constraints) -> int:
|
||||||
|
|
|
@ -2579,20 +2579,22 @@ class API:
|
||||||
claim_id: str, # id of claim to calculate support stats for
|
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
|
include_channel_content: bool = False, # if claim_id is for a channel, include supports for
|
||||||
# claims in that channel
|
# claims in that channel
|
||||||
|
exclude_own_supports: bool = False, # exclude supports signed by claim_id (i.e. self-supports)
|
||||||
**pagination_kwargs
|
**pagination_kwargs
|
||||||
) -> Paginated[Dict]: # supports grouped by channel
|
) -> Paginated[Dict]: # supports grouped by channel
|
||||||
# TODO: add unsigned supports to the output so the numbers add up. just a left join on identity
|
# 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.
|
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
|
If claim_id is a channel claim:
|
||||||
content claims in the channel.
|
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:
|
Usage:
|
||||||
support sum <claim_id> [--inculde_channel_content]
|
support sum <claim_id> [--inculde_channel_content]
|
||||||
{kwargs}
|
{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(
|
async def support_abandon(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -152,7 +152,8 @@ class Service:
|
||||||
async def search_transactions(self, txids):
|
async def search_transactions(self, txids):
|
||||||
raise NotImplementedError
|
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
|
raise NotImplementedError
|
||||||
|
|
||||||
async def announce_addresses(self, address_manager, addresses: List[str]):
|
async def announce_addresses(self, address_manager, addresses: List[str]):
|
||||||
|
|
|
@ -69,5 +69,6 @@ class FullNode(Service):
|
||||||
async def protobuf_resolve(self, urls, **kwargs):
|
async def protobuf_resolve(self, urls, **kwargs):
|
||||||
return await self.db.protobuf_resolve(urls, **kwargs)
|
return await self.db.protobuf_resolve(urls, **kwargs)
|
||||||
|
|
||||||
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) \
|
||||||
return await self.db.sum_supports(claim_hash, include_channel_content)
|
-> List[Dict]:
|
||||||
|
return await self.db.sum_supports(claim_hash, include_channel_content, exclude_own_supports)
|
||||||
|
|
|
@ -46,5 +46,6 @@ class LightClient(Service):
|
||||||
async def search_supports(self, accounts, **kwargs):
|
async def search_supports(self, accounts, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
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) \
|
||||||
return await self.client.sum_supports(claim_hash, include_channel_content)
|
-> List[Dict]:
|
||||||
|
return await self.client.sum_supports(claim_hash, include_channel_content, exclude_own_supports)
|
||||||
|
|
|
@ -945,34 +945,91 @@ class TestGeneralBlockchainSync(SyncingBlockchainTestCase):
|
||||||
self.assertEqual(claim.claim_id, results[0].claim_id)
|
self.assertEqual(claim.claim_id, results[0].claim_id)
|
||||||
|
|
||||||
async def test_claim_search_sum(self):
|
async def test_claim_search_sum(self):
|
||||||
|
# print("DB URL: " + self.chain.ledger.conf.db_url_or_default)
|
||||||
await self.generate(100)
|
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))
|
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))
|
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))
|
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)
|
# make some tips and supports from channels B and C to channel A
|
||||||
await self.support_claim(channel_a, '4.0', sign=channel_c)
|
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)
|
||||||
|
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)
|
await self.generate(1)
|
||||||
|
|
||||||
|
# check that supports sum correctly
|
||||||
results = await self.db.sum_supports(channel_a.claim_hash)
|
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))
|
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.support_claim(claim_a, '1.0', sign=channel_b)
|
||||||
await self.generate(1)
|
await self.generate(1)
|
||||||
|
|
||||||
|
# supports for just the channel claim should be unaffected ...
|
||||||
results = await self.db.sum_supports(channel_a.claim_hash)
|
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)
|
# check that sum_supports works as expected for a non-channel claim (with and without including channel content)
|
||||||
self.assertEqual(results, [{'supporter': '@B', 'staked': 1100000000}, {'supporter': '@C', 'staked': 600000000}])
|
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)
|
# if a support is abandoned, it stops counting
|
||||||
self.assertEqual(results, [{'supporter': '@B', 'staked': 100000000}])
|
await self.abandon_support(support_b)
|
||||||
results = await self.db.sum_supports(claim_a.claim_hash, True)
|
await self.generate(1)
|
||||||
self.assertEqual(results, [{'supporter': '@B', 'staked': 100000000}])
|
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):
|
async def test_meta_fields_are_translated_to_protobuf(self):
|
||||||
chan_ab = await self.get_claim(
|
chan_ab = await self.get_claim(
|
||||||
|
|
Loading…
Reference in a new issue