From 62543b94c9ba6f88b6f360b78ee969c4770581a9 Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Thu, 14 Nov 2019 18:01:46 -0500 Subject: [PATCH] minor cleanup of collections implementation --- lbry/lbry/extras/daemon/Daemon.py | 13 +- lbry/lbry/extras/daemon/collection.py | 366 ------------------ lbry/lbry/schema/attrs.py | 4 + lbry/lbry/schema/claim.py | 5 +- lbry/tests/integration/test_claim_commands.py | 5 +- 5 files changed, 18 insertions(+), 375 deletions(-) delete mode 100644 lbry/lbry/extras/daemon/collection.py diff --git a/lbry/lbry/extras/daemon/Daemon.py b/lbry/lbry/extras/daemon/Daemon.py index 1e3e923dd..00de970b3 100644 --- a/lbry/lbry/extras/daemon/Daemon.py +++ b/lbry/lbry/extras/daemon/Daemon.py @@ -3282,7 +3282,7 @@ class Daemon(metaclass=JSONRPCServerType): account_id=None, wallet_id=None, claim_address=None, funding_account_ids=None, preview=False, blocking=False, **kwargs): """ - Create a new collection .... + Create a new collection. Usage: collection_create ( | --name=) ( | --bid=) @@ -3414,7 +3414,7 @@ class Daemon(metaclass=JSONRPCServerType): Options: --claim_id= : (str) claim_id of the collection to update --bid= : (decimal) amount to back the claim - --claims= : (list) claim ids + --claims= : (list) claim ids --clear_claims : (bool) clear existing claim references (prior to adding new ones) --title= : (str) title of the collection --description=<description> : (str) description of the collection @@ -3630,8 +3630,13 @@ class Daemon(metaclass=JSONRPCServerType): items = await self.ledger.resolve_collection(txo, page_size * (page_num - 1), page_size) total_items = len(txo.claim.collection.claims.ids) - return {"items": items, 'total_pages': int((total_items + (page_size - 1)) / page_size), - 'total_items': total_items, 'page_size': page_size, 'page': page_num} + return { + "items": items, + "total_pages": int((total_items + (page_size - 1)) / page_size), + "total_items": total_items, + "page_size": page_size, + "page": page + } SUPPORT_DOC = """ Create, list and abandon all types of supports. diff --git a/lbry/lbry/extras/daemon/collection.py b/lbry/lbry/extras/daemon/collection.py deleted file mode 100644 index 2c48cfa7b..000000000 --- a/lbry/lbry/extras/daemon/collection.py +++ /dev/null @@ -1,366 +0,0 @@ -@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) -async def jsonrpc_collection_create( - self, name, bid, allow_duplicate_name=False, - channel_id=None, channel_name=None, channel_account_id=None, - account_id=None, wallet_id=None, claim_address=None, funding_account_ids=None, - preview=False, blocking=False, **kwargs): - """ - Create a new collection .... - - Usage: - collection_create (<name> | --name=<name>) (<bid> | --bid=<bid>) - ( --claims=<claimIds> ) - [--allow_duplicate_name=<allow_duplicate_name>] - [--title=<title>] [--description=<description>] - [--tags=<tags>...] [--languages=<languages>...] [--locations=<locations>...] - [--thumbnail_url=<thumbnail_url>] - [--account_id=<account_id>] [--wallet_id=<wallet_id>] - [--claim_address=<claim_address>] [--funding_account_ids=<funding_account_ids>...] - [--preview] [--blocking] - - Options: - --name=<name> : (str) name of the collection - --bid=<bid> : (decimal) amount to back the claim - --allow_duplicate_name=<allow_duplicate_name> : (bool) create new collection even if one already exists with - given name. default: false. - --claims=<claimIds> : (list) claim ids - --title=<title> : (str) title of the collection - --description=<description> : (str) description of the collection - --clear_languages : (bool) clear existing languages (prior to adding new ones) - --tags=<tags> : (list) content tags - --clear_languages : (bool) clear existing languages (prior to adding new ones) - --languages=<languages> : (list) languages used by the collection, - using RFC 5646 format, eg: - for English `--languages=en` - for Spanish (Spain) `--languages=es-ES` - for Spanish (Mexican) `--languages=es-MX` - for Chinese (Simplified) `--languages=zh-Hans` - for Chinese (Traditional) `--languages=zh-Hant` - --locations=<locations> : (list) locations of the collection, consisting of 2 letter - `country` code and a `state`, `city` and a postal - `code` along with a `latitude` and `longitude`. - for JSON RPC: pass a dictionary with aforementioned - attributes as keys, eg: - ... - "locations": [{'country': 'US', 'state': 'NH'}] - ... - for command line: pass a colon delimited list - with values in the following order: - - "COUNTRY:STATE:CITY:CODE:LATITUDE:LONGITUDE" - - making sure to include colon for blank values, for - example to provide only the city: - - ... --locations="::Manchester" - - with all values set: - - ... --locations="US:NH:Manchester:03101:42.990605:-71.460989" - - optionally, you can just pass the "LATITUDE:LONGITUDE": - - ... --locations="42.990605:-71.460989" - - finally, you can also pass JSON string of dictionary - on the command line as you would via JSON RPC - - ... --locations="{'country': 'US', 'state': 'NH'}" - - --thumbnail_url=<thumbnail_url>: (str) thumbnail url - # --cover_url=<cover_url> : (str) url of cover image - --account_id=<account_id> : (str) account to use for holding the transaction - --wallet_id=<wallet_id> : (str) restrict operation to specific wallet - --funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction - --claim_address=<claim_address>: (str) address where the collection is sent to, if not specified - it will be determined automatically from the account - --preview : (bool) do not broadcast the transaction - --blocking : (bool) wait until transaction is in mempool - - Returns: {Transaction} - """ - wallet = self.wallet_manager.get_wallet_or_default(wallet_id) - account = wallet.get_account_or_default(account_id) - funding_accounts = wallet.get_accounts_or_all(funding_account_ids) - self.valid_collection_name_or_error(name) - channel = await self.get_channel_or_none(wallet, channel_account_id, channel_id, channel_name, for_signing=True) - amount = self.get_dewies_or_error('bid', bid, positive_value=True) - claim_address = await self.get_receiving_address(claim_address, account) - kwargs['fee_address'] = self.get_fee_address(kwargs, claim_address) - - existing_collections = await self.ledger.get_collections(accounts=wallet.accounts, claim_name=name) - if len(existing_collections) > 0: - if not allow_duplicate_name: - raise Exception( - f"You already have a collection under the name '{name}'. " - f"Use --allow-duplicate-name flag to override." - ) - - claim = Claim() - claim.collection.update(**kwargs) #maybe specify claims=[] # here - tx = await Transaction.claim_create( - name, claim, amount, claim_address, funding_accounts, funding_accounts[0], channel - ) - new_txo = tx.outputs[0] - - if channel: - new_txo.sign(channel) - await tx.sign(funding_accounts) - if not preview: - await self.broadcast_or_release(tx, blocking) - await self.storage.save_claims([self._old_get_temp_claim_info( - tx, new_txo, claim_address, claim, name, dewies_to_lbc(amount) - )]) - # await self.analytics_manager.send_new_channel() - else: - await account.ledger.release_tx(tx) - - return tx - -@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) -async def jsonrpc_collection_update( - self, claim_id, bid=None, claim=None, allow_duplicate_name=False, - channel_id=None, channel_name=None, channel_account_id=None, clear_channel=False, - account_id=None, wallet_id=None, claim_address=None, funding_account_ids=None, - preview=False, blocking=False, replace=False, **kwargs): - """ - Update an existing collection claim. - - Usage: - collection_update (<claim_id> | --claim_id=<claim_id>) [<bid> | --bid=<bid>] - [--title=<title>] [--description=<description>] - [--tags=<tags>...] [--clear_tags] - [--languages=<languages>...] [--clear_languages] - [--locations=<locations>...] [--clear_locations] - [--thumbnail_url=<thumbnail_url>] [--cover_url=<cover_url>] - [--account_id=<account_id>] [--wallet_id=<wallet_id>] - [--claim_address=<claim_address>] [--new_signing_key] - [--funding_account_ids=<funding_account_ids>...] - [--preview] [--blocking] [--replace] - - Options: - --claim_id=<claim_id> : (str) claim_id of the collection to update - --bid=<bid> : (decimal) amount to back the claim - --claims=<claim_ids> : (list) claim ids - --clear_claims : (bool) clear existing claim references (prior to adding new ones) - --title=<title> : (str) title of the collection - --description=<description> : (str) description of the collection - --tags=<tags> : (list) add content tags - --clear_tags : (bool) clear existing tags (prior to adding new ones) - --languages=<languages> : (list) languages used by the collection, - using RFC 5646 format, eg: - for English `--languages=en` - for Spanish (Spain) `--languages=es-ES` - for Spanish (Mexican) `--languages=es-MX` - for Chinese (Simplified) `--languages=zh-Hans` - for Chinese (Traditional) `--languages=zh-Hant` - --clear_languages : (bool) clear existing languages (prior to adding new ones) - --locations=<locations> : (list) locations of the collection, consisting of 2 letter - `country` code and a `state`, `city` and a postal - `code` along with a `latitude` and `longitude`. - for JSON RPC: pass a dictionary with aforementioned - attributes as keys, eg: - ... - "locations": [{'country': 'US', 'state': 'NH'}] - ... - for command line: pass a colon delimited list - with values in the following order: - - "COUNTRY:STATE:CITY:CODE:LATITUDE:LONGITUDE" - - making sure to include colon for blank values, for - example to provide only the city: - - ... --locations="::Manchester" - - with all values set: - - ... --locations="US:NH:Manchester:03101:42.990605:-71.460989" - - optionally, you can just pass the "LATITUDE:LONGITUDE": - - ... --locations="42.990605:-71.460989" - - finally, you can also pass JSON string of dictionary - on the command line as you would via JSON RPC - - ... --locations="{'country': 'US', 'state': 'NH'}" - - --clear_locations : (bool) clear existing locations (prior to adding new ones) - --thumbnail_url=<thumbnail_url>: (str) thumbnail url - #--cover_url=<cover_url> : (str) url of cover image - --account_id=<account_id> : (str) account in which to look for collection (default: all) - --wallet_id=<wallet_id> : (str) restrict operation to specific wallet - --funding_account_ids=<funding_account_ids>: (list) ids of accounts to fund this transaction - --claim_address=<claim_address>: (str) address where the collection is sent - --new_signing_key : (bool) generate a new signing key, will invalidate all previous publishes - --preview : (bool) do not broadcast the transaction - --blocking : (bool) wait until transaction is in mempool - --replace : (bool) instead of modifying specific values on - the collection, this will clear all existing values - and only save passed in values, useful for form - submissions where all values are always set - - Returns: {Transaction} - """ - wallet = self.wallet_manager.get_wallet_or_default(wallet_id) - funding_accounts = wallet.get_accounts_or_all(funding_account_ids) - if account_id: - account = wallet.get_account_or_error(account_id) - accounts = [account] - else: - account = wallet.default_account - accounts = wallet.accounts - - existing_collections = await self.ledger.get_collections( - wallet=wallet, accounts=accounts, claim_id=claim_id - ) - if len(existing_collections) != 1: - account_ids = ', '.join(f"'{account.id}'" for account in accounts) - raise Exception( - f"Can't find the collection '{claim_id}' in account(s) {account_ids}." - ) - # Here we might have a problem of replacing a stream with a collection - old_txo = existing_collections[0] - if not old_txo.claim.is_collection: #as we're only checking @ or not, this is not definitive - raise Exception( - f"A claim with id '{claim_id}' was found but it is not a stream or collection." - ) - - if bid is not None: - amount = self.get_dewies_or_error('bid', bid, positive_value=True) - else: - amount = old_txo.amount - - if claim_address is not None: - self.valid_address_or_error(claim_address) - else: - claim_address = old_txo.get_address(account.ledger) - - channel = None - if channel_id or channel_name: - channel = await self.get_channel_or_error( - wallet, channel_account_id, channel_id, channel_name, for_signing=True) - elif old_txo.claim.is_signed and not clear_channel and not replace: - channel = old_txo.channel - - fee_address = self.get_fee_address(kwargs, claim_address) - if fee_address: - kwargs['fee_address'] = fee_address - - if replace: - claim = Claim() - claim.channel.public_key_bytes = old_txo.claim.channel.public_key_bytes - else: - claim = Claim.from_bytes(old_txo.claim.to_bytes()) - claim.channel.update(**kwargs) - tx = await Transaction.claim_update( - old_txo, claim, amount, claim_address, funding_accounts, funding_accounts[0] - ) - new_txo = tx.outputs[0] - - if new_signing_key: - new_txo.generate_channel_private_key() - else: - new_txo.private_key = old_txo.private_key - - new_txo.script.generate() - - if not preview: - await tx.sign(funding_accounts) - account.add_channel_private_key(new_txo.private_key) - wallet.save() - await self.broadcast_or_release(tx, blocking) - await self.storage.save_claims([self._old_get_temp_claim_info( - tx, new_txo, claim_address, new_txo.claim, new_txo.claim_name, dewies_to_lbc(amount) - )]) - await self.analytics_manager.send_new_channel() - else: - await account.ledger.release_tx(tx) - - return tx - -@requires(WALLET_COMPONENT, conditions=[WALLET_IS_UNLOCKED]) -async def jsonrpc_collection_abandon( - self, claim_id=None, txid=None, nout=None, account_id=None, wallet_id=None, - preview=False, blocking=True): - """ - Abandon one of my collection claims. - - Usage: - collection_abandon [<claim_id> | --claim_id=<claim_id>] - [<txid> | --txid=<txid>] [<nout> | --nout=<nout>] - [--account_id=<account_id>] [--wallet_id=<wallet_id>] - [--preview] [--blocking] - - Options: - --claim_id=<claim_id> : (str) claim_id of the claim to abandon - --txid=<txid> : (str) txid of the claim to abandon - --nout=<nout> : (int) nout of the claim to abandon - --account_id=<account_id> : (str) id of the account to use - --wallet_id=<wallet_id> : (str) restrict operation to specific wallet - --preview : (bool) do not broadcast the transaction - --blocking : (bool) wait until abandon is in mempool - - Returns: {Transaction} - """ - wallet = self.wallet_manager.get_wallet_or_default(wallet_id) - if account_id: - account = wallet.get_account_or_error(account_id) - accounts = [account] - else: - account = wallet.default_account - accounts = wallet.accounts - - if txid is not None and nout is not None: - claims = await self.ledger.get_claims( - wallet=wallet, accounts=accounts, **{'txo.txid': txid, 'txo.position': nout} - ) - elif claim_id is not None: - claims = await self.ledger.get_claims( - wallet=wallet, accounts=accounts, claim_id=claim_id - ) - else: - raise Exception('Must specify claim_id, or txid and nout') - - if not claims: - raise Exception('No claim found for the specified claim_id or txid:nout') - - tx = await Transaction.create( - [Input.spend(txo) for txo in claims], [], [account], account - ) - - if not preview: - await self.broadcast_or_release(tx, blocking) - await self.analytics_manager.send_claim_action('abandon') - else: - await account.ledger.release_tx(tx) - - return tx - -@requires(WALLET_COMPONENT) -def jsonrpc_collection_list(self, account_id=None, wallet_id=None, page=None, page_size=None): - """ - List my collection claims. - - Usage: - collection_list [<account_id> | --account_id=<account_id>] [--wallet_id=<wallet_id>] - [--page=<page>] [--page_size=<page_size>] - - Options: - --account_id=<account_id> : (str) id of the account to use - --wallet_id=<wallet_id> : (str) restrict results to specific wallet - --page=<page> : (int) page to return during paginating - --page_size=<page_size> : (int) number of items on page during pagination - - Returns: {Paginated[Output]} - """ - wallet = self.wallet_manager.get_wallet_or_default(wallet_id) - if account_id: - account: LBCAccount = wallet.get_account_or_error(account_id) - collections = account.get_collections - collection_count = account.get_collection_count - else: - collections = partial(self.ledger.get_collections, wallet=wallet, accounts=wallet.accounts) - collection_count = partial(self.ledger.get_collection_count, wallet=wallet, accounts=wallet.accounts) - return maybe_paginate(collections, collection_count, page, page_size) diff --git a/lbry/lbry/schema/attrs.py b/lbry/lbry/schema/attrs.py index 5e295d289..7586a7039 100644 --- a/lbry/lbry/schema/attrs.py +++ b/lbry/lbry/schema/attrs.py @@ -347,6 +347,10 @@ class ClaimList(BaseMessageList[ClaimReference]): __slots__ = () item_class = ClaimReference + @property + def _message(self): + return self.message.claim_references + def append(self, value): self.add().claim_id = value diff --git a/lbry/lbry/schema/claim.py b/lbry/lbry/schema/claim.py index 117caa09c..7150a547a 100644 --- a/lbry/lbry/schema/claim.py +++ b/lbry/lbry/schema/claim.py @@ -391,11 +391,10 @@ class Collection(BaseClaim): def to_dict(self): claim = super().to_dict() - if 'claim_references' in claim: + if claim.pop('claim_references', None): claim['claims'] = self.claims.ids - del claim['claim_references'] return claim @property def claims(self) -> ClaimList: - return ClaimList(self.message.claim_references) + return ClaimList(self.message) diff --git a/lbry/tests/integration/test_claim_commands.py b/lbry/tests/integration/test_claim_commands.py index a537b3ca4..43a658230 100644 --- a/lbry/tests/integration/test_claim_commands.py +++ b/lbry/tests/integration/test_claim_commands.py @@ -1275,6 +1275,7 @@ class CollectionCommands(CommandTestCase): collections = await self.out(self.daemon.jsonrpc_collection_list()) self.assertEqual(collections['items'][0]['value']['title'], 'boring title') self.assertEqual(collections['items'][0]['value']['claims'], claim_ids) + self.assertEqual(collections['items'][0]['value_type'], 'collection') self.assertItemCount(collections, 1) await self.assertBalance(self.account, '6.939679') @@ -1324,5 +1325,5 @@ class CollectionCommands(CommandTestCase): self.assertEqual(claims['items'][1]['name'], 'stream-two') self.assertEqual(claims['items'][2]['name'], 'stream-one') - - + claims = await self.out(self.daemon.jsonrpc_collection_resolve(claim_id2, page=10)) + self.assertEqual(claims['items'], [])