From 5e8f718f393e4bde5764c42a87641d0d66b34e99 Mon Sep 17 00:00:00 2001
From: inso <insomniak.fr@gmaiL.com>
Date: Wed, 26 Oct 2016 07:25:39 +0200
Subject: [PATCH] Network component

---
 src/sakia/data/connectors/node.py             |  1 +
 src/sakia/data/entities/node.py               |  2 +
 src/sakia/data/repositories/meta.sql          |  1 +
 src/sakia/data/repositories/nodes.py          |  2 +-
 src/sakia/gui/navigation/model.py             |  1 +
 .../gui/navigation/network/controller.py      |  5 +-
 src/sakia/gui/navigation/network/model.py     | 30 ++++---
 .../gui/navigation/network/table_model.py     | 89 +++++++------------
 src/sakia/services/network.py                 | 12 ++-
 9 files changed, 70 insertions(+), 73 deletions(-)

diff --git a/src/sakia/data/connectors/node.py b/src/sakia/data/connectors/node.py
index 53b648f4..8539d7eb 100644
--- a/src/sakia/data/connectors/node.py
+++ b/src/sakia/data/connectors/node.py
@@ -230,6 +230,7 @@ class NodeConnector(QObject):
                     self._logger.debug("Error in previous block reply of {0} : {1}".format(self.node.pubkey[:5], str(e)))
                 finally:
                     self.node.current_buid = BlockUID(block_data['number'], block_data['hash'])
+                    self.node.current_time = block_data['medianTime']
                     self._logger.debug("Changed block {0} -> {1}".format(self.node.current_buid.number,
                                                                     block_data['number']))
                     self.changed.emit()
diff --git a/src/sakia/data/entities/node.py b/src/sakia/data/entities/node.py
index 27126a3a..e89d9a44 100644
--- a/src/sakia/data/entities/node.py
+++ b/src/sakia/data/entities/node.py
@@ -59,6 +59,8 @@ class Node:
     uid = attr.ib(convert=str, cmp=False, default="")
     # The current block uid in /blockchain/current
     current_buid = attr.ib(convert=block_uid, cmp=False, default=None)
+    # The current block time in /blockchain/current
+    current_time = attr.ib(convert=int, cmp=False, default=0)
     # The previous block uid in /blockchain/current
     previous_buid = attr.ib(convert=block_uid, cmp=False, default=None)
     # The state of the node in Sakia
diff --git a/src/sakia/data/repositories/meta.sql b/src/sakia/data/repositories/meta.sql
index b77c0d47..6ee5f8a0 100644
--- a/src/sakia/data/repositories/meta.sql
+++ b/src/sakia/data/repositories/meta.sql
@@ -91,6 +91,7 @@ CREATE TABLE IF NOT EXISTS nodes(
                                peer_buid            VARCHAR(100),
                                uid                  VARCHAR(50),
                                current_buid         VARCHAR(100),
+                               current_time         INT,
                                previous_buid        VARCHAR(100),
                                state                INT,
                                software             VARCHAR(100),
diff --git a/src/sakia/data/repositories/nodes.py b/src/sakia/data/repositories/nodes.py
index e7b2a06d..99a1ccd3 100644
--- a/src/sakia/data/repositories/nodes.py
+++ b/src/sakia/data/repositories/nodes.py
@@ -18,7 +18,7 @@ class NodesRepo:
         with self._conn:
             node_tuple = attr.astuple(node, tuple_factory=list)
             node_tuple[2] = "\n".join([str(n) for n in node_tuple[2]])
-            node_tuple[11] = "\n".join([str(n) for n in node_tuple[11]])
+            node_tuple[12] = "\n".join([str(n) for n in node_tuple[11]])
             values = ",".join(['?'] * len(node_tuple))
             self._conn.execute("INSERT INTO nodes VALUES ({0})".format(values), node_tuple)
 
diff --git a/src/sakia/gui/navigation/model.py b/src/sakia/gui/navigation/model.py
index 39ce6ccf..794f2b1e 100644
--- a/src/sakia/gui/navigation/model.py
+++ b/src/sakia/gui/navigation/model.py
@@ -55,6 +55,7 @@ class NavigationModel(ComponentModel):
                             'title': self.tr('Network'),
                             'icon': ':/icons/network_icon',
                             'component': "Network",
+                            'network_service': self.app.network_services[connection.currency],
                         }
                     },
             #        {
diff --git a/src/sakia/gui/navigation/network/controller.py b/src/sakia/gui/navigation/network/controller.py
index 65145dc4..3647fef6 100644
--- a/src/sakia/gui/navigation/network/controller.py
+++ b/src/sakia/gui/navigation/network/controller.py
@@ -26,11 +26,10 @@ class NetworkController(ComponentController):
 
     @classmethod
     def create(cls, parent, app, **kwargs):
-        account = kwargs['account']
-        community = kwargs['community']
+        network_service = kwargs['network_service']
 
         view = NetworkView(parent.view)
-        model = NetworkModel(None, app, account, community)
+        model = NetworkModel(None, app, network_service)
         txhistory = cls(parent, view, model)
         model.setParent(txhistory)
         return txhistory
diff --git a/src/sakia/gui/navigation/network/model.py b/src/sakia/gui/navigation/network/model.py
index f395bfda..1dfd1f0f 100644
--- a/src/sakia/gui/navigation/network/model.py
+++ b/src/sakia/gui/navigation/network/model.py
@@ -8,23 +8,21 @@ class NetworkModel(ComponentModel):
     A network model
     """
 
-    def __init__(self, parent, app, account, community):
+    def __init__(self, parent, app, network_service):
         """
         Constructor of an network model
 
         :param sakia.gui.network.controller.NetworkController parent: the controller
-        :param sakia.core.Application app: the app
-        :param sakia.core.Account account: the account
-        :param sakia.core.Community community: the community
+        :param sakia.app.Application app: the app
+        :param sakia.services.NetworkService network_service: the service handling network state
         """
         super().__init__(parent)
         self.app = app
-        self.account = account
-        self.community = community
+        self.network_service = network_service
         self.table_model = None
 
     def init_network_table_model(self):
-        model = NetworkTableModel(self.community)
+        model = NetworkTableModel(self.network_service)
         proxy = NetworkFilterProxyModel()
         proxy.setSourceModel(model)
         self.table_model = proxy
@@ -36,7 +34,7 @@ class NetworkModel(ComponentModel):
         Start the refresh of the nodes
         :return:
         """
-        self.community.network.refresh_once()
+        self.network_service.refresh_once()
 
     def table_model_data(self, index):
         """
@@ -49,13 +47,23 @@ class NetworkModel(ComponentModel):
             is_root_col = self.table_model.sourceModel().columns_types.index('is_root')
             is_root_index = self.table_model.sourceModel().index(source_index.row(), is_root_col)
             is_root = self.table_model.sourceModel().data(is_root_index, Qt.DisplayRole)
-            node = self.community.network.nodes(source_index.row())
+            node = self.community.network.nodes()[source_index.row()]
             return True, node, is_root
         return False, None, None
 
     def add_root_node(self, node):
-        self.community.network.add_root_node(node)
+        """
+        The node set as root
+        :param sakia.data.entities.Node node: the node
+        """
+        node.root = True
+        self.network_service.commit_node(node)
 
     def unset_root_node(self, node):
-        self.community.network.remove_root_node(node)
+        """
+        The node set as root
+        :param sakia.data.entities.Node node: the node
+        """
+        node.root = False
+        self.network_service.commit_node(node)
 
diff --git a/src/sakia/gui/navigation/network/table_model.py b/src/sakia/gui/navigation/network/table_model.py
index f2fca71f..de931319 100644
--- a/src/sakia/gui/navigation/network/table_model.py
+++ b/src/sakia/gui/navigation/network/table_model.py
@@ -18,25 +18,10 @@ from sakia.decorators import asyncify, once_at_a_time
 class NetworkFilterProxyModel(QSortFilterProxyModel):
     def __init__(self, parent=None):
         super().__init__(parent)
-        self.community = None
 
     def columnCount(self, parent):
         return self.sourceModel().columnCount(None) - 2
 
-    def change_community(self, community):
-        """
-        Change current community and returns refresh task
-        :param sakia.core.Community community:
-        :return: the refresh task
-        :rtype: asyncio.Task
-        """
-        self.community = community
-        return self.sourceModel().change_community(community)
-
-    def setSourceModel(self, sourceModel):
-        self.community = sourceModel.community
-        super().setSourceModel(sourceModel)
-
     def lessThan(self, left, right):
         """
         Sort table by given column number.
@@ -95,13 +80,6 @@ class NetworkFilterProxyModel(QSortFilterProxyModel):
             if index.column() == source_model.columns_types.index('current_hash') :
                 return source_data[:10]
 
-            if index.column() == source_model.columns_types.index('current_time') and source_data:
-                return QLocale.toString(
-                            QLocale(),
-                            QDateTime.fromTime_t(source_data),
-                            QLocale.dateTimeFormat(QLocale(), QLocale.ShortFormat)
-                        )
-
         if role == Qt.TextAlignmentRole:
             if source_index.column() == source_model.columns_types.index('address') or source_index.column() == self.sourceModel().columns_types.index('current_block'):
                 return Qt.AlignRight | Qt.AlignVCenter
@@ -124,18 +102,19 @@ class NetworkTableModel(QAbstractTableModel):
     A Qt abstract item model to display
     """
 
-    def __init__(self, community, parent=None):
+    def __init__(self, network_service, parent=None):
         """
-        Constructor
+        The table showing nodes
+        :param sakia.services.NetworkService network_service:
+        :param parent:
         """
         super().__init__(parent)
-        self.community = community
+        self.network_service = network_service
         self.columns_types = (
             'address',
             'port',
             'current_block',
             'current_hash',
-            'current_time',
             'uid',
             'is_member',
             'pubkey',
@@ -163,48 +142,44 @@ class NetworkTableModel(QAbstractTableModel):
             Node.CORRUPTED: lambda: self.tr('Corrupted')
         }
         self.nodes_data = []
-        self.community.network.nodes_changed.connect(self.refresh_nodes)
+        self.network_service.nodes_changed.connect(self.refresh_nodes)
 
-    async def data_node(self, node: Node) -> tuple:
+    def data_node(self, node: Node) -> tuple:
         """
         Return node data tuple
         :param ..core.net.node.Node node: Network node
         :return:
         """
-        try:
-            members_pubkey = await self.community.members_pubkeys()
-            is_member = node.pubkey in members_pubkey
-        except NoPeerAvailable as e:
-            logging.error(e)
-            is_member = None
-
-        address = ""
-        if node.endpoint.server:
-            address = node.endpoint.server
-        elif node.endpoint.ipv4:
-            address = node.endpoint.ipv4
-        elif node.endpoint.ipv6:
-            address = node.endpoint.ipv6
-        port = node.endpoint.port
-
-        is_root = self.community.network.is_root_node(node)
-        if node.block:
-            number, block_hash, block_time = node.block['number'], node.block['hash'], node.block['medianTime']
+        is_member = False
+
+        addresses = []
+        ports = []
+        for e in node.endpoints:
+            if e.server:
+                addresses.append(e.server)
+            elif e.ipv4:
+                addresses.append(e.ipv4)
+            elif e.ipv6:
+                addresses.append(e.ipv6)
+            ports.append(str(e.port))
+        address = "\n".join(addresses)
+        port = "\n".join(ports)
+
+        is_root = node.root
+        if node.current_buid:
+            number, block_hash = node.current_buid.number, node.current_buid.hash
         else:
-            number, block_hash, block_time = "", "", ""
-        return (address, port, number, block_hash, block_time, node.uid,
-                is_member, node.pubkey, node.software, node.version, is_root, node.state)
+            number, block_hash = "", ""
+        return (address, port, number, block_hash, node.uid,
+                is_member, node.pubkey, node.software, node.version, node.root, node.state)
 
-    @once_at_a_time
-    @asyncify
-    async def refresh_nodes(self):
+    def refresh_nodes(self):
         self.beginResetModel()
         self.nodes_data = []
         nodes_data = []
-        if self.community:
-            for node in self.community.network.nodes:
-                data = await self.data_node(node)
-                nodes_data.append(data)
+        for node in self.network_service.nodes():
+            data = self.data_node(node)
+            nodes_data.append(data)
         self.nodes_data = nodes_data
         self.endResetModel()
 
diff --git a/src/sakia/services/network.py b/src/sakia/services/network.py
index 0e200a03..bdb0da83 100644
--- a/src/sakia/services/network.py
+++ b/src/sakia/services/network.py
@@ -83,6 +83,16 @@ class NetworkService(QObject):
         """
         asyncio.ensure_future(self.discover_network())
 
+    def nodes(self):
+        """
+        Get all nodes
+        :return:
+        """
+        return self._processor.nodes(self.currency)
+
+    def commit_node(self, node):
+        self._processor.commit_node(node)
+
     async def stop_coroutines(self, closing=False):
         """
         Stop network nodes crawling.
@@ -227,7 +237,7 @@ class NetworkService(QObject):
         if node.state in (Node.OFFLINE, Node.CORRUPTED) and \
                                 node.last_change + 3600 < time.time():
             node.disconnect()
-            self.nodes.remove(node)
+            self._processor.delete_node(node)
             self.nodes_changed.emit()
 
     @pyqtSlot()
-- 
GitLab