From 7007425b85472f91270b56519d2ac920cbbc5d2d Mon Sep 17 00:00:00 2001
From: inso <insomniak.fr@gmaiL.com>
Date: Wed, 21 Sep 2016 04:10:11 +0200
Subject: [PATCH] Add certification entity

---
 src/sakia/data/entities/__init__.py           |  1 +
 src/sakia/data/entities/certification.py      | 13 +++
 src/sakia/data/repositories/__init__.py       |  1 +
 src/sakia/data/repositories/certifications.py | 97 ++++++++++++++++++
 src/sakia/data/repositories/meta.sql          | 11 +++
 .../unit/data/test_certifications_repo.py     | 99 +++++++++++++++++++
 6 files changed, 222 insertions(+)
 create mode 100644 src/sakia/data/entities/certification.py
 create mode 100644 src/sakia/data/repositories/certifications.py
 create mode 100644 src/sakia/tests/unit/data/test_certifications_repo.py

diff --git a/src/sakia/data/entities/__init__.py b/src/sakia/data/entities/__init__.py
index a6ebca41..5e0ff2bb 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 00000000..2ed0495e
--- /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 eca82cc1..2a4b6e60 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 00000000..3afa7b08
--- /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 91f047ac..e82001c3 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 00000000..7254f29d
--- /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)
-- 
GitLab