diff --git a/src/sakia/data/entities/__init__.py b/src/sakia/data/entities/__init__.py index a6ebca41552f7d64cd933e4c2f6a1e874dbb8c75..5e0ff2bb2c1fc4e67662af07b00c59a83ac2d217 100644 --- a/src/sakia/data/entities/__init__.py +++ b/src/sakia/data/entities/__init__.py @@ -1,2 +1,3 @@ from .identity import Identity from .community import Community +from .certification import Certification diff --git a/src/sakia/data/entities/certification.py b/src/sakia/data/entities/certification.py new file mode 100644 index 0000000000000000000000000000000000000000..2ed0495e3ca42b87f2cf76831328d842f10abf98 --- /dev/null +++ b/src/sakia/data/entities/certification.py @@ -0,0 +1,13 @@ +import attr +from duniterpy.documents import block_uid, BlockUID + + +@attr.s() +class Certification: + currency = attr.ib(convert=str) + certifier = attr.ib(convert=str) + certified = attr.ib(convert=str) + blockstamp = attr.ib(convert=block_uid) + timestamp = attr.ib(convert=int, cmp=False) + signature = attr.ib(convert=str, cmp=False) + written_on = attr.ib(convert=block_uid, default=BlockUID.empty(), cmp=False) \ No newline at end of file diff --git a/src/sakia/data/repositories/__init__.py b/src/sakia/data/repositories/__init__.py index eca82cc1959936da34fffaf56cbd9fd921a89ee6..2a4b6e60aa3faa28acb6c0f1cf9ff8cf7a5605fe 100644 --- a/src/sakia/data/repositories/__init__.py +++ b/src/sakia/data/repositories/__init__.py @@ -1,3 +1,4 @@ from .identities import IdentitiesRepo from .communities import CommunitiesRepo from .meta import MetaDatabase +from .certifications import CertificationsRepo \ No newline at end of file diff --git a/src/sakia/data/repositories/certifications.py b/src/sakia/data/repositories/certifications.py new file mode 100644 index 0000000000000000000000000000000000000000..3afa7b086426b2fbb3a4b593d206a4a8f3317df3 --- /dev/null +++ b/src/sakia/data/repositories/certifications.py @@ -0,0 +1,97 @@ +import attr + +from ..entities import Certification + + +@attr.s(frozen=True) +class CertificationsRepo: + """The repository for Communities entities. + """ + _conn = attr.ib() # :type sqlite3.Connection + _primary_keys = (Certification.currency, Certification.certified, + Certification.certifier, Certification.blockstamp,) + + def insert(self, certification): + """ + Commit a certification to the database + :param sakia.data.entities.Certification certification: the certification to commit + """ + with self._conn: + certification_tuple = attr.astuple(certification) + values = ",".join(['?'] * len(certification_tuple)) + self._conn.execute("INSERT INTO certifications VALUES ({0})".format(values), certification_tuple) + + def update(self, certification): + """ + Update an existing certification in the database + :param sakia.data.entities.Certification certification: the certification to update + """ + with self._conn: + updated_fields = attr.astuple(certification, filter=attr.filters.exclude(*CertificationsRepo._primary_keys)) + where_fields = attr.astuple(certification, filter=attr.filters.include(*CertificationsRepo._primary_keys)) + self._conn.execute("""UPDATE certifications SET + ts=?, + signature=?, + written_on=? + WHERE + currency=? AND + certifier=? AND + certified=? AND + blockstamp=?""", + updated_fields + where_fields) + + def get_one(self, **search): + """ + Get an existing certification in the database + :param dict search: the criterions of the lookup + :rtype: sakia.data.entities.Certification + """ + with self._conn: + filters = [] + values = [] + for k, v in search.items(): + filters.append("{k}=?".format(k=k)) + values.append(v) + + request = "SELECT * FROM certifications WHERE {filters}".format(filters=" AND ".join(filters)) + + c = self._conn.execute(request, tuple(values)) + data = c.fetchone() + if data: + return Certification(*data) + + def get_all(self, **search): + """ + Get all existing certification in the database corresponding to the search + :param dict search: the criterions of the lookup + :rtype: sakia.data.entities.Certification + """ + with self._conn: + filters = [] + values = [] + for k, v in search.items(): + value = v + filters.append("{key} = ?".format(key=k)) + values.append(value) + + request = "SELECT * FROM certifications WHERE {filters}".format(filters=" AND ".join(filters)) + + c = self._conn.execute(request, tuple(values)) + datas = c.fetchall() + if datas: + return [Certification(*data) for data in datas] + return [] + + def drop(self, certification): + """ + Drop an existing certification from the database + :param sakia.data.entities.Certification certification: the certification to update + """ + with self._conn: + where_fields = attr.astuple(certification, filter=attr.filters.include(*CertificationsRepo._primary_keys)) + self._conn.execute("""DELETE FROM certifications + WHERE + currency=? AND + certifier=? AND + certified=? AND + blockstamp=?""", where_fields) diff --git a/src/sakia/data/repositories/meta.sql b/src/sakia/data/repositories/meta.sql index 91f047aca1e81af80dcb137bc3377231464a4c0d..e82001c33b80f060ff242caedc4f681a15b3820e 100644 --- a/src/sakia/data/repositories/meta.sql +++ b/src/sakia/data/repositories/meta.sql @@ -35,3 +35,14 @@ CREATE TABLE IF NOT EXISTS communities( currency VARCHAR(30), PRIMARY KEY (currency) ); +-- IDENTITY TABLE +CREATE TABLE IF NOT EXISTS certifications( + currency VARCHAR(30), + certifier VARCHAR(50), + certified VARCHAR(50), + blockstamp VARCHAR(100), + ts INT, + signature VARCHAR(100), + written_on VARCHAR(100), + PRIMARY KEY (currency, certifier, certified, blockstamp) + ); diff --git a/src/sakia/tests/unit/data/test_certifications_repo.py b/src/sakia/tests/unit/data/test_certifications_repo.py new file mode 100644 index 0000000000000000000000000000000000000000..7254f29d260aeee063aaba0f6727a7b5d5b63061 --- /dev/null +++ b/src/sakia/tests/unit/data/test_certifications_repo.py @@ -0,0 +1,99 @@ +from sakia.data.repositories import CertificationsRepo, MetaDatabase +from sakia.data.entities import Certification +from duniterpy.documents import BlockUID +import unittest +import sqlite3 + + +class TestCertificationsRepo(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_certification(self): + meta_repo = MetaDatabase(self.con) + meta_repo.prepare() + meta_repo.upgrade_database() + certifications_repo = CertificationsRepo(self.con) + certifications_repo.insert(Certification("testcurrency", + "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", + 1473108382, + "H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw==", + None)) + certification = certifications_repo.get_one(currency="testcurrency", + certifier="7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + certified="FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + blockstamp=BlockUID(20, + "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") + ) + self.assertEqual(certification.currency, "testcurrency") + self.assertEqual(certification.certifier, "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ") + self.assertEqual(certification.certified, "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn") + self.assertEqual(certification.blockstamp.number, 20) + self.assertEqual(certification.blockstamp.sha_hash, "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") + self.assertEqual(certification.timestamp, 1473108382) + self.assertEqual(certification.signature, "H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw==") + self.assertEqual(certification.written_on, BlockUID.empty()) + certifications_repo.drop(certification) + certification = certifications_repo.get_one(currency="testcurrency", + certifier="7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + certified="FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + blockstamp=BlockUID(20, + "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") + ) + self.assertIsNone(certification) + + def test_add_get_multiple_certification(self): + meta_repo = MetaDatabase(self.con) + meta_repo.prepare() + meta_repo.upgrade_database() + certifications_repo = CertificationsRepo(self.con) + certifications_repo.insert(Certification("testcurrency", + "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", + 1473108382, + "H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw==", + "22-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67")) + certifications_repo.insert(Certification("testcurrency", + "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "101-BAD49448A1AD73C978CEDCB8F137D20A5715EBAA739DAEF76B1E28EE67B2C00C", + 1473108382, + "H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw==", + "105-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67")) + certifications = certifications_repo.get_all(currency="testcurrency") + self.assertIn("testcurrency", [i.currency for i in certifications]) + self.assertIn("FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", [i.certifier for i in certifications]) + self.assertIn("7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", [i.certifier for i in certifications]) + self.assertIn("FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", [i.certified for i in certifications]) + self.assertIn("7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", [i.certified for i in certifications]) + + def test_add_update_certification(self): + meta_repo = MetaDatabase(self.con) + meta_repo.prepare() + meta_repo.upgrade_database() + certifications_repo = CertificationsRepo(self.con) + certification = Certification("testcurrency", + "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + "20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", + 1473108382, + "H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw==", + None) + + certifications_repo.insert(certification) + certification.written_on = BlockUID(22, "148C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") + certifications_repo.update(certification) + cert2 = certifications_repo.get_one(currency="testcurrency", + certifier="7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + certified="FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + blockstamp="20-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") + self.assertTrue(cert2.written_on)