From 4f254bd2100bb8bc386b1450c18ca02c3e86cc4c Mon Sep 17 00:00:00 2001 From: inso <insomniak.fr@gmaiL.com> Date: Mon, 19 Dec 2016 07:51:40 +0100 Subject: [PATCH] Removing Component inheritance --- src/sakia/app.py | 6 +- src/sakia/data/entities/connection.py | 9 +- src/sakia/data/processors/blockchain.py | 12 +- src/sakia/data/processors/certifications.py | 2 +- src/sakia/data/processors/connections.py | 18 ++- src/sakia/data/processors/transactions.py | 6 +- src/sakia/data/repositories/certifications.py | 2 +- src/sakia/data/repositories/connections.py | 2 +- src/sakia/gui/component/__init__.py | 1 - src/sakia/gui/component/controller.py | 44 ------ src/sakia/gui/component/model.py | 15 -- .../dialogs/certification/certification.ui | 19 ++- .../gui/dialogs/certification/controller.py | 142 ++++++++---------- src/sakia/gui/dialogs/certification/model.py | 55 ++++--- src/sakia/gui/dialogs/certification/view.py | 51 +++---- .../gui/dialogs/connection_cfg/controller.py | 9 +- src/sakia/gui/dialogs/connection_cfg/model.py | 5 +- .../gui/dialogs/revocation/controller.py | 9 +- src/sakia/gui/dialogs/revocation/model.py | 2 +- src/sakia/gui/dialogs/transfer/controller.py | 13 +- src/sakia/gui/dialogs/transfer/model.py | 4 +- src/sakia/gui/main_window/controller.py | 38 ++--- src/sakia/gui/main_window/model.py | 4 +- .../gui/main_window/status_bar/controller.py | 24 +-- src/sakia/gui/main_window/status_bar/model.py | 6 +- .../gui/main_window/toolbar/controller.py | 98 +++++------- src/sakia/gui/main_window/toolbar/model.py | 16 +- src/sakia/gui/navigation/controller.py | 26 ++-- .../gui/navigation/graphs/base/controller.py | 9 +- src/sakia/gui/navigation/graphs/base/model.py | 5 +- .../gui/navigation/graphs/wot/controller.py | 16 +- .../gui/navigation/homescreen/controller.py | 16 +- src/sakia/gui/navigation/homescreen/model.py | 4 +- .../gui/navigation/identities/controller.py | 22 +-- src/sakia/gui/navigation/identities/model.py | 5 +- .../gui/navigation/informations/controller.py | 23 +-- .../gui/navigation/informations/model.py | 5 +- src/sakia/gui/navigation/model.py | 75 +++++---- .../gui/navigation/network/controller.py | 30 ++-- src/sakia/gui/navigation/network/model.py | 5 +- .../gui/navigation/txhistory/controller.py | 36 ++--- src/sakia/gui/navigation/txhistory/model.py | 4 +- src/sakia/gui/password_asker.py | 2 +- src/sakia/gui/sub/search_user/controller.py | 32 ++-- src/sakia/gui/sub/search_user/model.py | 14 +- .../gui/sub/user_information/controller.py | 46 ++---- src/sakia/gui/sub/user_information/model.py | 28 ++-- src/sakia/gui/sub/user_information/view.py | 19 --- src/sakia/models/generic_tree.py | 2 +- src/sakia/services/identities.py | 140 +++++++++-------- src/sakia/services/transactions.py | 2 +- 51 files changed, 525 insertions(+), 653 deletions(-) delete mode 100644 src/sakia/gui/component/__init__.py delete mode 100644 src/sakia/gui/component/controller.py delete mode 100644 src/sakia/gui/component/model.py diff --git a/src/sakia/app.py b/src/sakia/app.py index ab0c862a..b9ba33e0 100644 --- a/src/sakia/app.py +++ b/src/sakia/app.py @@ -18,7 +18,7 @@ from sakia.data.connectors import BmaConnector from sakia.services import NetworkService, BlockchainService, IdentitiesService, SourcesServices, TransactionsService from sakia.data.repositories import SakiaDatabase from sakia.data.processors import BlockchainProcessor, NodesProcessor, IdentitiesProcessor, \ - CertificationsProcessor, SourcesProcessor, TransactionsProcessor + CertificationsProcessor, SourcesProcessor, TransactionsProcessor, ConnectionsProcessor from sakia.data.files import AppDataFile, UserParametersFile from sakia.decorators import asyncify from sakia.money import Relative @@ -90,6 +90,7 @@ class Application(QObject): nodes_processor = NodesProcessor(self.db.nodes_repo) bma_connector = BmaConnector(nodes_processor) + connections_processor = ConnectionsProcessor(self.db.connections_repo) identities_processor = IdentitiesProcessor(self.db.identities_repo, self.db.blockchains_repo, bma_connector) certs_processor = CertificationsProcessor(self.db.certifications_repo, self.db.identities_repo, bma_connector) blockchain_processor = BlockchainProcessor.instanciate(self) @@ -103,7 +104,8 @@ class Application(QObject): self.transactions_services = {} for currency in self.db.connections_repo.get_currencies(): - self.identities_services[currency] = IdentitiesService(currency, identities_processor, + self.identities_services[currency] = IdentitiesService(currency, connections_processor, + identities_processor, certs_processor, blockchain_processor, bma_connector) self.transactions_services[currency] = TransactionsService(currency, transactions_processor, diff --git a/src/sakia/data/entities/connection.py b/src/sakia/data/entities/connection.py index 58656413..15b9c2ce 100644 --- a/src/sakia/data/entities/connection.py +++ b/src/sakia/data/entities/connection.py @@ -1,5 +1,6 @@ import attr from duniterpy.documents import block_uid, BlockUID +from duniterpy.key import ScryptParams @attr.s() @@ -17,5 +18,11 @@ class Connection: scrypt_r = attr.ib(convert=int, default=16) scrypt_p = attr.ib(convert=int, default=1) blockstamp = attr.ib(convert=block_uid, default=BlockUID.empty(), cmp=False, hash=False) - password = attr.ib(convert=str, default="", cmp=False, hash=False) + password = attr.ib(init=False, convert=str, default="", cmp=False, hash=False) + def title(self): + return self.uid + " - " + self.pubkey[:5] + + @property + def scrypt_params(self): + return ScryptParams(self.scrypt_N, self.scrypt_r, self.scrypt_p) diff --git a/src/sakia/data/processors/blockchain.py b/src/sakia/data/processors/blockchain.py index c9a66e98..08b029e3 100644 --- a/src/sakia/data/processors/blockchain.py +++ b/src/sakia/data/processors/blockchain.py @@ -1,5 +1,6 @@ import attr -import re +import logging +from sakia.errors import NoPeerAvailable from ..entities import Blockchain, BlockchainParameters from .nodes import NodesProcessor from ..connectors import BmaConnector @@ -12,6 +13,7 @@ import asyncio class BlockchainProcessor: _repo = attr.ib() # :type sakia.data.repositories.CertificationsRepo _bma_connector = attr.ib() # :type sakia.data.connectors.bma.BmaConnector + _logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('sakia'))) @classmethod def instanciate(cls, app): @@ -22,6 +24,14 @@ class BlockchainProcessor: return cls(app.db.blockchains_repo, BmaConnector(NodesProcessor(app.db.nodes_repo))) + async def timestamp(self, currency, blockstamp): + try: + block = await self._bma_connector.get(currency, bma.blockchain.block, {'number': blockstamp.number}) + if block: + return block['medianTime'] + except NoPeerAvailable as e: + self._logger.debug(str(e)) + def current_buid(self, currency): """ Get the local current blockuid diff --git a/src/sakia/data/processors/certifications.py b/src/sakia/data/processors/certifications.py index 07e96dba..11c00629 100644 --- a/src/sakia/data/processors/certifications.py +++ b/src/sakia/data/processors/certifications.py @@ -54,7 +54,7 @@ class CertificationsProcessor: :return: the remaining time :rtype: int """ - certified = self._certifications_repo.get_latest_sent(currency=currency, certifier=pubkey) + certified = self._certifications_repo.get_latest_sent(currency=currency, pubkey=pubkey) if certified and blockchain_time - certified.timestamp < parameters.sig_period: return parameters.sig_period - (blockchain_time - certified.timestamp) return 0 diff --git a/src/sakia/data/processors/connections.py b/src/sakia/data/processors/connections.py index 528f94b9..536b4d67 100644 --- a/src/sakia/data/processors/connections.py +++ b/src/sakia/data/processors/connections.py @@ -8,6 +8,14 @@ class ConnectionsProcessor: _connections_repo = attr.ib() # :type sakia.data.repositories.ConnectionsRepo _logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('sakia'))) + @classmethod + def instanciate(cls, app): + """ + Instanciate a blockchain processor + :param sakia.app.Application app: the app + """ + return cls(app.db.connections_repo) + def commit_connection(self, connection): """ Saves a connection state in the db @@ -18,5 +26,11 @@ class ConnectionsProcessor: except sqlite3.IntegrityError: self._connections_repo.update(connection) - def pubkeys(self, currency): - return self._connections_repo.get_pubkeys(currency) \ No newline at end of file + def pubkeys(self): + return self._connections_repo.get_pubkeys() + + def connections(self, currency): + return self._connections_repo.get_all(currency=currency) + + def currencies(self): + return self._connections_repo.get_currencies() diff --git a/src/sakia/data/processors/transactions.py b/src/sakia/data/processors/transactions.py index cc427a68..19bcb0a2 100644 --- a/src/sakia/data/processors/transactions.py +++ b/src/sakia/data/processors/transactions.py @@ -69,9 +69,9 @@ class TransactionsProcessor: def find_by_hash(self, sha_hash): return self._repo.find_one(sha_hash=sha_hash) - def awaiting(self): - return self._repo.find_all(state=Transaction.AWAITING) + \ - self._repo.find_all(state=Transaction.AWAITING | Transaction.LOCAL) + def awaiting(self, currency): + return self._repo.get_all(currency=currency, state=Transaction.AWAITING) + \ + self._repo.get_all(currency=currency, state=Transaction.AWAITING | Transaction.LOCAL) def run_state_transitions(self, tx, *inputs): """ diff --git a/src/sakia/data/repositories/certifications.py b/src/sakia/data/repositories/certifications.py index daffd318..1ef13cdd 100644 --- a/src/sakia/data/repositories/certifications.py +++ b/src/sakia/data/repositories/certifications.py @@ -88,7 +88,7 @@ class CertificationsRepo: """ request = """SELECT * FROM certifications WHERE currency=? AND certifier=? - ORDER BY timestamp DESC + ORDER BY ts DESC LIMIT 1""" c = self._conn.execute(request, (currency, pubkey)) data = c.fetchone() diff --git a/src/sakia/data/repositories/connections.py b/src/sakia/data/repositories/connections.py index 4b0f4020..1de18b12 100644 --- a/src/sakia/data/repositories/connections.py +++ b/src/sakia/data/repositories/connections.py @@ -54,7 +54,7 @@ class ConnectionsRepo: request = "SELECT * FROM connections" if filters: - request += "WHERE {filters}".format(filters=" AND ".join(filters)) + request += " WHERE {filters}".format(filters=" AND ".join(filters)) c = self._conn.execute(request, tuple(values)) datas = c.fetchall() diff --git a/src/sakia/gui/component/__init__.py b/src/sakia/gui/component/__init__.py deleted file mode 100644 index 03eacd23..00000000 --- a/src/sakia/gui/component/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ['controller', 'model'] diff --git a/src/sakia/gui/component/controller.py b/src/sakia/gui/component/controller.py deleted file mode 100644 index 2a070404..00000000 --- a/src/sakia/gui/component/controller.py +++ /dev/null @@ -1,44 +0,0 @@ -from PyQt5.QtCore import QObject - - -class ComponentController(QObject): - """ - The navigation panel - """ - - def __init__(self, parent, view, model): - """ - Constructor of the navigation component - - :param PyQt5.QtWidgets.QWidget view: the presentation - :param sakia.gui.component.model.ComponentModel model: the model - """ - super().__init__(parent) - self._view = view - self._model = model - - @property - def view(self): - raise NotImplementedError("View property not implemented") - - @property - def model(self): - raise NotImplementedError("Model property not implemented") - - @classmethod - def create(cls, parent, app, **kwargs): - raise NotImplementedError("Create method not implemented") - - def attach(self, controller): - """ - Attach an component controller to this controller - :param ComponentController controller: the attached controller - :return: the attached controller - :rtype: ComponentController - """ - if controller: - controller.setParent(self) - #controller.view.setParent(self.view) - return controller - else: - return None diff --git a/src/sakia/gui/component/model.py b/src/sakia/gui/component/model.py deleted file mode 100644 index 1ba59950..00000000 --- a/src/sakia/gui/component/model.py +++ /dev/null @@ -1,15 +0,0 @@ -from PyQt5.QtCore import QObject - - -class ComponentModel(QObject): - """ - An component - """ - - def __init__(self, parent): - """ - Constructor of an component - - :param sakia.gui.component.controller.ComponentController parent: the controller - """ - super().__init__(parent) diff --git a/src/sakia/gui/dialogs/certification/certification.ui b/src/sakia/gui/dialogs/certification/certification.ui index 7f45c2f7..f97b51dd 100644 --- a/src/sakia/gui/dialogs/certification/certification.ui +++ b/src/sakia/gui/dialogs/certification/certification.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>517</width> - <height>316</height> + <height>378</height> </rect> </property> <property name="windowTitle"> @@ -23,11 +23,24 @@ </sizepolicy> </property> <property name="title"> - <string>Community</string> + <string>Send to currency network :</string> </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="QComboBox" name="combo_community"/> + <widget class="QComboBox" name="combo_currency"/> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>With the following identity : </string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="combo_pubkey"/> </item> <item> <widget class="QGroupBox" name="groupBox_3"> diff --git a/src/sakia/gui/dialogs/certification/controller.py b/src/sakia/gui/dialogs/certification/controller.py index d5a64f47..cba3047a 100644 --- a/src/sakia/gui/dialogs/certification/controller.py +++ b/src/sakia/gui/dialogs/certification/controller.py @@ -1,39 +1,38 @@ import asyncio -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, QObject from PyQt5.QtWidgets import QApplication -from sakia.decorators import asyncify, once_at_a_time -from sakia.gui.component.controller import ComponentController +from sakia.data.entities import Identity +from sakia.decorators import asyncify from sakia.gui.sub.search_user.controller import SearchUserController from sakia.gui.sub.user_information.controller import UserInformationController +from sakia.gui.password_asker import PasswordAskerDialog from .model import CertificationModel from .view import CertificationView +import attr -class CertificationController(ComponentController): +@attr.s() +class CertificationController(QObject): """ The Certification view """ - def __init__(self, parent, view, model, search_user, user_information): - """ - Constructor of the Certification component + view = attr.ib() + model = attr.ib() + search_user = attr.ib(default=None) + user_information = attr.ib(default=None) - :param sakia.gui.certification.view.CertificationView: the view - :param sakia.gui.certification.model.CertificationModel model: the model - :param sakia.gui.search_user.controller.SearchUserController search_user: the search user component - :param sakia.gui.user_information.controller.UserInformationController search_user: the search user component - """ - super().__init__(parent, view, model) + def __attrs_post_init__(self): + super().__init__() self.view.button_box.accepted.connect(self.accept) self.view.button_box.rejected.connect(self.reject) - self.view.combo_community.currentIndexChanged.connect(self.change_current_community) - self.search_user = search_user - self.user_information = user_information + self.view.combo_currency.currentIndexChanged.connect(self.change_currency) + self.view.combo_pubkey.currentIndexChanged.connect(self.change_connection) @classmethod - def create(cls, parent, app, **kwargs): + def create(cls, parent, app): """ Instanciate a Certification component :param sakia.gui.component.controller.ComponentController parent: @@ -41,66 +40,55 @@ class CertificationController(ComponentController): :return: a new Certification controller :rtype: CertificationController """ - view = CertificationView(parent.view, None, None) - model = CertificationModel(None, app) - certification = cls(parent, view, model, None, None) + model = CertificationModel(app) + certification = cls(view, model, None, None) - search_user = SearchUserController.create(certification, app) + search_user = SearchUserController.create(certification, app, model.available_currencies()[0]) certification.set_search_user(search_user) user_information = UserInformationController.create(certification, app, - account=model.account, - community=model.community, - identity=None) + model.available_currencies()[0], None) certification.set_user_information(user_information) - model.setParent(certification) + + view.set_currencies(certification.model.available_currencies()) + view.set_keys(certification.model.available_connections(certification.model.available_currencies()[0])) return certification @classmethod - def open_dialog(cls, parent, app, account, community, password_asker): + def open_dialog(cls, parent, app, connection): """ Certify and identity :param sakia.gui.component.controller.ComponentController parent: the parent :param sakia.core.Application app: the application :param sakia.core.Account account: the account certifying the identity :param sakia.core.Community community: the community - :param sakia.gui.password_asker.PasswordAsker password_asker: the password asker :return: """ - dialog = cls.create(parent, app, account=account, community=community, password_asker=password_asker) - if community: - dialog.view.combo_community.setCurrentText(community.name) + dialog = cls.create(parent, app) + if connection: + dialog.view.combo_currency.setCurrentText(connection.currency) dialog.refresh() return dialog.exec() @classmethod - async def certify_identity(cls, parent, app, account, password_asker, community, identity): + async def certify_identity(cls, parent, app, connection, identity): """ Certify and identity :param sakia.gui.component.controller.ComponentController parent: the parent :param sakia.core.Application app: the application :param sakia.core.Account account: the account certifying the identity - :param sakia.gui.password_asker.PasswordAsker password_asker: the password asker :param sakia.core.Community community: the community :param sakia.core.registry.Identity identity: the identity certified :return: """ - dialog = cls.create(parent, app, account=account, community=community, password_asker=password_asker) - dialog.view.combo_community.setCurrentText(community.name) + dialog = cls.create(parent, app) + dialog.view.combo_community.setCurrentText(connection.currency) dialog.view.edit_pubkey.setText(identity.pubkey) dialog.view.radio_pubkey.setChecked(True) dialog.refresh() return await dialog.async_exec() - @property - def view(self) -> CertificationView: - return self._view - - @property - def model(self) -> CertificationModel: - return self._model - def set_search_user(self, search_user): """ @@ -126,41 +114,28 @@ class CertificationController(ComponentController): Validate the dialog """ self.view.button_box.setDisabled(True) - pubkey = self.selected_pubkey() - if pubkey: - password = await self.password_asker.async_exec() - if password == "": - self.view.button_box.setEnabled(True) - return - QApplication.setOverrideCursor(Qt.WaitCursor) - result = await self.account.certify(password, self.community, pubkey) - if result[0]: - QApplication.restoreOverrideCursor() - await self.view.show_success() - self.view.accept() - else: - await self.view.show_error(result[1]) - QApplication.restoreOverrideCursor() - self.view.button_box.setEnabled(True) + password = await PasswordAskerDialog(self.model.connection).async_exec() + if password: + self.view.button_box.setEnabled(True) + return + QApplication.setOverrideCursor(Qt.WaitCursor) + if self.view.radio_pubkey.isChecked(): + result = await self.model.certify_pubkey(password, self.view.edit_pubkey.text()) + else: + result = await self.model.certify_identity(password, self.user_information.model.identity) + + if result[0]: + QApplication.restoreOverrideCursor() + await self.view.show_success() + self.view.accept() + else: + await self.view.show_error(result[1]) + QApplication.restoreOverrideCursor() + self.view.button_box.setEnabled(True) def reject(self): self.view.reject() - def selected_pubkey(self): - """ - Get selected pubkey in the widgets of the window - :return: the current pubkey - :rtype: str - """ - pubkey = None - - if self.view.recipient_mode() == CertificationView.RecipientMode.SEARCH: - if self.search_user.current_identity(): - pubkey = self.search_user.current_identity().pubkey - else: - pubkey = self.view.pubkey_value() - return pubkey - def refresh(self): stock = self.model.get_cert_stock() written, pending = self.model.nb_certifications() @@ -188,12 +163,21 @@ class CertificationController(ComponentController): Refresh user information """ pubkey = self.selected_pubkey() - self.user_information.search_identity(pubkey) - - def change_current_connection(self, index): - self.model.change_connection(index) - self.search_user.set_connection(self.model.connection) - self.user_information.change_connection(self.model.connection) + if self.search_user.identity_selected: + self.user_information.search_identity(self.search_user.model.identity()) + else: + self.user_information.search_identity(Identity(self.model.connection.currency, pubkey)) + + def change_currency(self, index): + currency = self.model.available_currencies()[index] + connections = self.model.available_connections(currency) + self.view.set_selected_key(connections[0]) + self.search_user.set_currency(currency) + self.user_information.set_currency(currency) + + def change_connection(self, index): + currency = self.model.available_currencies()[index] + self.model.set_connection(currency, index) self.refresh() def async_exec(self): diff --git a/src/sakia/gui/dialogs/certification/model.py b/src/sakia/gui/dialogs/certification/model.py index dbadf913..4d46e6d5 100644 --- a/src/sakia/gui/dialogs/certification/model.py +++ b/src/sakia/gui/dialogs/certification/model.py @@ -1,25 +1,28 @@ -from sakia.gui.component.model import ComponentModel -from sakia.data.processors import IdentitiesProcessor, CertificationsProcessor, BlockchainProcessor +from PyQt5.QtCore import QObject +from sakia.data.processors import IdentitiesProcessor, CertificationsProcessor, \ + BlockchainProcessor, ConnectionsProcessor +import attr -class CertificationModel(ComponentModel): +@attr.s() +class CertificationModel(QObject): """ The model of Certification component """ - def __init__(self, parent, app, connection): - """ - The data model of the certification dialog - :param parent: - :param sakia.app.Application app: - :param sakia.data.entities.Connection connection: the connection used to certify - """ - super().__init__(parent) - self.app = app - self.connection = connection - self._certifications_processor = CertificationsProcessor.instanciate(app) - self._identities_processor = IdentitiesProcessor.instanciate(app) - self._blockchain_processor = BlockchainProcessor.instanciate(app) + app = attr.ib() + connection = attr.ib(default=None) + _connections_processor = attr.ib(default=None) + _certifications_processor = attr.ib(default=None) + _identities_processor = attr.ib(default=None) + _blockchain_processor = attr.ib(default=None) + + def __attrs_post_init__(self): + super().__init__() + self._connections_processor = ConnectionsProcessor.instanciate(self.app) + self._certifications_processor = CertificationsProcessor.instanciate(self.app) + self._identities_processor = IdentitiesProcessor.instanciate(self.app) + self._blockchain_processor = BlockchainProcessor.instanciate(self.app) def change_connection(self, index): """ @@ -34,7 +37,7 @@ class CertificationModel(ComponentModel): :return: the certifications stock :rtype: int """ - return self.blockchain_processor.parameters(self.currency).sig_stock + return self._blockchain_processor.parameters(self.connection.currency).sig_stock def remaining_time(self): """ @@ -42,8 +45,11 @@ class CertificationModel(ComponentModel): :return: a tuple containing (days, hours, minutes, seconds) :rtype: tuple[int] """ + parameters = self._blockchain_processor.parameters(self.connection.currency) + blockchain_time = self._blockchain_processor.time(self.connection.currency) remaining_time = self._certifications_processor.cert_issuance_delay(self.connection.currency, - self.connection.pubkey) + self.connection.pubkey, + parameters, blockchain_time) days, remainder = divmod(remaining_time, 3600 * 24) hours, remainder = divmod(remainder, 3600) @@ -73,3 +79,16 @@ class CertificationModel(ComponentModel): self.connection.pubkey) return is_member and self._blockchain_processor.current_buid(self.connection.currency) + + def available_connections(self, currency): + return self._connections_processor.connections(currency=currency) + + def available_currencies(self): + return self._connections_processor.currencies() + + def set_connection(self, currency, index): + connections = self._connections_processor.connections(currency=currency) + self.connection = connections[index] + + def certify(self, password, pubkey): + self._certifications_processor.certify(self.connection, password, pubkey) \ No newline at end of file diff --git a/src/sakia/gui/dialogs/certification/view.py b/src/sakia/gui/dialogs/certification/view.py index f3802e6e..f8ed6042 100644 --- a/src/sakia/gui/dialogs/certification/view.py +++ b/src/sakia/gui/dialogs/certification/view.py @@ -18,9 +18,8 @@ class CertificationView(QDialog, Ui_CertificationDialog): OK = 3 class RecipientMode(Enum): - CONTACT = 0 - PUBKEY = 1 - SEARCH = 2 + PUBKEY = 0 + SEARCH = 1 _button_box_values = { ButtonBoxState.NO_MORE_CERTIFICATION: (False, @@ -40,35 +39,39 @@ class CertificationView(QDialog, Ui_CertificationDialog): :param parent: :param sakia.gui.search_user.view.SearchUserView search_user_view: :param sakia.gui.user_information.view.UserInformationView user_information_view: - :param list[str] communities_names: - :param list[str] contacts_names: + :param list[sakia.data.entities.Connection] connections: """ super().__init__(parent) self.setupUi(self) - self.radio_contact.toggled.connect(lambda c, radio=CertificationView.RecipientMode.CONTACT: self.recipient_mode_changed(radio)) - self.radio_pubkey.toggled.connect(lambda c, radio=CertificationView.RecipientMode.PUBKEY: self.recipient_mode_changed(radio)) - self.radio_search.toggled.connect(lambda c, radio=CertificationView.RecipientMode.SEARCH: self.recipient_mode_changed(radio)) - - for name in communities_names: - self.combo_community.addItem(name) - - for name in sorted(contacts_names): - self.combo_contact.addItem(name) - - if len(contacts_names) == 0: - self.radio_pubkey.setChecked(True) - self.radio_contact.setEnabled(False) + self.radio_pubkey.toggled.connect(lambda c, radio=CertificationView.RecipientMode.PUBKEY: + self.recipient_mode_changed(radio)) + self.radio_search.toggled.connect(lambda c, radio=CertificationView.RecipientMode.SEARCH: + self.recipient_mode_changed(radio)) self.search_user = search_user_view self.user_information_view = user_information_view - self.combo_contact.currentIndexChanged.connect(self.pubkey_changed) self.edit_pubkey.textChanged.connect(self.pubkey_changed) - self.radio_contact.toggled.connect(self.pubkey_changed) self.radio_search.toggled.connect(self.pubkey_changed) self.radio_pubkey.toggled.connect(self.pubkey_changed) + def set_keys(self, connections): + self.combo_pubkey.clear() + for c in connections: + self.combo_pubkey.addItem(c.title()) + + def set_selected_key(self, connection): + """ + :param sakia.data.entities.Connection connection: + """ + self.combo_pubkey.setCurrentText(connection.title()) + + def set_currencies(self, currencies): + self.combo_currency.clear() + for c in currencies: + self.combo_currency.addItem(c) + def set_search_user(self, search_user_view): """ @@ -84,16 +87,11 @@ class CertificationView(QDialog, Ui_CertificationDialog): self.layout_target_choice.addWidget(user_information_view) def recipient_mode(self): - if self.radio_contact.isChecked(): - return CertificationView.RecipientMode.CONTACT - elif self.radio_search.isChecked(): + if self.radio_search.isChecked(): return CertificationView.RecipientMode.SEARCH else: return CertificationView.RecipientMode.PUBKEY - def selected_contact(self): - return self.combo_contact.currentText() - def pubkey_value(self): return self.edit_pubkey.text() @@ -157,5 +155,4 @@ class CertificationView(QDialog, Ui_CertificationDialog): :param str radio: """ self.edit_pubkey.setEnabled(radio == CertificationView.RecipientMode.PUBKEY) - self.combo_contact.setEnabled(radio == CertificationView.RecipientMode.CONTACT) self.search_user.setEnabled(radio == CertificationView.RecipientMode.SEARCH) diff --git a/src/sakia/gui/dialogs/connection_cfg/controller.py b/src/sakia/gui/dialogs/connection_cfg/controller.py index 146d45b8..ca964ad0 100644 --- a/src/sakia/gui/dialogs/connection_cfg/controller.py +++ b/src/sakia/gui/dialogs/connection_cfg/controller.py @@ -2,19 +2,18 @@ import asyncio import logging from aiohttp.errors import DisconnectedError, ClientError, TimeoutError - from duniterpy.documents import MalformedDocumentError from sakia.errors import NoPeerAvailable from sakia.decorators import asyncify from sakia.data.processors import IdentitiesProcessor, NodesProcessor from sakia.data.connectors import BmaConnector -from sakia.gui.component.controller import ComponentController from sakia.gui.password_asker import PasswordAskerDialog, detect_non_printable from .model import ConnectionConfigModel from .view import ConnectionConfigView +from PyQt5.QtCore import QObject -class ConnectionConfigController(ComponentController): +class ConnectionConfigController(QObject): """ The AccountConfigController view """ @@ -30,7 +29,7 @@ class ConnectionConfigController(ComponentController): :param sakia.gui.account_cfg.view.AccountConfigCView: the view :param sakia.gui.account_cfg.model.AccountConfigModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) self.step_node = asyncio.Future() self.step_key = asyncio.Future() @@ -45,7 +44,7 @@ class ConnectionConfigController(ComponentController): self._logger = logging.getLogger('sakia') @classmethod - def create(cls, parent, app, **kwargs): + def create(cls, parent, app): """ Instanciate a AccountConfigController component :param sakia.gui.component.controller.ComponentController parent: diff --git a/src/sakia/gui/dialogs/connection_cfg/model.py b/src/sakia/gui/dialogs/connection_cfg/model.py index 21890963..1489d1f5 100644 --- a/src/sakia/gui/dialogs/connection_cfg/model.py +++ b/src/sakia/gui/dialogs/connection_cfg/model.py @@ -1,5 +1,5 @@ import aiohttp - +from PyQt5.QtCore import QObject from duniterpy.documents import BlockUID, BMAEndpoint from duniterpy.api import bma, errors from duniterpy.key import SigningKey @@ -7,10 +7,9 @@ from sakia.data.entities import Connection, Identity, Node from sakia.data.connectors import NodeConnector from sakia.data.processors import ConnectionsProcessor, NodesProcessor, BlockchainProcessor, \ SourcesProcessor, CertificationsProcessor, TransactionsProcessor -from sakia.gui.component.model import ComponentModel -class ConnectionConfigModel(ComponentModel): +class ConnectionConfigModel(QObject): """ The model of AccountConfig component """ diff --git a/src/sakia/gui/dialogs/revocation/controller.py b/src/sakia/gui/dialogs/revocation/controller.py index 9dd31568..116ecd2d 100644 --- a/src/sakia/gui/dialogs/revocation/controller.py +++ b/src/sakia/gui/dialogs/revocation/controller.py @@ -1,13 +1,13 @@ import asyncio +from PyQt5.QtCore import QObject from duniterpy.documents import MalformedDocumentError from sakia.decorators import asyncify -from sakia.gui.component.controller import ComponentController from .model import RevocationModel from .view import RevocationView -class RevocationController(ComponentController): +class RevocationController(QObject): """ The revocation view """ @@ -19,7 +19,7 @@ class RevocationController(ComponentController): :param sakia.gui.revocation.view.revocationView: the view :param sakia.gui.revocation.model.revocationModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) self.handle_next_step(init=True) self.view.button_next.clicked.connect(lambda checked: self.handle_next_step(False)) @@ -38,7 +38,7 @@ class RevocationController(ComponentController): self._current_step = 0 @classmethod - def create(cls, parent, app, **kwargs): + def create(cls, parent, app, account): """ Instanciate a revocation component :param sakia.gui.component.controller.ComponentController parent: @@ -46,7 +46,6 @@ class RevocationController(ComponentController): :return: a new revocation controller :rtype: revocationController """ - account = kwargs['account'] view = RevocationView(parent.view) model = RevocationModel(None, app, account) revocation = cls(parent, view, model) diff --git a/src/sakia/gui/dialogs/revocation/model.py b/src/sakia/gui/dialogs/revocation/model.py index 8647369b..3030926e 100644 --- a/src/sakia/gui/dialogs/revocation/model.py +++ b/src/sakia/gui/dialogs/revocation/model.py @@ -5,7 +5,7 @@ from sakia.core.net import Node import aiohttp -class RevocationModel(ComponentModel): +class RevocationModel(QObject): """ The model of HomeScreen component """ diff --git a/src/sakia/gui/dialogs/transfer/controller.py b/src/sakia/gui/dialogs/transfer/controller.py index 18c18980..45a0d389 100644 --- a/src/sakia/gui/dialogs/transfer/controller.py +++ b/src/sakia/gui/dialogs/transfer/controller.py @@ -1,18 +1,17 @@ import asyncio import logging -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, QObject from PyQt5.QtWidgets import QApplication from sakia.decorators import asyncify -from sakia.gui.component.controller import ComponentController from sakia.gui.sub.search_user.controller import SearchUserController from sakia.gui.sub.user_information.controller import UserInformationController from .model import TransferModel from .view import TransferView -class TransferController(ComponentController): +class TransferController(QObject): """ The transfer component controller """ @@ -24,7 +23,7 @@ class TransferController(ComponentController): :param sakia.gui.transfer.view.TransferView: the view :param sakia.gui.transfer.model.TransferModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) self.password_asker = password_asker self.search_user = search_user self.user_information = user_information @@ -36,7 +35,7 @@ class TransferController(ComponentController): self.view.spinbox_relative.valueChanged.connect(self.handle_relative_change) @classmethod - def create(cls, parent, app, **kwargs): + def create(cls, parent, app, account, community, transfer, password_asker): """ Instanciate a transfer component :param sakia.gui.component.controller.ComponentController parent: @@ -44,10 +43,6 @@ class TransferController(ComponentController): :return: a new Transfer controller :rtype: TransferController """ - account = kwargs['account'] - community = kwargs['community'] - transfer = kwargs['transfer'] - password_asker = kwargs['password_asker'] communities_names = [c.name for c in account.communities] wallets_names = [w.name for w in account.wallets] contacts_names = [c['name'] for c in account.contacts] diff --git a/src/sakia/gui/dialogs/transfer/model.py b/src/sakia/gui/dialogs/transfer/model.py index 10d306a2..ae51a9a0 100644 --- a/src/sakia/gui/dialogs/transfer/model.py +++ b/src/sakia/gui/dialogs/transfer/model.py @@ -1,7 +1,7 @@ -from sakia.gui.component.model import ComponentModel +from PyQt5.QtCore import QObject -class TransferModel(ComponentModel): +class TransferModel(QObject): """ The model of transfer component """ diff --git a/src/sakia/gui/main_window/controller.py b/src/sakia/gui/main_window/controller.py index 6125bd15..35945111 100644 --- a/src/sakia/gui/main_window/controller.py +++ b/src/sakia/gui/main_window/controller.py @@ -14,7 +14,6 @@ from PyQt5.QtGui import QIcon from ..password_asker import PasswordAskerDialog from ...__init__ import __version__ from ..widgets import toast -from sakia.gui.component.controller import ComponentController from .view import MainWindowView from .model import MainWindowModel from .status_bar.controller import StatusBarController @@ -22,7 +21,7 @@ from .toolbar.controller import ToolbarController from ..navigation.controller import NavigationController -class MainWindowController(ComponentController): +class MainWindowController(QObject): """ classdocs """ @@ -40,12 +39,14 @@ class MainWindowController(ComponentController): :type: sakia.core.app.Application """ # Set up the user interface from Designer. - super().__init__(None, view, model) + super().__init__() + self.view = view + self.model = model self.initialized = False self.password_asker = password_asker - self.status_bar = self.attach(status_bar) - self.toolbar = self.attach(toolbar) - self.navigation = self.attach(navigation) + self.status_bar = status_bar + self.toolbar = toolbar + self.navigation = navigation self.stacked_widgets = {} self.view.bottom_layout.insertWidget(0, self.navigation.view) self.view.top_layout.addWidget(self.toolbar.view) @@ -54,7 +55,7 @@ class MainWindowController(ComponentController): QApplication.setWindowIcon(QIcon(":/icons/sakia_logo")) @classmethod - def create(cls, parent, app, **kwargs): + def create(cls, app, password_asker, status_bar, toolbar, navigation): """ Instanciate a navigation component :param sakia.gui.status_bar.controller.StatusBarController status_bar: the controller of the status bar component @@ -64,10 +65,6 @@ class MainWindowController(ComponentController): :return: a new Navigation controller :rtype: MainWindowController """ - password_asker = kwargs['password_asker'] - status_bar = kwargs['status_bar'] - toolbar = kwargs['toolbar'] - navigation = kwargs['navigation'] view = MainWindowView() model = MainWindowModel(None, app) main_window = cls(view, model, password_asker, status_bar, toolbar, navigation) @@ -78,11 +75,12 @@ class MainWindowController(ComponentController): @classmethod def startup(cls, app): password_asker = PasswordAskerDialog(None) - main_window = cls.create(None, app, password_asker=password_asker, - status_bar=StatusBarController.create(None, app), - navigation=NavigationController.create(None, app), - toolbar=ToolbarController.create(None, app, - app.parameters, None) + navigation = NavigationController.create(None, app) + toolbar = ToolbarController.create(app, navigation) + main_window = cls.create(app, password_asker=password_asker, + status_bar=StatusBarController.create(app), + navigation=navigation, + toolbar=toolbar ) #app.version_requested.connect(main_window.latest_version_requested) @@ -93,14 +91,6 @@ class MainWindowController(ComponentController): main_window.refresh() return main_window - @property - def view(self) -> MainWindowView: - return self._view - - @property - def model(self) -> MainWindowModel: - return self._model - @pyqtSlot(str) def display_error(self, error): QMessageBox.critical(self, ":(", diff --git a/src/sakia/gui/main_window/model.py b/src/sakia/gui/main_window/model.py index d504aaa8..81714afe 100644 --- a/src/sakia/gui/main_window/model.py +++ b/src/sakia/gui/main_window/model.py @@ -1,7 +1,7 @@ -from sakia.gui.component.model import ComponentModel +from PyQt5.QtCore import QObject -class MainWindowModel(ComponentModel): +class MainWindowModel(QObject): """ The model of Navigation component """ diff --git a/src/sakia/gui/main_window/status_bar/controller.py b/src/sakia/gui/main_window/status_bar/controller.py index 31775aa9..d00f57f3 100644 --- a/src/sakia/gui/main_window/status_bar/controller.py +++ b/src/sakia/gui/main_window/status_bar/controller.py @@ -1,28 +1,29 @@ -from PyQt5.QtCore import QLocale, pyqtSlot, QDateTime, QTimer -from sakia.gui.component.controller import ComponentController +from PyQt5.QtCore import QLocale, pyqtSlot, QDateTime, QTimer, QObject from .model import StatusBarModel from .view import StatusBarView import logging -class StatusBarController(ComponentController): +class StatusBarController(QObject): """ The navigation panel """ - def __init__(self, parent, view, model): + def __init__(self, view, model): """ Constructor of the navigation component :param sakia.gui.status_bar.view.StatusBarView view: the presentation :param sakia.core.status_bar.model.StatusBarModel model: the model """ - super().__init__(parent, view, model) + super().__init__() + self.view = view + self.model = model view.combo_referential.currentIndexChanged[int].connect(self.referential_changed) self.update_time() @classmethod - def create(cls, parent, app, **kwargs): + def create(cls, app, **kwargs): """ Instanciate a navigation component :param sakia.gui.main_window.controller.MainWindowController parent: @@ -32,18 +33,9 @@ class StatusBarController(ComponentController): view = StatusBarView(None) model = StatusBarModel(None, app) - status_bar = cls(parent, view, model) - model.setParent(status_bar) + status_bar = cls(view, model) return status_bar - @property - def view(self) -> StatusBarView: - return self._view - - @property - def model(self) -> StatusBarModel: - return self._model - @pyqtSlot() def update_time(self): dateTime = QDateTime.currentDateTime() diff --git a/src/sakia/gui/main_window/status_bar/model.py b/src/sakia/gui/main_window/status_bar/model.py index f2582977..f55202aa 100644 --- a/src/sakia/gui/main_window/status_bar/model.py +++ b/src/sakia/gui/main_window/status_bar/model.py @@ -1,10 +1,8 @@ -import logging - -from sakia.gui.component.model import ComponentModel +from PyQt5.QtCore import QObject from sakia.money import Referentials -class StatusBarModel(ComponentModel): +class StatusBarModel(QObject): """ The model of status bar component """ diff --git a/src/sakia/gui/main_window/toolbar/controller.py b/src/sakia/gui/main_window/toolbar/controller.py index 3301f3d5..db44a5f2 100644 --- a/src/sakia/gui/main_window/toolbar/controller.py +++ b/src/sakia/gui/main_window/toolbar/controller.py @@ -1,10 +1,9 @@ import logging -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, QObject from PyQt5.QtWidgets import QDialog, QMessageBox -from sakia.decorators import asyncify, once_at_a_time -from sakia.gui.component.controller import ComponentController +from sakia.decorators import asyncify from sakia.gui.dialogs.connection_cfg.controller import ConnectionConfigController from sakia.gui.dialogs.certification.controller import CertificationController from sakia.gui.dialogs.transfer.controller import TransferController @@ -14,18 +13,20 @@ from .model import ToolbarModel from .view import ToolbarView -class ToolbarController(ComponentController): +class ToolbarController(QObject): """ The navigation panel """ - def __init__(self, parent, view, model): + def __init__(self, view, model): """ :param sakia.gui.component.controller.ComponentController parent: the parent :param sakia.gui.toolbar.view.ToolbarView view: :param sakia.gui.toolbar.model.ToolbarModel model: """ - super().__init__(parent, view, model) + super().__init__() + self.view = view + self.model = model self.view.button_certification.clicked.connect(self.open_certification_dialog) self.view.button_send_money.clicked.connect(self.open_transfer_money_dialog) self.view.action_gen_revokation.triggered.connect(self.action_save_revokation) @@ -34,30 +35,19 @@ class ToolbarController(ComponentController): self.view.action_create_account.triggered.connect(self.open_create_account_dialog) @classmethod - def create(cls, parent, app, parameters, currency): + def create(cls, app, navigation): """ Instanciate a navigation component - :param sakia.gui.agent.controller.AgentController parent: :param sakia.app.Application app: - :param sakia.entities.data.UserParameters parameters: - :param str currency: - :return: a new Toolbar controller - :rtype: ToolbarController + :param sakia.gui.navigation.controller.NavigationController navigation: + :return: a new Navigation controller + :rtype: NavigationController """ view = ToolbarView(None) - model = ToolbarModel(None, app, parameters, currency) - toolbar = cls(parent, view, model) - model.setParent(toolbar) + model = ToolbarModel(app, navigation.model) + toolbar = cls(view, model) return toolbar - @property - def view(self) -> ToolbarView: - return self._view - - @property - def model(self) -> ToolbarModel: - return self._model - @asyncify async def action_save_revokation(self, checked=False): password = await self.password_asker.async_exec() @@ -146,39 +136,36 @@ The process to join back the community later will have to be done again.""") await QAsyncMessageBox.critical(self, self.tr("UID"), result[1]) - @once_at_a_time - @asyncify async def refresh_quality_buttons(self): - if self.account and self.community: - try: - account_identity = await self.account.identity(self.community) - published_uid = await account_identity.published_uid(self.community) - uid_is_revokable = await account_identity.uid_is_revokable(self.community) - if published_uid: - logging.debug("UID Published") - self.action_revoke_uid.setEnabled(uid_is_revokable) - is_member = await account_identity.is_member(self.community) - if is_member: - self.button_membership.setText(self.tr("Renew membership")) - self.button_membership.setEnabled(True) - self.button_certification.setEnabled(True) - self.action_publish_uid.setEnabled(False) - else: - logging.debug("Not a member") - self.button_membership.setText(self.tr("Send membership demand")) - self.button_membership.setEnabled(True) - self.action_publish_uid.setEnabled(False) - if await self.community.get_block(0) is not None: - self.button_certification.setEnabled(False) + try: + account_identity = self.app.identity(self.community) + published_uid = await account_identity.published_uid(self.community) + uid_is_revokable = await account_identity.uid_is_revokable(self.community) + if published_uid: + logging.debug("UID Published") + self.action_revoke_uid.setEnabled(uid_is_revokable) + is_member = await account_identity.is_member(self.community) + if is_member: + self.button_membership.setText(self.tr("Renew membership")) + self.button_membership.setEnabled(True) + self.button_certification.setEnabled(True) + self.action_publish_uid.setEnabled(False) else: - logging.debug("UID not published") - self.button_membership.setEnabled(False) - self.button_certification.setEnabled(False) - self.action_publish_uid.setEnabled(True) - except LookupFailureError: + logging.debug("Not a member") + self.button_membership.setText(self.tr("Send membership demand")) + self.button_membership.setEnabled(True) + self.action_publish_uid.setEnabled(False) + if await self.community.get_block(0) is not None: + self.button_certification.setEnabled(False) + else: + logging.debug("UID not published") self.button_membership.setEnabled(False) self.button_certification.setEnabled(False) - self.action_publish_uid.setEnabled(False) + self.action_publish_uid.setEnabled(True) + except LookupFailureError: + self.button_membership.setEnabled(False) + self.button_certification.setEnabled(False) + self.action_publish_uid.setEnabled(False) def set_account(self, account): """ @@ -196,9 +183,7 @@ The process to join back the community later will have to be done again.""") def open_certification_dialog(self): CertificationController.open_dialog(self, self.model.app, - self.model.account, - self.model.community, - self.password_asker) + self.model.navigation_model.current_connection()) def open_revocation_dialog(self): RevocationDialog.open_dialog(self.app, @@ -207,8 +192,7 @@ The process to join back the community later will have to be done again.""") def open_transfer_money_dialog(self): TransferController.open_dialog(self, self.model.app, account=self.model.account, - password_asker=self.password_asker, - community=self.model.community) + password_asker=self.password_asker) def open_create_account_dialog(self): ConnectionConfigController.create_connection(self, self.model.app) diff --git a/src/sakia/gui/main_window/toolbar/model.py b/src/sakia/gui/main_window/toolbar/model.py index 68a01160..56a49cb5 100644 --- a/src/sakia/gui/main_window/toolbar/model.py +++ b/src/sakia/gui/main_window/toolbar/model.py @@ -1,13 +1,15 @@ -from sakia.gui.component.model import ComponentModel +from PyQt5.QtCore import QObject +import attr -class ToolbarModel(ComponentModel): +@attr.s() +class ToolbarModel(QObject): """ The model of Navigation component """ - def __init__(self, parent, app, account, community): - super().__init__(parent) - self.app = app - self.account = account - self.community = community + app = attr.ib() + navigation_model = attr.ib() + + def __attrs_post_init__(self): + super().__init__() diff --git a/src/sakia/gui/navigation/controller.py b/src/sakia/gui/navigation/controller.py index cccc645a..be45fff6 100644 --- a/src/sakia/gui/navigation/controller.py +++ b/src/sakia/gui/navigation/controller.py @@ -1,5 +1,4 @@ from .model import NavigationModel -from sakia.gui.component.controller import ComponentController from .view import NavigationView from .txhistory.controller import TxHistoryController from .homescreen.controller import HomeScreenController @@ -7,12 +6,11 @@ from .network.controller import NetworkController from .identities.controller import IdentitiesController from .informations.controller import InformationsController from .graphs.wot.controller import WotController -from sakia.data.repositories import ConnectionsRepo from sakia.data.entities import Connection -from PyQt5.QtCore import pyqtSignal +from PyQt5.QtCore import pyqtSignal, QObject -class NavigationController(ComponentController): +class NavigationController(QObject): """ The navigation panel """ @@ -26,7 +24,9 @@ class NavigationController(ComponentController): :param sakia.gui.navigation.view view: the view :param sakia.gui.navigation.model.NavigationModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) + self.view = view + self.model = model self.components = { 'TxHistory': TxHistoryController, 'HomeScreen': HomeScreenController, @@ -52,21 +52,13 @@ class NavigationController(ComponentController): navigation.init_navigation() return navigation - @property - def view(self) -> NavigationView: - return self._view - - @property - def model(self) -> NavigationModel: - return self._model - def init_navigation(self): def parse_node(node_data): - if 'component' in node_data['node']: - component_class = self.components[node_data['node']['component']] - component = component_class.create(self, self.model.app, **node_data['node']) + if 'component' in node_data: + component_class = self.components[node_data['component']] + component = component_class.create(self, self.model.app, **node_data['dependencies']) widget = self.view.add_widget(component.view) - node_data['node']['widget'] = widget + node_data['widget'] = widget if 'children' in node_data: for child in node_data['children']: parse_node(child) diff --git a/src/sakia/gui/navigation/graphs/base/controller.py b/src/sakia/gui/navigation/graphs/base/controller.py index a8e57dc2..00b391d5 100644 --- a/src/sakia/gui/navigation/graphs/base/controller.py +++ b/src/sakia/gui/navigation/graphs/base/controller.py @@ -1,14 +1,13 @@ import asyncio -from PyQt5.QtCore import pyqtSlot +from PyQt5.QtCore import pyqtSlot, QObject from PyQt5.QtGui import QCursor from sakia.decorators import asyncify, once_at_a_time -from sakia.gui.component.controller import ComponentController from sakia.gui.widgets.context_menu import ContextMenu -class BaseGraphController(ComponentController): +class BaseGraphController(QObject): """ The homescreen view """ @@ -20,7 +19,9 @@ class BaseGraphController(ComponentController): :param sakia.gui.homescreen.view.HomeScreenView: the view :param sakia.gui.homescreen.model.HomeScreenModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) + self.view = view + self.model = model self.password_asker = password_asker def set_scene(self, scene): diff --git a/src/sakia/gui/navigation/graphs/base/model.py b/src/sakia/gui/navigation/graphs/base/model.py index c52f2693..129c8b82 100644 --- a/src/sakia/gui/navigation/graphs/base/model.py +++ b/src/sakia/gui/navigation/graphs/base/model.py @@ -1,7 +1,6 @@ -from sakia.gui.component.model import ComponentModel +from PyQt5.QtCore import QObject - -class BaseGraphModel(ComponentModel): +class BaseGraphModel(QObject): """ The model of Navigation component """ diff --git a/src/sakia/gui/navigation/graphs/wot/controller.py b/src/sakia/gui/navigation/graphs/wot/controller.py index f03affb8..19f4d2e0 100644 --- a/src/sakia/gui/navigation/graphs/wot/controller.py +++ b/src/sakia/gui/navigation/graphs/wot/controller.py @@ -24,28 +24,16 @@ class WotController(BaseGraphController): self.reset() @classmethod - def create(cls, parent, app, **kwargs): - connection = kwargs['connection'] - blockchain_service = kwargs['blockchain_service'] - identities_service = kwargs['identities_service'] - + def create(cls, parent, app, connection, blockchain_service, identities_service): view = WotView(parent.view) model = WotModel(None, app, connection, blockchain_service, identities_service) wot = cls(parent, view, model) model.setParent(wot) - search_user = SearchUserController.create(wot, app, **{'connection': connection}) + search_user = SearchUserController.create(wot, app, currency=connection.currency) wot.view.set_search_user(search_user.view) search_user.identity_selected.connect(wot.center_on_identity) return wot - @property - def view(self) -> WotView: - return self._view - - @property - def model(self) -> WotModel: - return self._model - def center_on_identity(self, identity): """ Draw community graph centered on the identity diff --git a/src/sakia/gui/navigation/homescreen/controller.py b/src/sakia/gui/navigation/homescreen/controller.py index 796878ca..521906a0 100644 --- a/src/sakia/gui/navigation/homescreen/controller.py +++ b/src/sakia/gui/navigation/homescreen/controller.py @@ -1,9 +1,9 @@ -from sakia.gui.component.controller import ComponentController +from PyQt5.QtCore import QObject from .view import HomeScreenView from .model import HomeScreenModel -class HomeScreenController(ComponentController): +class HomeScreenController(QObject): """ The homescreen view """ @@ -15,7 +15,9 @@ class HomeScreenController(ComponentController): :param sakia.gui.homescreen.view.HomeScreenView: the view :param sakia.gui.homescreen.model.HomeScreenModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) + self.view = view + self.model = model @classmethod def create(cls, parent, app, **kwargs): @@ -31,11 +33,3 @@ class HomeScreenController(ComponentController): homescreen = cls(parent, view, model) model.setParent(homescreen) return homescreen - - @property - def view(self) -> HomeScreenView: - return self._view - - @property - def model(self) -> HomeScreenModel: - return self._model \ No newline at end of file diff --git a/src/sakia/gui/navigation/homescreen/model.py b/src/sakia/gui/navigation/homescreen/model.py index 66db3c5c..a9dbae61 100644 --- a/src/sakia/gui/navigation/homescreen/model.py +++ b/src/sakia/gui/navigation/homescreen/model.py @@ -1,7 +1,7 @@ -from sakia.gui.component.model import ComponentModel +from PyQt5.QtCore import QObject -class HomeScreenModel(ComponentModel): +class HomeScreenModel(QObject): """ The model of HomeScreen component """ diff --git a/src/sakia/gui/navigation/identities/controller.py b/src/sakia/gui/navigation/identities/controller.py index 6eef79a5..1e258525 100644 --- a/src/sakia/gui/navigation/identities/controller.py +++ b/src/sakia/gui/navigation/identities/controller.py @@ -1,17 +1,17 @@ import logging from PyQt5.QtGui import QCursor +from PyQt5.QtCore import QObject from sakia.errors import NoPeerAvailable from duniterpy.api import errors from sakia.decorators import once_at_a_time, asyncify -from sakia.gui.component.controller import ComponentController from sakia.gui.widgets.context_menu import ContextMenu from .model import IdentitiesModel from .view import IdentitiesView -class IdentitiesController(ComponentController): +class IdentitiesController(QObject): """ The navigation panel """ @@ -23,7 +23,9 @@ class IdentitiesController(ComponentController): :param sakia.gui.identities.view.IdentitiesView view: the view :param sakia.gui.identities.model.IdentitiesModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) + self.view = view + self.model = model self.password_asker = password_asker self.view.search_by_text_requested.connect(self.search_text) self.view.search_directly_connected_requested.connect(self.search_direct_connections) @@ -31,14 +33,6 @@ class IdentitiesController(ComponentController): table_model = self.model.init_table_model() self.view.set_table_identities_model(table_model) - @property - def view(self) -> IdentitiesView: - return self._view - - @property - def model(self) -> IdentitiesModel: - return self._model - @property def app(self): return self.model.app @@ -52,11 +46,7 @@ class IdentitiesController(ComponentController): return self.model.account @classmethod - def create(cls, parent, app, **kwargs): - connection = kwargs['connection'] - blockchain_service = kwargs['blockchain_service'] - identities_service = kwargs['identities_service'] - + def create(cls, parent, app, connection, blockchain_service, identities_service): view = IdentitiesView(parent.view) model = IdentitiesModel(None, app, connection, blockchain_service, identities_service) identities = cls(parent, view, model) diff --git a/src/sakia/gui/navigation/identities/model.py b/src/sakia/gui/navigation/identities/model.py index b2728206..9ec85aba 100644 --- a/src/sakia/gui/navigation/identities/model.py +++ b/src/sakia/gui/navigation/identities/model.py @@ -1,9 +1,8 @@ -from PyQt5.QtCore import Qt -from sakia.gui.component.model import ComponentModel +from PyQt5.QtCore import Qt, QObject from .table_model import IdentitiesFilterProxyModel, IdentitiesTableModel -class IdentitiesModel(ComponentModel): +class IdentitiesModel(QObject): """ The model of the identities component """ diff --git a/src/sakia/gui/navigation/informations/controller.py b/src/sakia/gui/navigation/informations/controller.py index 764afc95..a19b6dd9 100644 --- a/src/sakia/gui/navigation/informations/controller.py +++ b/src/sakia/gui/navigation/informations/controller.py @@ -1,15 +1,15 @@ import logging +from PyQt5.QtCore import QObject from sakia.errors import NoPeerAvailable from duniterpy.api import errors from sakia.decorators import asyncify -from sakia.gui.component.controller import ComponentController from .model import InformationsModel from .view import InformationsView -class InformationsController(ComponentController): +class InformationsController(QObject): """ The informations component """ @@ -21,7 +21,9 @@ class InformationsController(ComponentController): :param sakia.gui.informations.view.InformationsView view: the view :param sakia.gui.informations.model.InformationsModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) + self.view = view + self.model = model @property def informations_view(self): @@ -31,12 +33,7 @@ class InformationsController(ComponentController): return self.view @classmethod - def create(cls, parent, app, **kwargs): - connection = kwargs['connection'] - blockchain_service = kwargs['blockchain_service'] - identities_service = kwargs['identities_service'] - sources_service = kwargs['sources_service'] - + def create(cls, parent, app, connection, blockchain_service, identities_service, sources_service): view = InformationsView(parent.view) model = InformationsModel(None, app, connection, blockchain_service, identities_service, sources_service) informations = cls(parent, view, model) @@ -44,14 +41,6 @@ class InformationsController(ComponentController): informations.init_view_text() return informations - @property - def view(self) -> InformationsView: - return self._view - - @property - def model(self) -> InformationsModel: - return self._model - @asyncify async def init_view_text(self): """ diff --git a/src/sakia/gui/navigation/informations/model.py b/src/sakia/gui/navigation/informations/model.py index 570fd613..20474455 100644 --- a/src/sakia/gui/navigation/informations/model.py +++ b/src/sakia/gui/navigation/informations/model.py @@ -1,15 +1,14 @@ import logging import math -from PyQt5.QtCore import QLocale, QDateTime, pyqtSignal +from PyQt5.QtCore import QLocale, QDateTime, pyqtSignal, QObject from sakia.errors import NoPeerAvailable from sakia.money.currency import shortened -from sakia.gui.component.model import ComponentModel from sakia.money import Referentials -class InformationsModel(ComponentModel): +class InformationsModel(QObject): """ An component """ diff --git a/src/sakia/gui/navigation/model.py b/src/sakia/gui/navigation/model.py index 49f97c03..3b8afb11 100644 --- a/src/sakia/gui/navigation/model.py +++ b/src/sakia/gui/navigation/model.py @@ -1,9 +1,9 @@ -from sakia.gui.component.model import ComponentModel +from PyQt5.QtCore import QObject from sakia.models.generic_tree import GenericTreeModel from PyQt5.QtCore import pyqtSignal -class NavigationModel(ComponentModel): +class NavigationModel(QObject): """ The model of Navigation component """ @@ -23,64 +23,79 @@ class NavigationModel(ComponentModel): def init_navigation_data(self): self.navigation = [ { - 'node': { - 'title': self.app.parameters.profile_name, - 'component': "HomeScreen", - 'parameters': self.app.parameters - }, + 'title': self.app.parameters.profile_name, + 'component': "HomeScreen", + 'parameters': self.app.parameters, + 'dependencies': {}, + 'misc': {}, 'children': [] } ] self._current_data = self.navigation[0] for connection in self.app.db.connections_repo.get_all(): self.navigation[0]['children'].append({ - 'node': { - 'title': connection.currency, - 'component': "Informations", + 'title': connection.currency, + 'component': "Informations", + 'dependencies': { 'blockchain_service': self.app.blockchain_services[connection.currency], 'identities_service': self.app.identities_services[connection.currency], 'sources_service': self.app.sources_services[connection.currency], 'connection': connection, }, + 'misc': { + 'connection': connection + }, 'children': [ { - 'node': { - 'title': self.tr('Transfers'), - 'icon': ':/icons/tx_icon', - 'component': "TxHistory", + 'title': self.tr('Transfers'), + 'icon': ':/icons/tx_icon', + 'component': "TxHistory", + 'dependencies': { 'connection': connection, 'identities_service': self.app.identities_services[connection.currency], 'blockchain_service': self.app.blockchain_services[connection.currency], 'transactions_service': self.app.transactions_services[connection.currency], "sources_service": self.app.sources_services[connection.currency] - } + }, + 'misc': { + 'connection': connection + } }, { - 'node': { - 'title': self.tr('Network'), - 'icon': ':/icons/network_icon', - 'component': "Network", + 'title': self.tr('Network'), + 'icon': ':/icons/network_icon', + 'component': "Network", + 'dependencies': { 'network_service': self.app.network_services[connection.currency], + }, + 'misc': { + 'connection': connection } }, { - 'node': { - 'title': self.tr('Identities'), - 'icon': ':/icons/members_icon', - 'component': "Identities", + 'title': self.tr('Identities'), + 'icon': ':/icons/members_icon', + 'component': "Identities", + 'dependencies': { 'connection': connection, 'blockchain_service': self.app.blockchain_services[connection.currency], 'identities_service': self.app.identities_services[connection.currency], + }, + 'misc': { + 'connection': connection } }, { - 'node': { - 'title': self.tr('Web of Trust'), - 'icon': ':/icons/wot_icon', - 'component': "Wot", + 'title': self.tr('Web of Trust'), + 'icon': ':/icons/wot_icon', + 'component': "Wot", + 'dependencies': { 'connection': connection, 'blockchain_service': self.app.blockchain_services[connection.currency], 'identities_service': self.app.identities_services[connection.currency], + }, + 'misc': { + 'connection': connection } } ] @@ -95,3 +110,9 @@ class NavigationModel(ComponentModel): def current_data(self, key): return self._current_data.get(key, None) + + def current_connection(self): + if self._current_data: + return self._current_data['misc'].get('connection', None) + else: + return None diff --git a/src/sakia/gui/navigation/network/controller.py b/src/sakia/gui/navigation/network/controller.py index 3647fef6..296b15a8 100644 --- a/src/sakia/gui/navigation/network/controller.py +++ b/src/sakia/gui/navigation/network/controller.py @@ -1,13 +1,13 @@ -from sakia.gui.component.controller import ComponentController from .model import NetworkModel from .view import NetworkView from PyQt5.QtWidgets import QAction, QMenu from PyQt5.QtGui import QCursor, QDesktopServices -from PyQt5.QtCore import pyqtSlot, QUrl +from PyQt5.QtCore import pyqtSlot, QUrl, QObject from duniterpy.api import bma +from sakia.data.processors import ConnectionsProcessor -class NetworkController(ComponentController): +class NetworkController(QObject): """ The network panel """ @@ -19,28 +19,28 @@ class NetworkController(ComponentController): :param sakia.gui.network.view.NetworkView: the view :param sakia.gui.network.model.NetworkModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) + self.view = view + self.model = model table_model = self.model.init_network_table_model() self.view.set_network_table_model(table_model) self.view.manual_refresh_clicked.connect(self.refresh_nodes_manually) @classmethod - def create(cls, parent, app, **kwargs): - network_service = kwargs['network_service'] + def create(cls, parent, app, network_service): + """ - view = NetworkView(parent.view) + :param PyQt5.QObject parent: + :param sakia.app.Application app: + :param sakia.services.NetworkService network_service: + :param sakia.data.entities.Connection connection: + :return: + """ + view = NetworkView(parent.view,) model = NetworkModel(None, app, network_service) txhistory = cls(parent, view, model) model.setParent(txhistory) return txhistory - - @property - def view(self) -> NetworkView: - return self._view - - @property - def model(self) -> NetworkModel: - return self._model def refresh_nodes_manually(self): self.model.refresh_nodes_once() diff --git a/src/sakia/gui/navigation/network/model.py b/src/sakia/gui/navigation/network/model.py index 1dfd1f0f..4e8c8106 100644 --- a/src/sakia/gui/navigation/network/model.py +++ b/src/sakia/gui/navigation/network/model.py @@ -1,9 +1,8 @@ -from sakia.gui.component.model import ComponentModel from .table_model import NetworkTableModel, NetworkFilterProxyModel -from PyQt5.QtCore import QModelIndex, Qt +from PyQt5.QtCore import QModelIndex, Qt, QObject -class NetworkModel(ComponentModel): +class NetworkModel(QObject): """ A network model """ diff --git a/src/sakia/gui/navigation/txhistory/controller.py b/src/sakia/gui/navigation/txhistory/controller.py index 1cfd4b48..5c451b30 100644 --- a/src/sakia/gui/navigation/txhistory/controller.py +++ b/src/sakia/gui/navigation/txhistory/controller.py @@ -1,33 +1,35 @@ import logging -from PyQt5.QtCore import QTime, pyqtSignal +from PyQt5.QtCore import QTime, pyqtSignal, QObject from PyQt5.QtGui import QCursor from sakia.decorators import asyncify, once_at_a_time -from sakia.gui.component.controller import ComponentController from sakia.gui.widgets import toast from sakia.gui.widgets.context_menu import ContextMenu from .model import TxHistoryModel from .view import TxHistoryView +import attr -class TxHistoryController(ComponentController): +@attr.s() +class TxHistoryController(QObject): """ Transfer history component controller """ view_in_wot = pyqtSignal(object) - def __init__(self, parent, view, model, password_asker=None): + view = attr.ib() + model = attr.ib() + password_asker = attr.ib() + + def __attrs_post_init__(self): """ Init :param sakia.gui.txhistory.view.TxHistoryView view: :param sakia.gui.txhistory.model.TxHistoryModel model: :param password_asker: """ - - super().__init__(parent, view, model) - self.password_asker = password_asker - + super().__init__() ts_from, ts_to = self.view.get_time_frame() model = self.model.init_history_table_model(ts_from, ts_to) self.view.set_table_history_model(model) @@ -38,28 +40,16 @@ class TxHistoryController(ComponentController): self.refresh() @classmethod - def create(cls, parent, app, **kwargs): - connection = kwargs['connection'] - identities_service = kwargs['identities_service'] - blockchain_service = kwargs['blockchain_service'] - transactions_service = kwargs['transactions_service'] - sources_service = kwargs['sources_service'] + def create(cls, parent, app, connection, + identities_service, blockchain_service, transactions_service, sources_service): view = TxHistoryView(parent.view) model = TxHistoryModel(None, app, connection, blockchain_service, identities_service, transactions_service, sources_service) - txhistory = cls(parent, view, model) + txhistory = cls(view, model, None) model.setParent(txhistory) return txhistory - @property - def view(self) -> TxHistoryView: - return self._view - - @property - def model(self) -> TxHistoryModel: - return self._model - def refresh_minimum_maximum(self): """ Refresh minimum and maximum datetime diff --git a/src/sakia/gui/navigation/txhistory/model.py b/src/sakia/gui/navigation/txhistory/model.py index ab652acb..ef2a79d1 100644 --- a/src/sakia/gui/navigation/txhistory/model.py +++ b/src/sakia/gui/navigation/txhistory/model.py @@ -1,4 +1,4 @@ -from sakia.gui.component.model import ComponentModel +from PyQt5.QtCore import QObject from .table_model import HistoryTableModel, TxFilterProxyModel from PyQt5.QtCore import Qt, QDateTime, QTime, pyqtSignal, QModelIndex from sakia.errors import NoPeerAvailable @@ -7,7 +7,7 @@ from duniterpy.api import errors import logging -class TxHistoryModel(ComponentModel): +class TxHistoryModel(QObject): """ The model of Navigation component """ diff --git a/src/sakia/gui/password_asker.py b/src/sakia/gui/password_asker.py index 036a6d1c..865bf2ba 100644 --- a/src/sakia/gui/password_asker.py +++ b/src/sakia/gui/password_asker.py @@ -68,7 +68,7 @@ class PasswordAskerDialog(QDialog, Ui_PasswordAskerDialog): QMessageBox.Ok) return False - if SigningKey(self.connection.salt, password).pubkey != self.connection.pubkey: + if SigningKey(self.connection.salt, password, self.connection.scrypt_params).pubkey != self.connection.pubkey: QMessageBox.warning(self, self.tr("Failed to get private key"), self.tr("Wrong password typed. Cannot open the private key"), QMessageBox.Ok) diff --git a/src/sakia/gui/sub/search_user/controller.py b/src/sakia/gui/sub/search_user/controller.py index d459bef1..787c2aed 100644 --- a/src/sakia/gui/sub/search_user/controller.py +++ b/src/sakia/gui/sub/search_user/controller.py @@ -1,13 +1,12 @@ -from PyQt5.QtCore import pyqtSignal +from PyQt5.QtCore import pyqtSignal, QObject from sakia.data.entities import Identity from sakia.decorators import asyncify -from sakia.gui.component.controller import ComponentController from .model import SearchUserModel from .view import SearchUserView -class SearchUserController(ComponentController): +class SearchUserController(QObject): """ The navigation panel """ @@ -21,28 +20,20 @@ class SearchUserController(ComponentController): :param sakia.gui.search_user.view.SearchUserView view: :param sakia.gui.search_user.model.SearchUserModel model: """ - super().__init__(parent, view, model) + super().__init__(parent) + self.view = view + self.model = model self.view.search_requested.connect(self.search) self.view.node_selected.connect(self.select_node) @classmethod - def create(cls, parent, app, **kwargs): - connection = kwargs['connection'] - + def create(cls, parent, app, currency): view = SearchUserView(parent.view) - model = SearchUserModel(parent, app, connection) + model = SearchUserModel(parent, app, currency) search_user = cls(parent, view, model) model.setParent(search_user) return search_user - @property - def view(self) -> SearchUserView: - return self._view - - @property - def model(self) -> SearchUserModel: - return self._model - @asyncify async def search(self, text): """ @@ -69,10 +60,5 @@ class SearchUserController(ComponentController): self.model.select_identity(index) self.identity_selected.emit(self.model.identity()) - def set_connection(self, connection): - """ - Set community - :param sakia.data.entities.Connection connection - :return: - """ - self.model.connection = connection + def set_currency(self, currency): + self.model.currency = currency diff --git a/src/sakia/gui/sub/search_user/model.py b/src/sakia/gui/sub/search_user/model.py index 029ff8d3..cd7707f0 100644 --- a/src/sakia/gui/sub/search_user/model.py +++ b/src/sakia/gui/sub/search_user/model.py @@ -1,27 +1,27 @@ -from sakia.gui.component.model import ComponentModel -from duniterpy.api import errors, bma +from PyQt5.QtCore import QObject +from duniterpy.api import errors from sakia.errors import NoPeerAvailable from sakia.data.processors import IdentitiesProcessor import logging -class SearchUserModel(ComponentModel): +class SearchUserModel(QObject): """ The model of Navigation component """ - def __init__(self, parent, app, connection): + def __init__(self, parent, app, currency): """ :param sakia.gui.search_user.controller.NetworkController parent: the controller :param sakia.app.Application app: the app - :param sakia.data.entities.Connection connection: the connection + :param str currency: the currency network to look for users """ super().__init__(parent) self.app = app self.identities_processor = IdentitiesProcessor.instanciate(app) - self.connection = connection + self.currency = currency self._nodes = list() self._current_identity = None @@ -46,7 +46,7 @@ class SearchUserModel(ComponentModel): :return: """ try: - self._nodes = await self.identities_processor.lookup(self.connection.currency, text) + self._nodes = await self.identities_processor.lookup(self.currency, text) except errors.DuniterError as e: if e.ucode == errors.NO_MATCHING_IDENTITY: self._nodes = list() diff --git a/src/sakia/gui/sub/user_information/controller.py b/src/sakia/gui/sub/user_information/controller.py index db3cfab1..5cd642dd 100644 --- a/src/sakia/gui/sub/user_information/controller.py +++ b/src/sakia/gui/sub/user_information/controller.py @@ -1,12 +1,11 @@ from PyQt5.QtWidgets import QDialog - +from PyQt5.QtCore import QObject from sakia.decorators import asyncify -from sakia.gui.component.controller import ComponentController from .model import UserInformationModel from .view import UserInformationView -class UserInformationController(ComponentController): +class UserInformationController(QObject): """ The homescreen view """ @@ -18,19 +17,12 @@ class UserInformationController(ComponentController): :param sakia.gui.homescreen.view.HomeScreenView: the view :param sakia.gui.homescreen.model.HomeScreenModel model: the model """ - super().__init__(parent, view, model) + super().__init__(parent) + self.view = view + self.model = model @classmethod - def create(cls, parent, app, **kwargs): - """ - Instanciate a homescreen component - :param sakia.gui.component.controller.ComponentController parent: - :param sakia.core.Application app: - :return: a new Homescreen controller - :rtype: UserInformationController - """ - currency = kwargs['currency'] - identity = kwargs['identity'] + def create(cls, parent, app, currency, identity): view = UserInformationView(parent.view) model = UserInformationModel(None, app, currency, identity) homescreen = cls(parent, view, model) @@ -40,32 +32,24 @@ class UserInformationController(ComponentController): @classmethod def open_dialog(cls, parent, app, currency, identity): dialog = QDialog() - user_info = cls.create(parent, app, currency=currency, identity=identity) + user_info = cls.create(parent, app, currency, identity) user_info.view.setParent(dialog) user_info.refresh() dialog.exec() - @classmethod - def as_widget(cls, parent, app, currency, identity): - return cls(app, currency, identity) - @asyncify async def refresh(self): if self.model.identity: self.view.show_busy() self.view.display_uid(self.model.identity.uid) - await self.model.load_identity() + await self.model.load_identity(self.model.identity) self.view.display_identity_timestamps(self.model.identity.pubkey, self.model.identity.timestamp, self.model.identity.membership_timestamp) self.view.hide_busy() @asyncify - async def search_identity(self, pubkey): - """ - Set identity - :param str pubkey: the pubkey - """ - await self.model.search_identity(pubkey) + async def search_identity(self, identity): + await self.model.load_identity(identity) self.refresh() def change_identity(self, identity): @@ -76,10 +60,6 @@ class UserInformationController(ComponentController): self.model.identity = identity self.refresh() - @property - def view(self) -> UserInformationView: - return self._view - - @property - def model(self) -> UserInformationModel: - return self._model \ No newline at end of file + def set_currency(self, currency): + self.model.set_currency(currency) + self.refresh() \ No newline at end of file diff --git a/src/sakia/gui/sub/user_information/model.py b/src/sakia/gui/sub/user_information/model.py index a14e74d3..3b8e62ef 100644 --- a/src/sakia/gui/sub/user_information/model.py +++ b/src/sakia/gui/sub/user_information/model.py @@ -1,16 +1,13 @@ -import logging +from PyQt5.QtCore import QObject +from sakia.data.processors import CertificationsProcessor -from sakia.errors import NoPeerAvailable -from sakia.gui.component.model import ComponentModel - - -class UserInformationModel(ComponentModel): +class UserInformationModel(QObject): """ The model of HomeScreen component """ - def __init__(self, parent, app, currency, identity, identities_service): + def __init__(self, parent, app, currency, identity): """ :param sakia.gui.user_information.controller.UserInformationController parent: @@ -20,15 +17,22 @@ class UserInformationModel(ComponentModel): :param sakia.services.IdentitiesService identities_service: the identities service of current currency """ super().__init__(parent) + self._certifications_processor = CertificationsProcessor.instanciate(app) self.app = app self.currency = currency self.identity = identity - self.identities_service = identities_service + if identity: + self.certs_sent = self._certifications_processor.certifications_sent(currency, identity.pubkey) + self.certs_received = self._certifications_processor.certifications_received(currency, identity.pubkey) + self.identities_service = self.app.identities_services[self.currency] - async def load_identity(self): + async def load_identity(self, identity): """ Ask network service to request identity informations """ - await self.identities_service.load_memberships(self.identity) - await self.identities_service.load_certifiers_of(self.identity) - await self.identities_service.load_certified_by(self.identity) + self.identity = identity + self.identity = await self.identities_service.load_memberships(self.identity) + + def set_currency(self, currency): + self.currency = currency + self.identities_service = self.app.identities_services[self.currency] diff --git a/src/sakia/gui/sub/user_information/view.py b/src/sakia/gui/sub/user_information/view.py index 1f5d663d..c2a4eef7 100644 --- a/src/sakia/gui/sub/user_information/view.py +++ b/src/sakia/gui/sub/user_information/view.py @@ -70,25 +70,6 @@ class UserInformationView(QWidget, Ui_UserInformationWidget): """ self.label_uid.setText(uid) - def display_path(self, path): - """ - Display path to identity - :param list[str] path: - """ - - text = """<tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr>"""\ - .format(self.tr('Distance'), len(path)) - if len(path) > 1: - for index, uid in enumerate(path): - if index == 0: - text += self.tr("""<tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr>""") \ - .format(self.tr('Path'), uid) - else: - text += self.tr("""<tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr>""") \ - .format('', uid) - - self.label_path.setText(text) - def show_busy(self): self.busy.show() diff --git a/src/sakia/models/generic_tree.py b/src/sakia/models/generic_tree.py index 2f83ed6d..0d1b4faf 100644 --- a/src/sakia/models/generic_tree.py +++ b/src/sakia/models/generic_tree.py @@ -76,7 +76,7 @@ class GenericTreeModel(QAbstractItemModel): @classmethod def create(cls, title, data): def parse_node(node_data, parent_item): - node = NodeItem(node_data['node'], parent_item) + node = NodeItem(node_data, parent_item) if parent_item: parent_item.appendChild(node) if 'children' in node_data: diff --git a/src/sakia/services/identities.py b/src/sakia/services/identities.py index 51769282..92f3f54e 100644 --- a/src/sakia/services/identities.py +++ b/src/sakia/services/identities.py @@ -12,7 +12,8 @@ class IdentitiesService(QObject): Identities service is managing identities data received to update data locally """ - def __init__(self, currency, identities_processor, certs_processor, blockchain_processor, bma_connector): + def __init__(self, currency, connections_processor, identities_processor, certs_processor, + blockchain_processor, bma_connector): """ Constructor the identities service @@ -23,6 +24,7 @@ class IdentitiesService(QObject): :param sakia.data.connectors.BmaConnector bma_connector: The connector to BMA API """ super().__init__() + self._connections_processor = connections_processor self._identities_processor = identities_processor self._certs_processor = certs_processor self._blockchain_processor = blockchain_processor @@ -51,10 +53,10 @@ class IdentitiesService(QObject): return blockchain_time - cert_time < parameters.sig_window * parameters.avg_gen_time def _get_connections_identities(self): - pubkeys = self._connections_processor.pubkeys(self.currency) - identities = set([]) - for p in pubkeys: - identities += self._connections_processor.get_identities(self.currency, p) + connections = self._connections_processor.connections(self.currency) + identities = [] + for c in connections: + identities.append(self._identities_processor.get_identity(self.currency, c.pubkey)) return identities async def load_memberships(self, identity): @@ -63,28 +65,30 @@ class IdentitiesService(QObject): It does nothing if the identity is already written and updated with blockchain lookups :param sakia.data.entities.Identity identity: the identity """ - if not identity.written_on: - try: - search = await self._bma_connector.get(self.currency, bma.blockchain.membership, - {'search': self.pubkey}) - blockstamp = BlockUID.empty() - membership_data = None + try: + search = await self._bma_connector.get(self.currency, bma.blockchain.memberships, + {'search': identity.pubkey}) + blockstamp = BlockUID.empty() + membership_data = None - for ms in search['memberships']: - if ms['blockNumber'] > blockstamp.number: - blockstamp = BlockUID(ms["blockNumber"], ms['blockHash']) - membership_data = ms - if membership_data: - identity.membership_timestamp = await self._blockchain_processor.timestamp(blockstamp) - identity.membership_buid = blockstamp - identity.membership_type = ms["type"] - identity.membership_written_on = ms["written"] - await self.refresh_requirements(identity) - self._identities_processor.insert_or_update_identity(identity) - except errors.DuniterError as e: - logging.debug(str(e)) - except NoPeerAvailable as e: - logging.debug(str(e)) + for ms in search['memberships']: + if ms['blockNumber'] > blockstamp.number: + blockstamp = BlockUID(ms["blockNumber"], ms['blockHash']) + membership_data = ms + if membership_data: + identity.membership_timestamp = await self._blockchain_processor.timestamp(self.currency, blockstamp) + identity.membership_buid = blockstamp + identity.membership_type = ms["membership"] + identity.membership_written_on = ms["written"] + identity = await self.refresh_requirements(identity) + # We save connections pubkeys + if identity.pubkey in self._connections_processor.pubkeys(): + self._identities_processor.insert_or_update_identity(identity) + except errors.DuniterError as e: + logging.debug(str(e)) + except NoPeerAvailable as e: + logging.debug(str(e)) + return identity async def load_certifiers_of(self, identity): """ @@ -92,25 +96,31 @@ class IdentitiesService(QObject): It does nothing if the identity is already written and updated with blockchain lookups :param sakia.data.entities.Identity identity: the identity """ - if not identity.written_on: - try: - data = await self._bma_connector.get(self.currency, bma.wot.certifiers_of, {'search': identity.pubkey}) - for certifier_data in data['certifications']: - cert = Certification(currency=self.currency, - certified=data["pubkey"], - certifier=certifier_data["pubkey"], - block=certifier_data["cert_time"]["block"], - timestamp=certifier_data["cert_time"]["medianTime"], - signature=certifier_data['signature']) - if certifier_data['written']: - cert.written_on = BlockUID(certifier_data['written']['number'], - certifier_data['written']['hash']) + try: + certifications = [] + data = await self._bma_connector.get(self.currency, bma.wot.certifiers_of, {'search': identity.pubkey}) + for certifier_data in data['certifications']: + cert = Certification(currency=self.currency, + certified=data["pubkey"], + certifier=certifier_data["pubkey"], + block=certifier_data["cert_time"]["block"], + timestamp=certifier_data["cert_time"]["medianTime"], + signature=certifier_data['signature']) + if certifier_data['written']: + cert.written_on = BlockUID(certifier_data['written']['number'], + certifier_data['written']['hash']) + certifications.append(cert) + # We save connections pubkeys + if identity.pubkey in self._connections_processor.pubkeys(): self._certs_processor.insert_or_update_certification(cert) - except errors.DuniterError as e: - if e.ucode in (errors.NO_MATCHING_IDENTITY, errors.NO_MEMBER_MATCHING_PUB_OR_UID): - logging.debug("Certified by error : {0}".format(str(e))) - except NoPeerAvailable as e: - logging.debug(str(e)) + return certifications + except errors.DuniterError as e: + if e.ucode in (errors.NO_MATCHING_IDENTITY, errors.NO_MEMBER_MATCHING_PUB_OR_UID): + logging.debug("Certified by error : {0}".format(str(e))) + return [] + except NoPeerAvailable as e: + logging.debug(str(e)) + return [] async def load_certified_by(self, identity): """ @@ -118,25 +128,31 @@ class IdentitiesService(QObject): It does nothing if the identity is already written and updated with blockchain lookups :param sakia.data.entities.Identity identity: the identity """ - if not identity.written_on: - try: - data = await self._bma_connector.get(self.currency, bma.wot.certified_by, {'search': identity.pubkey}) - for certified_data in data['certifications']: - cert = Certification(currency=self.currency, - certifier=data["pubkey"], - certified=certified_data["pubkey"], - block=certified_data["cert_time"]["block"], - timestamp=certified_data["cert_time"]["medianTime"], - signature=certified_data['signature']) - if certified_data['written']: - cert.written_on = BlockUID(certified_data['written']['number'], - certified_data['written']['hash']) + try: + certifications = [] + data = await self._bma_connector.get(self.currency, bma.wot.certified_by, {'search': identity.pubkey}) + for certified_data in data['certifications']: + cert = Certification(currency=self.currency, + certifier=data["pubkey"], + certified=certified_data["pubkey"], + block=certified_data["cert_time"]["block"], + timestamp=certified_data["cert_time"]["medianTime"], + signature=certified_data['signature']) + if certified_data['written']: + cert.written_on = BlockUID(certified_data['written']['number'], + certified_data['written']['hash']) + certifications.append(cert) + # We save connections pubkeys + if identity.pubkey in self._connections_processor.pubkeys(): self._certs_processor.insert_or_update_certification(cert) - except errors.DuniterError as e: - if e.ucode in (errors.NO_MATCHING_IDENTITY, errors.NO_MEMBER_MATCHING_PUB_OR_UID): - logging.debug("Certified by error : {0}".format(str(e))) - except NoPeerAvailable as e: - logging.debug(str(e)) + return certifications + except errors.DuniterError as e: + if e.ucode in (errors.NO_MATCHING_IDENTITY, errors.NO_MEMBER_MATCHING_PUB_OR_UID): + logging.debug("Certified by error : {0}".format(str(e))) + return [] + except NoPeerAvailable as e: + logging.debug(str(e)) + return [] def _parse_revocations(self, block): """ diff --git a/src/sakia/services/transactions.py b/src/sakia/services/transactions.py index 109a4b06..fe365943 100644 --- a/src/sakia/services/transactions.py +++ b/src/sakia/services/transactions.py @@ -33,7 +33,7 @@ class TransactionsService(QObject): :return: The list of transfers sent """ transfers = [] - for tx in [t for t in self._transactions_processor.awaiting()]: + for tx in [t for t in self._transactions_processor.awaiting(self.currency)]: self._transactions_processor.run_state_transitions(tx, (False, block_doc)) new_transactions = [t for t in block_doc.transactions -- GitLab