From b2c04d75c3a817601c0db3d60e3fce54e49e0e2b Mon Sep 17 00:00:00 2001 From: Vincent Texier <vit@free.fr> Date: Sat, 24 Sep 2016 11:26:50 +0200 Subject: [PATCH] merge community in blockchain repository add paginate and sort in get_all --- src/sakia/data/entities/__init__.py | 3 +- src/sakia/data/entities/blockchain.py | 38 +++++++ src/sakia/data/entities/community.py | 42 ------- src/sakia/data/processors/communities.py | 16 --- src/sakia/data/repositories/__init__.py | 1 - src/sakia/data/repositories/blockchains.py | 54 ++++++--- src/sakia/data/repositories/communities.py | 102 ----------------- src/sakia/data/repositories/meta.sql | 10 +- .../tests/unit/data/test_blockchains_repo.py | 107 ++++++++++++++++-- 9 files changed, 177 insertions(+), 196 deletions(-) delete mode 100644 src/sakia/data/entities/community.py delete mode 100644 src/sakia/data/processors/communities.py delete mode 100644 src/sakia/data/repositories/communities.py diff --git a/src/sakia/data/entities/__init__.py b/src/sakia/data/entities/__init__.py index 99cf36c9..40b3174b 100644 --- a/src/sakia/data/entities/__init__.py +++ b/src/sakia/data/entities/__init__.py @@ -1,6 +1,5 @@ from .identity import Identity -from .community import Community -from .blockchain import Blockchain +from .blockchain import Blockchain, BlockchainParameters from .certification import Certification from .transaction import Transaction from .node import Node diff --git a/src/sakia/data/entities/blockchain.py b/src/sakia/data/entities/blockchain.py index bdf7bf2c..72d44d38 100644 --- a/src/sakia/data/entities/blockchain.py +++ b/src/sakia/data/entities/blockchain.py @@ -2,8 +2,46 @@ import attr from duniterpy.documents import block_uid, BlockUID +@attr.s() +class BlockchainParameters: + # The decimal percent growth of the UD every [dt] period + c = attr.ib(convert=float, default=0, cmp=False) + # Time period between two UD in seconds + dt = attr.ib(convert=int, default=0, cmp=False) + # UD(0), i.e. initial Universal Dividend + ud0 = attr.ib(convert=int, default=0, cmp=False) + # Minimum delay between 2 certifications of a same issuer, in seconds. Must be positive or zero + sig_period = attr.ib(convert=int, default=0, cmp=False) + # Maximum quantity of active certifications made by member + sig_stock = attr.ib(convert=int, default=0, cmp=False) + # Maximum delay in seconds a certification can wait before being expired for non-writing + sig_window = attr.ib(convert=int, default=0, cmp=False) + # Maximum age of a active signature (in seconds) + sig_validity = attr.ib(convert=int, default=0, cmp=False) + # Minimum quantity of signatures to be part of the WoT + sig_qty = attr.ib(convert=int, default=0, cmp=False) + # Minimum decimal percent of sentries to reach to match the distance rule + xpercent = attr.ib(convert=float, default=0, cmp=False) + # Maximum age of an active membership( in seconds) + ms_validity = attr.ib(convert=int, default=0, cmp=False) + # Maximum distance between each WoT member and a newcomer + step_max = attr.ib(convert=int, default=0, cmp=False) + # Number of blocks used for calculating median time + median_time_blocks = attr.ib(convert=int, default=0, cmp=False) + # The average time for writing 1 block (wished time) in seconds + avg_gen_time = attr.ib(convert=int, default=0, cmp=False) + # The number of blocks required to evaluate again PoWMin value + dt_diff_eval = attr.ib(convert=int, default=0, cmp=False) + # The number of previous blocks to check for personalized difficulty + blocks_rot = attr.ib(convert=int, default=0, cmp=False) + # The decimal percent of previous issuers to reach for personalized difficulty + percent_rot = attr.ib(convert=float, default=0, cmp=False) + + @attr.s() class Blockchain: + # Parameters in block 0 + parameters = attr.ib(default=BlockchainParameters()) # block number and hash current_buid = attr.ib(convert=block_uid, default=BlockUID.empty()) # Number of members diff --git a/src/sakia/data/entities/community.py b/src/sakia/data/entities/community.py deleted file mode 100644 index 2e483c09..00000000 --- a/src/sakia/data/entities/community.py +++ /dev/null @@ -1,42 +0,0 @@ -import attr - - -@attr.s() -class Community: - # The decimal percent growth of the UD every [dt] period - c = attr.ib(convert=float, default=0, cmp=False) - # Time period between two UD in seconds - dt = attr.ib(convert=int, default=0, cmp=False) - # UD(0), i.e. initial Universal Dividend - ud0 = attr.ib(convert=int, default=0, cmp=False) - # Minimum delay between 2 certifications of a same issuer, in seconds. Must be positive or zero - sig_period = attr.ib(convert=int, default=0, cmp=False) - # Maximum quantity of active certifications made by member - sig_stock = attr.ib(convert=int, default=0, cmp=False) - # Maximum delay in seconds a certification can wait before being expired for non-writing - sig_window = attr.ib(convert=int, default=0, cmp=False) - # Maximum age of a active signature (in seconds) - sig_validity = attr.ib(convert=int, default=0, cmp=False) - # Minimum quantity of signatures to be part of the WoT - sig_qty = attr.ib(convert=int, default=0, cmp=False) - # Minimum decimal percent of sentries to reach to match the distance rule - xpercent = attr.ib(convert=float, default=0, cmp=False) - # Maximum age of an active membership( in seconds) - ms_validity = attr.ib(convert=int, default=0, cmp=False) - # Maximum distance between each WoT member and a newcomer - step_max = attr.ib(convert=int, default=0, cmp=False) - # Number of blocks used for calculating median time - median_time_blocks = attr.ib(convert=int, default=0, cmp=False) - # The average time for writing 1 block (wished time) in seconds - avg_gen_time = attr.ib(convert=int, default=0, cmp=False) - # The number of blocks required to evaluate again PoWMin value - dt_diff_eval = attr.ib(convert=int, default=0, cmp=False) - # The number of previous blocks to check for personalized difficulty - blocks_rot = attr.ib(convert=int, default=0, cmp=False) - # The decimal percent of previous issuers to reach for personalized difficulty - percent_rot = attr.ib(convert=float, default=0, cmp=False) - # Currency name - currency = attr.ib(convert=str, default="", cmp=False) - - - diff --git a/src/sakia/data/processors/communities.py b/src/sakia/data/processors/communities.py deleted file mode 100644 index b9ad62a4..00000000 --- a/src/sakia/data/processors/communities.py +++ /dev/null @@ -1,16 +0,0 @@ -import attr - - -@attr.s -class CommunityProcessor: - _repo = attr.ib() # :type sakia.data.repositories.CommunitiesRepo - - async def get_from_currency(self, currency): - """ - Get the community of a currency - - :param currency: - :rtype: sakia.data.entities.Community - """ - return self._repo.get_one(currency=currency) - diff --git a/src/sakia/data/repositories/__init__.py b/src/sakia/data/repositories/__init__.py index b047145e..a7e8f366 100644 --- a/src/sakia/data/repositories/__init__.py +++ b/src/sakia/data/repositories/__init__.py @@ -1,5 +1,4 @@ from .identities import IdentitiesRepo -from .communities import CommunitiesRepo from .blockchains import BlockchainsRepo from .meta import MetaDatabase from .certifications import CertificationsRepo diff --git a/src/sakia/data/repositories/blockchains.py b/src/sakia/data/repositories/blockchains.py index cede12dd..055deab2 100644 --- a/src/sakia/data/repositories/blockchains.py +++ b/src/sakia/data/repositories/blockchains.py @@ -1,6 +1,8 @@ +from typing import List + import attr -from ..entities import Blockchain +from ..entities import Blockchain, BlockchainParameters @attr.s(frozen=True) @@ -16,7 +18,8 @@ class BlockchainsRepo: :param sakia.data.entities.Blockchain blockchain: the blockchain to commit """ with self._conn: - blockchain_tuple = attr.astuple(blockchain) + blockchain_tuple = attr.astuple(blockchain.parameters) \ + + attr.astuple(blockchain, filter=attr.filters.exclude(Blockchain.parameters)) values = ",".join(['?'] * len(blockchain_tuple)) self._conn.execute("INSERT INTO blockchains VALUES ({0})".format(values), blockchain_tuple) @@ -26,7 +29,8 @@ class BlockchainsRepo: :param sakia.data.entities.Blockchain blockchain: the blockchain to update """ with self._conn: - updated_fields = attr.astuple(blockchain, filter=attr.filters.exclude(*BlockchainsRepo._primary_keys)) + updated_fields = attr.astuple(blockchain, filter=attr.filters.exclude( + Blockchain.parameters, *BlockchainsRepo._primary_keys)) where_fields = attr.astuple(blockchain, filter=attr.filters.include(*BlockchainsRepo._primary_keys)) self._conn.execute("""UPDATE blockchains SET current_buid=?, @@ -58,29 +62,49 @@ class BlockchainsRepo: c = self._conn.execute(request, tuple(values)) data = c.fetchone() if data: - return Blockchain(*data) + return Blockchain(BlockchainParameters(*data[:15]), *data[16:]) - def get_all(self, **search): + def get_all(self, offset=0, limit=1000, sort_by="currency", sort_order="ASC", **search) -> List[Blockchain]: """ Get all existing blockchain in the database corresponding to the search + :param int offset: offset in results to paginate + :param int limit: limit results to paginate + :param str sort_by: column name to sort by + :param str sort_order: sort order ASC or DESC :param dict search: the criterions of the lookup - :rtype: sakia.data.entities.Blockchain + :rtype: [sakia.data.entities.Blockchain] """ with self._conn: filters = [] values = [] - for k, v in search.items(): - operator = "LIKE" if k == "currency" else "=" - value = "%{value}%".format(value=v) if k == "currency" else v - filters.append("{key} {operator} ?".format(key=k, operator=operator)) - values.append(value) + if search: + for k, v in search.items(): + filters.append("{k}=?".format(k=k)) + values.append(v) - request = "SELECT * FROM blockchains WHERE {filters}".format(filters=" AND ".join(filters)) - - c = self._conn.execute(request, tuple(values)) + request = """SELECT * FROM blockchains WHERE {filters} + ORDER BY {sort_by} {sort_order} + LIMIT {limit} OFFSET {offset}""".format( + filters=" AND ".join(filters), + offset=offset, + limit=limit, + sort_by=sort_by, + sort_order=sort_order + ) + c = self._conn.execute(request, tuple(values)) + else: + request = """SELECT * FROM blockchains + ORDER BY {sort_by} {sort_order} + LIMIT {limit} OFFSET {offset}""".format( + offset=offset, + limit=limit, + sort_by=sort_by, + sort_order=sort_order + ) + c = self._conn.execute(request) datas = c.fetchall() if datas: - return [Blockchain(*data) for data in datas] + return [Blockchain(BlockchainParameters(*data[:15]), *data[16:]) for data in datas] return [] def drop(self, blockchain): diff --git a/src/sakia/data/repositories/communities.py b/src/sakia/data/repositories/communities.py deleted file mode 100644 index d2f21a1b..00000000 --- a/src/sakia/data/repositories/communities.py +++ /dev/null @@ -1,102 +0,0 @@ -import attr - -from ..entities import Community - - -@attr.s(frozen=True) -class CommunitiesRepo: - """The repository for Communities entities. - """ - _conn = attr.ib() # :type sqlite3.Connection - _primary_keys = (Community.currency,) - - def insert(self, community): - """ - Commit a community to the database - :param sakia.data.entities.Community community: the community to commit - """ - with self._conn: - community_tuple = attr.astuple(community) - values = ",".join(['?'] * len(community_tuple)) - self._conn.execute("INSERT INTO communities VALUES ({0})".format(values), community_tuple) - - def update(self, community): - """ - Update an existing community in the database - :param sakia.data.entities.Community community: the community to update - """ - with self._conn: - updated_fields = attr.astuple(community, filter=attr.filters.exclude(*CommunitiesRepo._primary_keys)) - where_fields = attr.astuple(community, filter=attr.filters.include(*CommunitiesRepo._primary_keys)) - self._conn.execute("""UPDATE communities SET - c=?, - dt=?, - ud0=?, - sig_period=?, - sig_stock=?, - sig_window=?, - sig_validity=?, - sig_qty=?, - xpercent=?, - ms_validity=?, - step_max=?, - median_time_blocks=?, - avg_gen_time=?, - dt_diff_eval=?, - blocks_rot=?, - percent_rot=? - WHERE - currency=?""", - updated_fields + where_fields) - - def get_one(self, **search): - """ - Get an existing community in the database - :param dict search: the criterions of the lookup - :rtype: sakia.data.entities.Community - """ - with self._conn: - filters = [] - values = [] - for k, v in search.items(): - filters.append("{k}=?".format(k=k)) - values.append(v) - - request = "SELECT * FROM communities WHERE {filters}".format(filters=" AND ".join(filters)) - - c = self._conn.execute(request, tuple(values)) - data = c.fetchone() - if data: - return Community(*data) - - def get_all(self, **search): - """ - Get all existing community in the database corresponding to the search - :param dict search: the criterions of the lookup - :rtype: sakia.data.entities.Community - """ - with self._conn: - filters = [] - values = [] - for k, v in search.items(): - operator = "LIKE" if k == "currency" else "=" - value = "%{value}%".format(value=v) if k == "currency" else v - filters.append("{key} {operator} ?".format(key=k, operator=operator)) - values.append(value) - - request = "SELECT * FROM communities WHERE {filters}".format(filters=" AND ".join(filters)) - - c = self._conn.execute(request, tuple(values)) - datas = c.fetchall() - if datas: - return [Community(*data) for data in datas] - return [] - - def drop(self, community): - """ - Drop an existing community from the database - :param sakia.data.entities.Community community: the community to update - """ - with self._conn: - where_fields = attr.astuple(community, filter=attr.filters.include(*CommunitiesRepo._primary_keys)) - self._conn.execute("DELETE FROM communities WHERE currency=?", where_fields) diff --git a/src/sakia/data/repositories/meta.sql b/src/sakia/data/repositories/meta.sql index 31e6af6c..f30c866d 100644 --- a/src/sakia/data/repositories/meta.sql +++ b/src/sakia/data/repositories/meta.sql @@ -16,8 +16,8 @@ CREATE TABLE IF NOT EXISTS identities( PRIMARY KEY (currency, pubkey, uid, blockstamp) ); --- COMMUNITIES TABLE -CREATE TABLE IF NOT EXISTS communities ( +-- BLOCKCHAIN TABLE +CREATE TABLE IF NOT EXISTS blockchains ( c FLOAT(1, 6), dt INT, ud0 INT, @@ -34,12 +34,6 @@ CREATE TABLE IF NOT EXISTS communities ( dt_diff_eval INT, blocks_rot INT, percent_rot FLOAT(1, 6), - currency VARCHAR(30), - PRIMARY KEY (currency) -); - --- BLOCKCHAIN TABLE -CREATE TABLE IF NOT EXISTS blockchains ( current_buid INT, nb_members INT, current_mass INT, diff --git a/src/sakia/tests/unit/data/test_blockchains_repo.py b/src/sakia/tests/unit/data/test_blockchains_repo.py index c52ef722..8f831fd0 100644 --- a/src/sakia/tests/unit/data/test_blockchains_repo.py +++ b/src/sakia/tests/unit/data/test_blockchains_repo.py @@ -3,7 +3,7 @@ import unittest from duniterpy.documents import BlockUID -from sakia.data.entities import Blockchain +from sakia.data.entities import Blockchain, BlockchainParameters from sakia.data.repositories import BlockchainsRepo, MetaDatabase @@ -23,6 +23,23 @@ class TestBlockchainsRepo(unittest.TestCase): meta_repo.upgrade_database() blockchains_repo = BlockchainsRepo(self.con) blockchains_repo.insert(Blockchain( + BlockchainParameters( + 0.1, + 86400, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66), "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 10, 1000000, @@ -33,6 +50,23 @@ class TestBlockchainsRepo(unittest.TestCase): "testcurrency" )) blockchain = blockchains_repo.get_one(currency="testcurrency") + self.assertEqual(blockchain.parameters, BlockchainParameters( + 0.1, + 86400, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66)) self.assertEqual(blockchain.currency, "testcurrency") self.assertEqual(blockchain.current_buid, BlockUID(20, "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") @@ -49,6 +83,23 @@ class TestBlockchainsRepo(unittest.TestCase): meta_repo.upgrade_database() blockchains_repo = BlockchainsRepo(self.con) blockchains_repo.insert(Blockchain( + BlockchainParameters( + 0.1, + 86400, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66), "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 10, 1000000, @@ -57,9 +108,25 @@ class TestBlockchainsRepo(unittest.TestCase): 0, 999999, "testcurrency" - ) - ) + )) blockchains_repo.insert(Blockchain( + BlockchainParameters( + 0.1, + 86400 * 365, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66), "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 20, 1000000, @@ -68,14 +135,17 @@ class TestBlockchainsRepo(unittest.TestCase): 0, 999999, "testcurrency2" - ) - ) + )) + + blockchains = blockchains_repo.get_all() + # result sorted by currency name by default + self.assertEquals(86400, blockchains[0].parameters.dt) + self.assertEquals("testcurrency", blockchains[0].currency) + self.assertEquals(10, blockchains[0].nb_members) - blockchains = blockchains_repo.get_all(currency="testcurrency") - self.assertIn("testcurrency", [i.currency for i in blockchains]) - self.assertIn("testcurrency2", [i.currency for i in blockchains]) - self.assertIn(10, [i.nb_members for i in blockchains]) - self.assertIn(20, [i.nb_members for i in blockchains]) + self.assertEquals(86400*365, blockchains[1].parameters.dt) + self.assertEquals("testcurrency2", blockchains[1].currency) + self.assertEquals(20, blockchains[1].nb_members) def test_add_update_blockchain(self): meta_repo = MetaDatabase(self.con) @@ -83,6 +153,23 @@ class TestBlockchainsRepo(unittest.TestCase): meta_repo.upgrade_database() blockchains_repo = BlockchainsRepo(self.con) blockchain = Blockchain( + BlockchainParameters( + 0.1, + 86400, + 100000, + 10800, + 40, + 2629800, + 31557600, + 1, + 0.9, + 604800, + 5, + 12, + 300, + 25, + 10, + 0.66), "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 10, 1000000, -- GitLab