From b563e8bd60554105ce923591d19ea0e1b712ceaa Mon Sep 17 00:00:00 2001
From: Inso <insomniak.fr@gmail.com>
Date: Fri, 27 Feb 2015 08:40:52 +0100
Subject: [PATCH] Refactoring with adding watchers threads to the core module

---
 src/cutecoin/core/account.py              |  8 +---
 src/cutecoin/core/community.py            | 10 ++---
 src/cutecoin/core/net/network.py          | 35 ++++++++++++++++-
 src/cutecoin/core/net/node.py             |  7 +++-
 src/cutecoin/core/watchers/__init__.py    |  0
 src/cutecoin/core/watchers/blockchain.py  | 46 +++++++++++++++++++++++
 src/cutecoin/core/watchers/nodes.py       | 17 +++++++++
 src/cutecoin/gui/currency_tab.py          | 46 ++---------------------
 src/cutecoin/gui/process_cfg_community.py |  2 +-
 src/cutecoin/models/network.py            | 14 ++++++-
 10 files changed, 126 insertions(+), 59 deletions(-)
 create mode 100644 src/cutecoin/core/watchers/__init__.py
 create mode 100644 src/cutecoin/core/watchers/blockchain.py
 create mode 100644 src/cutecoin/core/watchers/nodes.py

diff --git a/src/cutecoin/core/account.py b/src/cutecoin/core/account.py
index 8bb903d0..bca78280 100644
--- a/src/cutecoin/core/account.py
+++ b/src/cutecoin/core/account.py
@@ -104,12 +104,8 @@ class Account(QObject):
         communities = []
         dead_communities = []
         for data in json_data['communities']:
-            try:
-                community = Community.load(data)
-                communities.append(community)
-            except NoPeerAvailable:
-                community = Community.without_network(data)
-                dead_communities.append(community)
+            community = Community.load(data)
+            communities.append(community)
 
         account = cls(salt, pubkey, name, communities, wallets,
                       contacts, dead_communities)
diff --git a/src/cutecoin/core/community.py b/src/cutecoin/core/community.py
index c872cf37..78c29b39 100644
--- a/src/cutecoin/core/community.py
+++ b/src/cutecoin/core/community.py
@@ -5,8 +5,6 @@ Created on 1 févr. 2014
 '''
 
 from ucoinpy.api import bma
-from ucoinpy import PROTOCOL_VERSION
-from ucoinpy.documents.peer import Peer, Endpoint, BMAEndpoint
 from ucoinpy.documents.block import Block
 from ..tools.exceptions import NoPeerAvailable
 from .net.node import Node
@@ -15,7 +13,7 @@ import logging
 import inspect
 import hashlib
 import re
-from requests.exceptions import RequestException, Timeout
+from requests.exceptions import RequestException
 
 
 class Cache():
@@ -90,9 +88,6 @@ class Community(object):
     def create(cls, currency, peer):
         community = cls(currency, [peer])
         logging.debug("Creating community")
-        community.peers = community.peering()
-        logging.debug("{0} peers found".format(len(community.peers)))
-        logging.debug([peer.pubkey for peer in community.peers])
         return community
 
     @classmethod
@@ -174,6 +169,9 @@ class Community(object):
     def nodes(self):
         return self._network.all_nodes
 
+    def add_peer(self, peer):
+        self._network.add_node(Node.from_peer(peer))
+
     def get_block(self, number=None):
         if number is None:
             data = self.request(bma.blockchain.Current)
diff --git a/src/cutecoin/core/net/network.py b/src/cutecoin/core/net/network.py
index 07d4af18..22d31623 100644
--- a/src/cutecoin/core/net/network.py
+++ b/src/cutecoin/core/net/network.py
@@ -12,7 +12,7 @@ from .node import Node
 import logging
 import time
 
-from PyQt5.QtCore import QObject, pyqtSignal
+from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
 
 
 class Network(QObject):
@@ -25,8 +25,12 @@ class Network(QObject):
         '''
         Constructor
         '''
+        super().__init__()
         self.currency = currency
         self._nodes = nodes
+        for n in self._nodes:
+            n.changed.connect(self.nodes_changed)
+        self.must_crawl = False
         #TODO: Crawl nodes at startup
 
     @classmethod
@@ -40,12 +44,30 @@ class Network(QObject):
             node.check_sync(currency, block_max)
         return cls(currency, nodes)
 
+    @classmethod
+    def create(cls, currency, node):
+        nodes = [node]
+        network = cls(currency, nodes)
+        nodes = network.crawling
+        block_max = max([n.block for n in nodes])
+        for node in nodes:
+            node.check_sync(currency, block_max)
+        network._nodes = nodes
+        return network
+
     def jsonify(self):
         data = []
         for node in self.nodes:
             data.append(node.jsonify())
         return data
 
+    def __del__(self):
+        self.must_crawl = False
+
+    @pyqtSlot()
+    def stop_crawling(self):
+        self.must_crawl = False
+
     @property
     def online_nodes(self):
         return [n for n in self._nodes if n.state == Node.ONLINE]
@@ -54,9 +76,18 @@ class Network(QObject):
     def all_nodes(self):
         return self._nodes.copy()
 
+    def add_nodes(self, node):
+        self._nodes.append(node)
+        node.changed.connect(self.nodes_changed)
+
+    @pyqtSlot()
     def perpetual_crawling(self):
+        self.must_crawl = True
         while self.must_crawl:
-            self.crawling(interval=10)
+            self.nodes = self.crawling(interval=10)
+            self.nodes_changed.disconnect()
+            for n in self._nodes:
+                n.changed.connect(self.nodes_changed)
 
     def crawling(self, interval=0):
         nodes = []
diff --git a/src/cutecoin/core/net/node.py b/src/cutecoin/core/net/node.py
index 340c7342..c8b1d6aa 100644
--- a/src/cutecoin/core/net/node.py
+++ b/src/cutecoin/core/net/node.py
@@ -12,8 +12,10 @@ from ...tools.exceptions import PersonNotFoundError
 import logging
 import time
 
+from PyQt5.QtCore import QObject, pyqtSignal
 
-class Node(object):
+
+class Node(QObject):
     '''
     classdocs
     '''
@@ -23,10 +25,13 @@ class Node(object):
     DESYNCED = 3
     CORRUPTED = 4
 
+    changed = pyqtSignal()
+
     def __init__(self, endpoints, pubkey, block, state):
         '''
         Constructor
         '''
+        super().__init__()
         self._endpoints = endpoints
         self._pubkey = pubkey
         self._block = block
diff --git a/src/cutecoin/core/watchers/__init__.py b/src/cutecoin/core/watchers/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/cutecoin/core/watchers/blockchain.py b/src/cutecoin/core/watchers/blockchain.py
new file mode 100644
index 00000000..b2f6861f
--- /dev/null
+++ b/src/cutecoin/core/watchers/blockchain.py
@@ -0,0 +1,46 @@
+'''
+Created on 27 févr. 2015
+
+@author: inso
+'''
+
+from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal
+import logging
+import time
+from requests.exceptions import RequestException
+from ...tools.exceptions import NoPeerAvailable
+
+
+class BlockchainWatcher(QObject):
+    def __init__(self, account, community):
+        super().__init__()
+        self.account = account
+        self.community = community
+        self.time_to_wait = int(self.community.get_parameters()['avgGenTime'] / 10)
+        self.exiting = False
+        blockid = self.community.current_blockid()
+        self.last_block = blockid['number']
+
+    @pyqtSlot()
+    def watch(self):
+        while not self.exiting:
+            time.sleep(self.time_to_wait)
+            try:
+                blockid = self.community.current_blockid()
+                block_number = blockid['number']
+                if self.last_block != block_number:
+                    self.community.refresh_cache()
+                    for w in self.account.wallets:
+                        w.refresh_cache(self.community)
+
+                    logging.debug("New block, {0} mined in {1}".format(block_number,
+                                                                       self.community.currency))
+                    self.new_block_mined.emit(block_number)
+                    self.last_block = block_number
+            except NoPeerAvailable:
+                return
+            except RequestException as e:
+                self.connection_error.emit("Cannot check new block : {0}".format(str(e)))
+
+    new_block_mined = pyqtSignal(int)
+    connection_error = pyqtSignal(str)
\ No newline at end of file
diff --git a/src/cutecoin/core/watchers/nodes.py b/src/cutecoin/core/watchers/nodes.py
new file mode 100644
index 00000000..f7f61d3b
--- /dev/null
+++ b/src/cutecoin/core/watchers/nodes.py
@@ -0,0 +1,17 @@
+'''
+Created on 27 févr. 2015
+
+@author: inso
+'''
+
+
+class NodesWatcher(object):
+    '''
+    This will crawl the network to always
+    have up to date informations about the nodes
+    '''
+
+    def __init__(self):
+        '''
+        Constructor
+        '''
diff --git a/src/cutecoin/gui/currency_tab.py b/src/cutecoin/gui/currency_tab.py
index 8aa750fa..1a0d229e 100644
--- a/src/cutecoin/gui/currency_tab.py
+++ b/src/cutecoin/gui/currency_tab.py
@@ -4,15 +4,11 @@ Created on 2 févr. 2014
 @author: inso
 '''
 
-import logging
 import time
-import requests
-
-from ucoinpy.api import bma
 from PyQt5.QtWidgets import QWidget, QMenu, QAction, QApplication, \
                             QMessageBox, QDialog, QAbstractItemView, QHeaderView
-from PyQt5.QtCore import QModelIndex, Qt, pyqtSlot, QObject, \
-                        QThread, pyqtSignal, QDateTime
+from PyQt5.QtCore import QModelIndex, Qt, pyqtSlot, \
+                        QThread, QDateTime
 from PyQt5.QtGui import QIcon, QCursor
 from ..gen_resources.currency_tab_uic import Ui_CurrencyTabWidget
 from .community_tab import CommunityTabWidget
@@ -21,45 +17,11 @@ from .wallets_tab import WalletsTabWidget
 from .network_tab import NetworkTabWidget
 from ..models.txhistory import HistoryTableModel, TxFilterProxyModel
 from .informations_tab import InformationsTabWidget
-from ..tools.exceptions import NoPeerAvailable, MembershipNotFoundError
+from ..tools.exceptions import MembershipNotFoundError
 from ..core.wallet import Wallet
 from ..core.person import Person
 from ..core.transfer import Transfer
-
-
-class BlockchainWatcher(QObject):
-    def __init__(self, account, community):
-        super().__init__()
-        self.account = account
-        self.community = community
-        self.time_to_wait = int(self.community.get_parameters()['avgGenTime'] / 10)
-        self.exiting = False
-        blockid = self.community.current_blockid()
-        self.last_block = blockid['number']
-
-    @pyqtSlot()
-    def watch(self):
-        while not self.exiting:
-            time.sleep(self.time_to_wait)
-            try:
-                blockid = self.community.current_blockid()
-                block_number = blockid['number']
-                if self.last_block != block_number:
-                    self.community.refresh_cache()
-                    for w in self.account.wallets:
-                        w.refresh_cache(self.community)
-
-                    logging.debug("New block, {0} mined in {1}".format(block_number,
-                                                                       self.community.currency))
-                    self.new_block_mined.emit(block_number)
-                    self.last_block = block_number
-            except NoPeerAvailable:
-                return
-            except requests.exceptions.RequestException as e:
-                self.connection_error.emit("Cannot check new block : {0}".format(str(e)))
-
-    new_block_mined = pyqtSignal(int)
-    connection_error = pyqtSignal(str)
+from ..core.watchers.blockchain import BlockchainWatcher
 
 
 class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget):
diff --git a/src/cutecoin/gui/process_cfg_community.py b/src/cutecoin/gui/process_cfg_community.py
index a8b3e580..db84a69a 100644
--- a/src/cutecoin/gui/process_cfg_community.py
+++ b/src/cutecoin/gui/process_cfg_community.py
@@ -175,7 +175,7 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog):
             peer = Peer.from_signed_raw("{0}{1}\n".format(peer_data['raw'],
                                                       peer_data['signature']))
             if peer.currency == self.community.currency:
-                self.community.peers.append(peer)
+                self.community.add_peer(peer)
             else:
                 QMessageBox.critical(self, "Error",
                                      "This peer doesn't use this community currency.")
diff --git a/src/cutecoin/models/network.py b/src/cutecoin/models/network.py
index 907853e6..d74a2199 100644
--- a/src/cutecoin/models/network.py
+++ b/src/cutecoin/models/network.py
@@ -7,8 +7,9 @@ Created on 5 févr. 2014
 import logging
 from ..core.person import Person
 from ..tools.exceptions import PersonNotFoundError
-from ucoinpy.api import bma
+from ..core.net.node import Node
 from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant, QSortFilterProxyModel
+from PyQt5.QtGui import QColor
 
 
 class NetworkFilterProxyModel(QSortFilterProxyModel):
@@ -47,6 +48,10 @@ class NetworkFilterProxyModel(QSortFilterProxyModel):
     def data(self, index, role):
         source_index = self.mapToSource(index)
         source_data = self.sourceModel().data(source_index, role)
+        if index.column() == self.sourceModel().column_types.index('is_member') \
+         and role == Qt.DisplayRole:
+            value = {True: 'oui', False: 'non'}
+            return value[source_data]
         return source_data
 
 
@@ -117,6 +122,13 @@ class NetworkTableModel(QAbstractTableModel):
         node = self.nodes[row]
         if role == Qt.DisplayRole:
             return self.data_node(node)[col]
+        if role == Qt.BackgroundColorRole:
+            colors = {Node.ONLINE: QVariant(),
+                      Node.OFFLINE: QColor(Qt.darkRed),
+                      Node.DESYNCED: QColor(Qt.gray),
+                      Node.CORRUPTED: QColor(Qt.darkRed)
+                      }
+            return colors[node.state]
         #TODO: Display colors depending on node state
 
     def flags(self, index):
-- 
GitLab