From 98396e9d800c0fcfdda6ce3192fc8dca54bca291 Mon Sep 17 00:00:00 2001 From: Inso <insomniak.fr@gmail.com> Date: Fri, 19 Jun 2015 07:23:52 +0200 Subject: [PATCH] Remember pending requests to not spam the network Identities are now refreshing correctly --- src/cutecoin/core/account.py | 11 +++- src/cutecoin/core/community.py | 2 +- src/cutecoin/core/net/api/bma/access.py | 45 +++++++++------ src/cutecoin/core/registry/identities.py | 2 +- src/cutecoin/core/registry/identity.py | 8 ++- src/cutecoin/core/wallet.py | 2 +- src/cutecoin/gui/community_tab.py | 70 +++++++++++++++--------- src/cutecoin/models/identities.py | 34 +++++++++--- 8 files changed, 118 insertions(+), 56 deletions(-) diff --git a/src/cutecoin/core/account.py b/src/cutecoin/core/account.py index bf88aeb9..786e31c8 100644 --- a/src/cutecoin/core/account.py +++ b/src/cutecoin/core/account.py @@ -118,7 +118,7 @@ class Account(QObject): ) loading_progressed = pyqtSignal(int, int) - inner_data_changed = pyqtSignal() + inner_data_changed = pyqtSignal(str) wallets_changed = pyqtSignal() def __init__(self, salt, pubkey, name, communities, wallets, contacts, identities_registry): @@ -257,6 +257,15 @@ class Account(QObject): def set_display_referential(self, index): self.referential = index + def identity(self, community): + """ + Get the account identity in the specified community + :param cutecoin.core.community.Community community: The community where to look after the identity + :return: The account identity in the community + :rtype: cutecoin.core.registry.Identity + """ + return self._identities_registry.lookup(self.pubkey, community) + @property def units_to_ref(self): return Account.referentials[self.referential][0] diff --git a/src/cutecoin/core/community.py b/src/cutecoin/core/community.py index 3857d6a2..1835ce42 100644 --- a/src/cutecoin/core/community.py +++ b/src/cutecoin/core/community.py @@ -26,7 +26,7 @@ class Community(QObject): .. warning:: The currency name is supposed to be unique in cutecoin but nothing exists in ucoin to assert that a currency name is unique. """ - inner_data_changed = pyqtSignal(int) + inner_data_changed = pyqtSignal(str) def __init__(self, currency, network, bma_access): """ diff --git a/src/cutecoin/core/net/api/bma/access.py b/src/cutecoin/core/net/api/bma/access.py index b18a977e..251b0f6e 100644 --- a/src/cutecoin/core/net/api/bma/access.py +++ b/src/cutecoin/core/net/api/bma/access.py @@ -22,6 +22,7 @@ class BmaAccess(QObject): """ super().__init__() self._data = data + self._pending_requests = {} self._network = network @classmethod @@ -93,8 +94,13 @@ class BmaAccess(QObject): #Move to network nstead of community #after removing qthreads reply = self.request(request, req_args, get_args) - reply.finished.connect(lambda: - self.handle_reply(caller, request, req_args, get_args, tries)) + if cache_key in self._pending_requests: + if caller not in self._pending_requests[cache_key]: + self._pending_requests[cache_key].append(caller) + else: + self._pending_requests[cache_key] = [caller] + reply.finished.connect(lambda: + self.handle_reply(request, req_args, get_args, tries)) return ret_data def request(self, request, req_args={}, get_args={}): @@ -119,16 +125,17 @@ class BmaAccess(QObject): raise NoPeerAvailable(self.currency, len(nodes)) @pyqtSlot(int, dict, dict, QObject) - def handle_reply(self, caller, request, req_args, get_args, tries): + def handle_reply(self, request, req_args, get_args, tries): reply = self.sender() - #logging.debug("Handling QtNetworkReply for {0}".format(str(request))) + logging.debug("Handling QtNetworkReply for {0}".format(str(request))) + cache_key = (str(request), + str(tuple(frozenset(sorted(req_args.keys())))), + str(tuple(frozenset(sorted(req_args.values())))), + str(tuple(frozenset(sorted(get_args.keys())))), + str(tuple(frozenset(sorted(get_args.values()))))) if reply.error() == QNetworkReply.NoError: - cache_key = (str(request), - str(tuple(frozenset(sorted(req_args.keys())))), - str(tuple(frozenset(sorted(req_args.values())))), - str(tuple(frozenset(sorted(get_args.keys())))), - str(tuple(frozenset(sorted(get_args.values()))))) strdata = bytes(reply.readAll()).decode('utf-8') + json_data = json.loads(strdata) #logging.debug("Data in reply : {0}".format(strdata)) if cache_key not in self._data: @@ -136,18 +143,24 @@ class BmaAccess(QObject): if 'metadata' not in self._data[cache_key]: self._data[cache_key]['metadata'] = {} + + if 'value' not in self._data[cache_key]: + self._data[cache_key]['value'] = {} self._data[cache_key]['metadata']['block'] = self._network.latest_block change = False - if 'value' in self._data[cache_key]: - if self._data[cache_key]['value'] != json.loads(strdata): - change = True - else: + if self._data[cache_key]['value'] != json_data: change = True if change: - self._data[cache_key]['value'] = json.loads(strdata) - caller.inner_data_changed.emit(request) + self._data[cache_key]['value'] = json_data + logging.debug(self._pending_requests.keys()) + for caller in self._pending_requests[cache_key]: + logging.debug("Emit change for {0} : {1} ".format(caller, request)) + caller.inner_data_changed.emit(str(request)) + self._pending_requests.pop(cache_key) else: logging.debug("Error in reply : {0}".format(reply.error())) if tries < 3: - self.get(caller, request, req_args, get_args) + self._pending_requests.pop(cache_key) + for caller in self._pending_requests[cache_key]: + self.get(caller, request, req_args, get_args) diff --git a/src/cutecoin/core/registry/identities.py b/src/cutecoin/core/registry/identities.py index b23b8c41..e17fb2ad 100644 --- a/src/cutecoin/core/registry/identities.py +++ b/src/cutecoin/core/registry/identities.py @@ -81,7 +81,7 @@ class IdentitiesRegistry: identity_uid = uid_data["uid"] identity.uid = identity_uid identity.status = Identity.FOUND - identity.inner_data_changed.emit(qtbma.wot.Lookup) + identity.inner_data_changed.emit(str(qtbma.wot.Lookup)) return def from_metadata(self, metadata): diff --git a/src/cutecoin/core/registry/identity.py b/src/cutecoin/core/registry/identity.py index 3505cd6b..0517c1be 100644 --- a/src/cutecoin/core/registry/identity.py +++ b/src/cutecoin/core/registry/identity.py @@ -22,7 +22,7 @@ class Identity(QObject): FOUND = 1 NOT_FOUND = 0 - inner_data_changed = pyqtSignal(int) + inner_data_changed = pyqtSignal(str) def __init__(self, uid, pubkey, status): """ @@ -280,3 +280,9 @@ class Identity(QObject): 'pubkey': self.pubkey, 'status': self.status} return data + + def __str__(self): + status_str = ("NOT_FOUND", "FOUND") + return "{0} - {1} - {2}".format(self.uid, + self.pubkey, + status_str[self.status]) \ No newline at end of file diff --git a/src/cutecoin/core/wallet.py b/src/cutecoin/core/wallet.py index dedc572c..4be6cdbf 100644 --- a/src/cutecoin/core/wallet.py +++ b/src/cutecoin/core/wallet.py @@ -196,7 +196,7 @@ class Wallet(QObject): A wallet is used to manage money with a unique key. ''' - inner_data_changed = pyqtSignal(int) + inner_data_changed = pyqtSignal(str) refresh_progressed = pyqtSignal(int, int) def __init__(self, walletid, pubkey, name, identities_registry): diff --git a/src/cutecoin/gui/community_tab.py b/src/cutecoin/gui/community_tab.py index eb5a6945..ab53db02 100644 --- a/src/cutecoin/gui/community_tab.py +++ b/src/cutecoin/gui/community_tab.py @@ -43,9 +43,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): self.parent = parent self.app = app self.community = community - self.community.inner_data_changed.connect(self.handle_change) self.account = account - self._last_search = '' self.password_asker = password_asker identities_model = IdentitiesTableModel(community) proxy = IdentitiesFilterProxyModel() @@ -64,7 +62,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): direct_connections = QAction(self.tr("Direct connections"), self) direct_connections.triggered.connect(self.search_direct_connections) self.button_search.addAction(direct_connections) - self.refresh() + self.search_direct_connections() self.refresh_quality_buttons() def identity_context_menu(self, point): @@ -285,14 +283,20 @@ Revoking your UID can only success if it is not already validated by the network for identity in response['results']: persons.append(self.app.identities_registry(identity['pubkey'], self.community)) - self._last_search = 'text' self.edit_textsearch.clear() self.refresh(persons) - def handle_change(self, origin): + @pyqtSlot(str) + def handle_community_change(self, origin): + logging.debug("Handle account community {0}".format(origin)) if origin == qtbma.wot.Members: - if self._last_search == 'members': - self.search_members() + self.search_members() + + @pyqtSlot(str) + def handle_account_identity_change(self, origin): + logging.debug("Handle account identity change {0}".format(origin)) + if origin in (str(qtbma.wot.CertifiedBy), str(qtbma.wot.CertifiersOf)): + self.search_direct_connections() def search_members(self): """ @@ -303,7 +307,16 @@ Revoking your UID can only success if it is not already validated by the network for p in pubkeys: identities.append(self.app.identities_registry.lookup(p, self.community)) - self._last_search = 'members' + self_identity = self.account.identity(self.community) + + try: + self_identity.inner_data_changed.disconnect(self.handle_account_identity_change) + self.community.inner_data_changed.connect(self.handle_community_change) + except TypeError as e: + if "disconnect() failed" in str(e): + pass + else: + raise self.edit_textsearch.clear() self.refresh(identities) @@ -312,30 +325,33 @@ Revoking your UID can only success if it is not already validated by the network """ Search members of community and display found members """ - self._last_search = 'direct_connections' - self.refresh() + self_identity = self.account.identity(self.community) + try: + self_identity.inner_data_changed.connect(self.handle_account_identity_change) + self.community.inner_data_changed.disconnect(self.handle_community_change) + except TypeError as e: + if "disconnect() failed" in str(e): + pass + else: + raise + + account_connections = [] + for p in self_identity.unique_valid_certifiers_of(self.community): + account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community)) + certifiers_of = [p for p in account_connections] + for p in self_identity.unique_valid_certified_by(self.community): + account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community)) + certified_by = [p for p in account_connections + if p.pubkey not in [i.pubkey for i in certifiers_of]] + identities = certifiers_of + certified_by + self.refresh(identities) - def refresh(self, persons=None): + def refresh(self, identities): ''' Refresh the table with specified identities. If no identities is passed, use the account connections. ''' - if persons is None: - self_identity = self.app.identities_registry.lookup(self.account.pubkey, self.community) - account_connections = [] - certifiers_of = [] - certified_by = [] - for p in self_identity.unique_valid_certifiers_of(self.community): - account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community)) - certifiers_of = [p for p in account_connections] - logging.debug(persons) - for p in self_identity.unique_valid_certified_by(self.community): - account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community)) - certified_by = [p for p in account_connections - if p.pubkey not in [i.pubkey for i in certifiers_of]] - persons = certifiers_of + certified_by - - self.table_identities.model().sourceModel().refresh_identities(persons) + self.table_identities.model().sourceModel().refresh_identities(identities) self.table_identities.resizeColumnsToContents() def refresh_quality_buttons(self): diff --git a/src/cutecoin/models/identities.py b/src/cutecoin/models/identities.py index 0f069f6f..21c4642b 100644 --- a/src/cutecoin/models/identities.py +++ b/src/cutecoin/models/identities.py @@ -94,10 +94,10 @@ class IdentitiesTableModel(QAbstractTableModel): ''' return [i[1] for i in self.identities_data] - def identity_data(self, person): + def identity_data(self, identity): parameters = self.community.parameters try: - join_block = person.membership(self.community)['blockNumber'] + join_block = identity.membership(self.community)['blockNumber'] try: join_date = self.community.get_block(join_block)['medianTime'] expiration_date = join_date + parameters['sigValidity'] @@ -108,16 +108,34 @@ class IdentitiesTableModel(QAbstractTableModel): join_date = None expiration_date = None - return (person.uid, person.pubkey, join_date, expiration_date) + return (identity.uid, identity.pubkey, join_date, expiration_date) - def refresh_identities(self, persons): - logging.debug("Refresh {0} identities".format(len(persons))) + def refresh_identities(self, identities): + """ + Change the identities to display + + :param cutecoin.core.registry.IdentitiesRegistry identities: The new identities to display + """ + logging.debug("Refresh {0} identities".format(len(identities))) self.identities_data = [] self.beginResetModel() - for person in persons: - self.identities_data.append(self.identity_data(person)) + for identity in identities: + identity.inner_data_changed.connect(lambda: self.refresh_identity(identity)) + self.identities_data.append(self.identity_data(identity)) self.endResetModel() + def refresh_identity(self, identity): + """ + Refresh an identity when its inner_data changed + :param cutecoin.core.registry.Identity identity: The refreshed identity + """ + try: + index = self.identities_data.index(identity) + self.identities_data[index] = self.identity_data(identity) + self.dataChanged.emit(index, index) + except ValueError: + logging.debug("Identity {0} is not in list".format(identity)) + def rowCount(self, parent): return len(self.identities_data) @@ -135,7 +153,7 @@ class IdentitiesTableModel(QAbstractTableModel): col = index.column() return self.identities_data[row][col] - def person_index(self, pubkey): + def identity_index(self, pubkey): try: row = self.pubkeys.index(pubkey) index_start = self.index(row, 0) -- GitLab