diff --git a/README.md b/README.md index 3de008165a12767f5a358a709fee79cf3b4a68f6..72f7b5568d28162df6f32b5f2ab64546512fcd06 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Python3 and PyQt5 Client for [uCoin](http://www.ucoin.io) project. * The executable is generated in "build" folder, named "sakia" ### Download latest release - * Go to [current release](https://github.com/ucoin-io/sakia/release) + * Go to [current release](https://github.com/ucoin-io/sakia/releases) * Download corresponding package to your operating system * Unzip and start "sakia" :) * Join our beta community by contacting us on [uCoin forum](http://forum.ucoin.io/) diff --git a/appveyor.yml b/appveyor.yml index 7f38948077e1342046d7f96a832d6d5665df6476..dd6efecb5f9ebb74cd081aa15337ff6051a50220 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -44,6 +44,7 @@ artifacts: name: sakia-win$(PYTHON_ARCH) # upload to releases deploy: + tag: $(APPVEYOR_REPO_TAG_NAME) release: sakia $(APPVEYOR_REPO_TAG_NAME) provider: GitHub auth_token: diff --git a/res/icons/icons.qrc b/res/icons/icons.qrc index 78d69f6a22f0952ca3d82782f46f3f293756c69d..cfbe0e556023429890485c14b43b9779a4108ac8 100644 --- a/res/icons/icons.qrc +++ b/res/icons/icons.qrc @@ -12,7 +12,7 @@ <file alias="connect_icon">noun_152997_cc.svg</file> <file alias="home_icon">iconmonstr-home-icon.svg</file> <file alias="add_account_icon">noun_7440_cc.svg</file> - <file>logo.svg</file> + <file alias="sakia_logo">logo.svg</file> <file alias="ucoin_info_icon">noun_76373_cc.svg</file> <file alias="import_icon">noun_62479_cc.svg</file> <file alias="network_icon">noun_21549_cc.svg</file> diff --git a/setup.py b/setup.py index 8b96d572dcc2c86173f3f1beab47108274ee7a5a..fb1a7ce11c195b5125da87d1240bb194798de24e 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,3 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# source d'inspiration: http://wiki.wxpython.org/cx_freeze - import sys, os, subprocess, multiprocessing, site from cx_Freeze import setup, Executable from PyQt5 import QtCore @@ -116,7 +111,7 @@ target = Executable( # creation du setup setup( name = "sakia", - version = "0.11.0", + version = "0.11.2", description = "UCoin client", author = "Inso", options = {"build_exe": options}, diff --git a/src/sakia/__init__.py b/src/sakia/__init__.py index e3efc97e3b1a25481ae082c53c9038743e264cc7..03e0055e7e45c2e6aa109116494f77af1495f348 100644 --- a/src/sakia/__init__.py +++ b/src/sakia/__init__.py @@ -1,2 +1,2 @@ -__version_info__ = ('0', '11', '0') +__version_info__ = ('0', '11', '2') __version__ = '.'.join(__version_info__) diff --git a/src/sakia/core/account.py b/src/sakia/core/account.py index c901dce087ab50fe910ffeebbc82bd8af78c0543..c6a801d8ede7ef0b4b4a10a3061a69f182ebd79b 100644 --- a/src/sakia/core/account.py +++ b/src/sakia/core/account.py @@ -12,15 +12,13 @@ import logging import time import asyncio -from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication -from PyQt5.QtNetwork import QNetworkReply +from PyQt5.QtCore import QObject, pyqtSignal from . import money from .wallet import Wallet from .community import Community from .registry import LocalState from ..tools.exceptions import ContactAlreadyExists, NoPeerAvailable -from ..tools.decorators import asyncify from ucoinpy.api import bma from ucoinpy.api.bma import PROTOCOL_VERSION from aiohttp.errors import ClientError diff --git a/src/sakia/core/app.py b/src/sakia/core/app.py index 22722c1f65dccec79a84ecf306a01cff7b5aa05a..d59ff93c74094ad1e9dd9d0e6f9e484f3f190783 100644 --- a/src/sakia/core/app.py +++ b/src/sakia/core/app.py @@ -278,19 +278,13 @@ class Application(QObject): if os.path.exists(network_path): with open(network_path, 'r') as json_data: data = json.load(json_data) - if 'version' in data and data['version'] == __version__: logging.debug("Merging network : {0}".format(data)) community.network.merge_with_json(data['network']) - else: - os.remove(network_path) if os.path.exists(bma_path): with open(bma_path, 'r') as json_data: data = json.load(json_data) - if 'version' in data and data['version'] == __version__: community.bma_access.load_from_json(data['cache']) - else: - os.remove(bma_path) for wallet in account.wallets: for c in account.communities: @@ -300,10 +294,7 @@ class Application(QObject): if os.path.exists(wallet_path): with open(wallet_path, 'r') as json_data: data = json.load(json_data) - if 'version' in data and data['version'] == __version__: wallet.load_caches(self, data) - else: - os.remove(wallet_path) def load_preferences(self): """ @@ -454,7 +445,7 @@ class Application(QObject): name, 'properties') json_data = open(account_path, 'r') data = json.load(json_data) - account = Account.load(data) + account = Account.load(data, self._identities_registry) account.name = name self.add_account(account) self.save(account) diff --git a/src/sakia/core/net/node.py b/src/sakia/core/net/node.py index cb312212541a1e6fe44214c55572be5f28c66d2d..3539d85abd3134f0f269b7e095119ec2faa96a31 100644 --- a/src/sakia/core/net/node.py +++ b/src/sakia/core/net/node.py @@ -457,14 +457,16 @@ class Node(QObject): try: leaf_data = await bma.network.peering.Peers(conn_handler).get(leaf=leaf_hash) if "raw" in leaf_data['leaf']['value']: - peer_doc = Peer.from_signed_raw("{0}{1}\n".format(leaf_data['leaf']['value']['raw'], - leaf_data['leaf']['value']['signature'])) + str_doc = "{0}{1}\n".format(leaf_data['leaf']['value']['raw'], + leaf_data['leaf']['value']['signature']) + peer_doc = Peer.from_signed_raw(str_doc) pubkey = leaf_data['leaf']['value']['pubkey'] self.neighbour_found.emit(peer_doc, pubkey) else: logging.debug("Incorrect leaf reply") - except ValueError as e: - logging.debug("Error in leaf reply") + except (AttributeError, ValueError) as e: + logging.debug("{pubkey} : Incorrect peer data in {leaf}".format(pubkey=self.pubkey[:5], + leaf=leaf_hash)) self.state = Node.OFFLINE self.changed.emit() except (ClientError, gaierror, asyncio.TimeoutError, DisconnectedError) as e: diff --git a/src/sakia/gui/network_tab.py b/src/sakia/gui/network_tab.py index 8cf6e13949c6a7fdd09c3efd0d33c372b8d35b73..417c1e06ef0c4037e1eeadcc6158bdb9ce973890 100644 --- a/src/sakia/gui/network_tab.py +++ b/src/sakia/gui/network_tab.py @@ -108,6 +108,8 @@ class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): def manual_nodes_refresh(self): self.community.network.refresh_once() + self.button_manual_refresh.setEnabled(False) + asyncio.get_event_loop().call_later(15, lambda: self.button_manual_refresh.setEnabled(True)) def changeEvent(self, event): """ diff --git a/src/sakia/gui/process_cfg_community.py b/src/sakia/gui/process_cfg_community.py index dca582223fe7f1b5e4cf00abb5db0ebb9783fa5a..de3b43dfd2d0e956ef9acf9707a1c7afa43774cb 100644 --- a/src/sakia/gui/process_cfg_community.py +++ b/src/sakia/gui/process_cfg_community.py @@ -73,6 +73,8 @@ class StepPageInit(Step): self.config_dialog.label_error.setText(str(e)) except aiohttp.errors.ClientError as e: self.config_dialog.label_error.setText(str(e)) + except ValueError as e: + self.config_dialog.label_error.setText(str(e)) @asyncify async def check_connect(self, checked=False): @@ -99,6 +101,8 @@ Yours : {0}, the network : {1}""".format(registered[1], registered[2]))) self.config_dialog.label_error.setText(str(e)) except aiohttp.errors.ClientError as e: self.config_dialog.label_error.setText(str(e)) + except ValueError as e: + self.config_dialog.label_error.setText(str(e)) @asyncify async def check_register(self, checked=False): @@ -140,6 +144,8 @@ Yours : {0}, the network : {1}""".format(registered[1], registered[2]))) self.config_dialog.label_error.setText(str(e)) except aiohttp.errors.ClientError as e: self.config_dialog.label_error.setText(str(e)) + except ValueError as e: + self.config_dialog.label_error.setText(str(e)) def is_valid(self): return self.node is not None diff --git a/src/sakia/gui/transactions_tab.py b/src/sakia/gui/transactions_tab.py index 384b5bccd439a1d40dea820d410ca5cc11989f5b..0b216371768491a90f35996ed46a2ec4f489d2d5 100644 --- a/src/sakia/gui/transactions_tab.py +++ b/src/sakia/gui/transactions_tab.py @@ -120,25 +120,30 @@ class TransactionsTabWidget(QWidget, Ui_transactionsTabWidget): self.progressbar.show() self.progressbar.setValue(value) self.progressbar.setMaximum(maximum) - self.app.current_account.loading_progressed.connect(progressing) - self.app.current_account.loading_finished.connect(self.stop_progress) + self.account.loading_progressed.connect(progressing) + self.account.loading_finished.connect(self.stop_progress) - @pyqtSlot(list) def stop_progress(self, community, received_list): if community == self.community: - amount = 0 - for r in received_list: - amount += r.metadata['amount'] self.progressbar.hide() - if len(received_list) > 0: - text = self.tr("Received {0} {1} from {2} transfers").format(amount, - self.community.currency, - len(received_list)) - if self.app.preferences['notifications']: - toast.display(self.tr("New transactions received"), text) - self.table_history.model().sourceModel().refresh_transfers() self.table_history.resizeColumnsToContents() + self.notification_reception(received_list) + + @asyncify + @asyncio.coroutine + def notification_reception(self, received_list): + if len(received_list) > 0: + amount = 0 + for r in received_list: + amount += r.metadata['amount'] + localized_amount = yield from self.app.current_account.current_ref(amount, self.community, self.app)\ + .localized(units=True, + international_system=self.app.preferences['international_system_of_units']) + text = self.tr("Received {amount} from {number} transfers").format(amount=localized_amount , + number=len(received_list)) + if self.app.preferences['notifications']: + toast.display(self.tr("New transactions received"), text) @once_at_a_time @asyncify diff --git a/src/sakia/gui/wot_tab.py b/src/sakia/gui/wot_tab.py index bc97af0cffe09cd7bb0a816c7b0882476d24de1a..d20f93adb65765cc1ca1dec37817224a963ca61d 100644 --- a/src/sakia/gui/wot_tab.py +++ b/src/sakia/gui/wot_tab.py @@ -16,8 +16,9 @@ from .certification import CertificationDialog from .transfer import TransferMoneyDialog from .contact import ConfigureContactDialog from ..gen_resources.wot_tab_uic import Ui_WotTabWidget -from sakia.gui.views.wot import NODE_STATUS_HIGHLIGHTED, NODE_STATUS_SELECTED, NODE_STATUS_OUT -from sakia.gui.widgets.busy import Busy +from .views.wot import NODE_STATUS_HIGHLIGHTED, NODE_STATUS_SELECTED, NODE_STATUS_OUT +from .widgets.busy import Busy +from ..tools.exceptions import NoPeerAvailable class WotTabWidget(QWidget, Ui_WotTabWidget): @@ -263,20 +264,23 @@ class WotTabWidget(QWidget, Ui_WotTabWidget): if len(text) < 2: return False - response = await self.community.bma_access.future_request(bma.wot.Lookup, {'search': text}) - - nodes = {} - for identity in response['results']: - nodes[identity['pubkey']] = identity['uids'][0]['uid'] - - if nodes: - self.nodes = list() - self.comboBoxSearch.clear() - self.comboBoxSearch.lineEdit().setText(text) - for pubkey, uid in nodes.items(): - self.nodes.append({'pubkey': pubkey, 'uid': uid}) - self.comboBoxSearch.addItem(uid) - self.comboBoxSearch.showPopup() + try: + response = await self.community.bma_access.future_request(bma.wot.Lookup, {'search': text}) + + nodes = {} + for identity in response['results']: + nodes[identity['pubkey']] = identity['uids'][0]['uid'] + + if nodes: + self.nodes = list() + self.comboBoxSearch.clear() + self.comboBoxSearch.lineEdit().setText(text) + for pubkey, uid in nodes.items(): + self.nodes.append({'pubkey': pubkey, 'uid': uid}) + self.comboBoxSearch.addItem(uid) + self.comboBoxSearch.showPopup() + except NoPeerAvailable: + pass def select_node(self, index): """ diff --git a/src/sakia/main.py b/src/sakia/main.py index 604317a645e1ad0fb1e318c81f320ac480f22697..12ee9c5c29f030a4e4d75811e1db659e9e23d549 100755 --- a/src/sakia/main.py +++ b/src/sakia/main.py @@ -41,16 +41,16 @@ def async_exception_handler(loop, context): log_lines.append('{}: {!r}'.format(key, context[key])) logging.error('\n'.join(log_lines), exc_info=exc_info) - if "Unclosed" not in message and \ - "socket.gaierror" not in str(exception) and \ - "socket.gaierror" not in message: - os._exit(1) + for line in log_lines: + for ignored in ("Unclosed", "socket.gaierror"): + if ignored in line: + return + os._exit(1) if __name__ == '__main__': # activate ctrl-c interrupt signal.signal(signal.SIGINT, signal.SIG_DFL) - sakia = QApplication(sys.argv) loop = QEventLoop(sakia) loop.set_exception_handler(async_exception_handler)