diff --git a/src/sakia/data/entities/__init__.py b/src/sakia/data/entities/__init__.py index 28edfe53662ce8a0f0d90de545a274fa37729afd..99cf36c9a513103de5abb7a6f2cd7fcf7b42a502 100644 --- a/src/sakia/data/entities/__init__.py +++ b/src/sakia/data/entities/__init__.py @@ -1,5 +1,6 @@ from .identity import Identity from .community import Community +from .blockchain import Blockchain 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 62e5b495947f0c6cb627a272cd2a2d32c2b94533..bdf7bf2c43e6fd6d0aa392fa48c2357a42e10e3a 100644 --- a/src/sakia/data/entities/blockchain.py +++ b/src/sakia/data/entities/blockchain.py @@ -4,11 +4,19 @@ from duniterpy.documents import block_uid, BlockUID @attr.s() class Blockchain: + # block number and hash current_buid = attr.ib(convert=block_uid, default=BlockUID.empty()) + # Number of members nb_members = attr.ib(convert=int, default=0, cmp=False) + # Current monetary mass in units current_mass = attr.ib(convert=int, default=0, cmp=False) + # Median time in seconds median_time = attr.ib(convert=int, default=0, cmp=False) + # Last UD amount in units (multiply by 10^base) last_ud = attr.ib(convert=int, default=0, cmp=False) + # Last UD base last_ud_base = attr.ib(convert=int, default=0, cmp=False) + # Previous monetary mass in units previous_mass = attr.ib(convert=int, default=0, cmp=False) + # Currency name currency = attr.ib(convert=str, default="", cmp=False) diff --git a/src/sakia/data/repositories/__init__.py b/src/sakia/data/repositories/__init__.py index b34c4a698b7ae020250e3c384e2099058df6f9c8..b047145e9178da572f0772ca381fef9ac3aec033 100644 --- a/src/sakia/data/repositories/__init__.py +++ b/src/sakia/data/repositories/__init__.py @@ -1,6 +1,7 @@ from .identities import IdentitiesRepo from .communities import CommunitiesRepo +from .blockchains import BlockchainsRepo from .meta import MetaDatabase from .certifications import CertificationsRepo from .transactions import TransactionsRepo -from .nodes import NodesRepo \ No newline at end of file +from .nodes import NodesRepo diff --git a/src/sakia/data/repositories/blockchains.py b/src/sakia/data/repositories/blockchains.py new file mode 100644 index 0000000000000000000000000000000000000000..cede12ddc233cc56b2162d6163c0cf9fea0653cd --- /dev/null +++ b/src/sakia/data/repositories/blockchains.py @@ -0,0 +1,93 @@ +import attr + +from ..entities import Blockchain + + +@attr.s(frozen=True) +class BlockchainsRepo: + """The repository for Blockchain entities. + """ + _conn = attr.ib() # :type sqlite3.Connection + _primary_keys = (Blockchain.currency,) + + def insert(self, blockchain): + """ + Commit a blockchain to the database + :param sakia.data.entities.Blockchain blockchain: the blockchain to commit + """ + with self._conn: + blockchain_tuple = attr.astuple(blockchain) + values = ",".join(['?'] * len(blockchain_tuple)) + self._conn.execute("INSERT INTO blockchains VALUES ({0})".format(values), blockchain_tuple) + + def update(self, blockchain): + """ + Update an existing blockchain in the database + :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)) + where_fields = attr.astuple(blockchain, filter=attr.filters.include(*BlockchainsRepo._primary_keys)) + self._conn.execute("""UPDATE blockchains SET + current_buid=?, + nb_members=?, + current_mass=?, + median_time=?, + last_ud=?, + last_ud_base=?, + previous_mass=? + WHERE + currency=?""", + updated_fields + where_fields) + + def get_one(self, **search): + """ + Get an existing blockchain in the database + :param dict search: the criterions of the lookup + :rtype: sakia.data.entities.Blockchain + """ + with self._conn: + filters = [] + values = [] + 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)) + data = c.fetchone() + if data: + return Blockchain(*data) + + def get_all(self, **search): + """ + Get all existing blockchain in the database corresponding to the search + :param dict search: the criterions of the lookup + :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) + + request = "SELECT * FROM blockchains WHERE {filters}".format(filters=" AND ".join(filters)) + + c = self._conn.execute(request, tuple(values)) + datas = c.fetchall() + if datas: + return [Blockchain(*data) for data in datas] + return [] + + def drop(self, blockchain): + """ + Drop an existing blockchain from the database + :param sakia.data.entities.Blockchain blockchain: the blockchain to update + """ + with self._conn: + where_fields = attr.astuple(blockchain, filter=attr.filters.include(*BlockchainsRepo._primary_keys)) + self._conn.execute("DELETE FROM blockchains WHERE currency=?", where_fields) diff --git a/src/sakia/data/repositories/meta.sql b/src/sakia/data/repositories/meta.sql index 0174946638dea982ed02f4fcdbeb0d118d13450a..31e6af6c1431bd1637d8a1cd05605a9b8f52422e 100644 --- a/src/sakia/data/repositories/meta.sql +++ b/src/sakia/data/repositories/meta.sql @@ -16,27 +16,40 @@ CREATE TABLE IF NOT EXISTS identities( PRIMARY KEY (currency, pubkey, uid, blockstamp) ); --- COMMUNITY TABLE -CREATE TABLE IF NOT EXISTS communities( - c FLOAT(1,6), - dt INT, - ud0 INT, - sig_period INT, - sig_stock INT, - sig_window INT, - sig_validity INT, - sig_qty INT, - xpercent FLOAT(1,6), - ms_validity INT, - step_max INT, - median_time_blocks INT, - avg_gen_time INT, - dt_diff_eval INT, - blocks_rot INT, - percent_rot FLOAT(1,6), - currency VARCHAR(30), - PRIMARY KEY (currency) - ); +-- COMMUNITIES TABLE +CREATE TABLE IF NOT EXISTS communities ( + c FLOAT(1, 6), + dt INT, + ud0 INT, + sig_period INT, + sig_stock INT, + sig_window INT, + sig_validity INT, + sig_qty INT, + xpercent FLOAT(1, 6), + ms_validity INT, + step_max INT, + median_time_blocks INT, + avg_gen_time INT, + 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, + median_time INT, + last_ud INT, + last_ud_base INT, + previous_mass INT, + currency VARCHAR(30), + PRIMARY KEY (currency) +); -- CERTIFICATIONS TABLE diff --git a/src/sakia/tests/unit/data/test_blockchains_repo.py b/src/sakia/tests/unit/data/test_blockchains_repo.py new file mode 100644 index 0000000000000000000000000000000000000000..c52ef72229109bc5355753d8ed1abe4148b69746 --- /dev/null +++ b/src/sakia/tests/unit/data/test_blockchains_repo.py @@ -0,0 +1,99 @@ +import sqlite3 +import unittest + +from duniterpy.documents import BlockUID + +from sakia.data.entities import Blockchain +from sakia.data.repositories import BlockchainsRepo, MetaDatabase + + +class TestBlockchainsRepo(unittest.TestCase): + def setUp(self): + sqlite3.register_adapter(BlockUID, str) + sqlite3.register_adapter(bool, int) + sqlite3.register_converter("BOOLEAN", lambda v: bool(int(v))) + self.con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES) + + def tearDown(self): + self.con.close() + + def test_add_get_drop_blockchain(self): + meta_repo = MetaDatabase(self.con) + meta_repo.prepare() + meta_repo.upgrade_database() + blockchains_repo = BlockchainsRepo(self.con) + blockchains_repo.insert(Blockchain( + "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", + 10, + 1000000, + 86400, + 100000, + 0, + 999999, + "testcurrency" + )) + blockchain = blockchains_repo.get_one(currency="testcurrency") + self.assertEqual(blockchain.currency, "testcurrency") + self.assertEqual(blockchain.current_buid, BlockUID(20, + "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") + ) + self.assertEqual(blockchain.nb_members, 10) + + blockchains_repo.drop(blockchain) + blockchain = blockchains_repo.get_one(currency="testcurrency") + self.assertIsNone(blockchain) + + def test_add_get_multiple_blockchain(self): + meta_repo = MetaDatabase(self.con) + meta_repo.prepare() + meta_repo.upgrade_database() + blockchains_repo = BlockchainsRepo(self.con) + blockchains_repo.insert(Blockchain( + "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", + 10, + 1000000, + 86400, + 100000, + 0, + 999999, + "testcurrency" + ) + ) + blockchains_repo.insert(Blockchain( + "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", + 20, + 1000000, + 86400, + 100000, + 0, + 999999, + "testcurrency2" + ) + ) + + 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]) + + def test_add_update_blockchain(self): + meta_repo = MetaDatabase(self.con) + meta_repo.prepare() + meta_repo.upgrade_database() + blockchains_repo = BlockchainsRepo(self.con) + blockchain = Blockchain( + "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", + 10, + 1000000, + 86400, + 100000, + 0, + 999999, + "testcurrency" + ) + blockchains_repo.insert(blockchain) + blockchain.nb_members = 30 + blockchains_repo.update(blockchain) + blockchain2 = blockchains_repo.get_one(currency="testcurrency") + self.assertEquals(30, blockchain2.nb_members) diff --git a/src/sakia/tests/unit/data/test_communities_repo.py b/src/sakia/tests/unit/data/test_communities_repo.py index 5a0d72f3b93c751b5b06ed3b865aa1c3ac39227d..fb99e2adff8ca0a727f79bb2da244d35ca35138a 100644 --- a/src/sakia/tests/unit/data/test_communities_repo.py +++ b/src/sakia/tests/unit/data/test_communities_repo.py @@ -7,7 +7,7 @@ from sakia.data.entities import Community from sakia.data.repositories import CommunitiesRepo, MetaDatabase -class TestIdentitiesRepo(unittest.TestCase): +class TestCommunitiesRepo(unittest.TestCase): def setUp(self): sqlite3.register_adapter(BlockUID, str) sqlite3.register_adapter(bool, int)