diff --git a/src/sakia/app.py b/src/sakia/app.py index bf04d55b84ee4b2633d728503352515769e555df..250a62c0cc6ec81b6fd10b62ffb1ec6c48e88062 100644 --- a/src/sakia/app.py +++ b/src/sakia/app.py @@ -47,6 +47,7 @@ class Application(QObject): identity_changed = pyqtSignal(Identity) new_connection = pyqtSignal(Connection) referential_changed = pyqtSignal() + sources_refreshed = pyqtSignal() qapp = attr.ib() loop = attr.ib() @@ -125,16 +126,18 @@ class Application(QObject): identities_processor, connections_processor, bma_connector) + if currency not in self.sources_services: + self.sources_services[currency] = SourcesServices(currency, sources_processor, + connections_processor, bma_connector) + if currency not in self.blockchain_services: self.blockchain_services[currency] = BlockchainService(self, currency, blockchain_processor, bma_connector, - self.identities_services[currency], - self.transactions_services[currency]) - + self.identities_services[currency], + self.transactions_services[currency], + self.sources_services[currency]) if currency not in self.network_services: self.network_services[currency] = NetworkService.load(self, currency, nodes_processor, self.blockchain_services[currency]) - if currency not in self.sources_services: - self.sources_services[currency] = SourcesServices(currency, sources_processor, bma_connector) def switch_language(self): logging.debug("Loading translations") diff --git a/src/sakia/data/processors/sources.py b/src/sakia/data/processors/sources.py index fe7cc72d7f925c592bdc9e32007a808245e5dad9..c2e13042057fc61cc5e9403a5f068890fce53038 100644 --- a/src/sakia/data/processors/sources.py +++ b/src/sakia/data/processors/sources.py @@ -1,10 +1,9 @@ import attr -import re +import sqlite3 from ..entities import Source from .nodes import NodesProcessor from ..connectors import BmaConnector from duniterpy.api import bma, errors -import asyncio @attr.s @@ -25,30 +24,33 @@ class SourcesProcessor: return cls(app.db.sources_repo, BmaConnector(NodesProcessor(app.db.nodes_repo), app.parameters)) + def commit(self, source): + try: + self._repo.insert(source) + except sqlite3.IntegrityError: + self._repo.update(source) + async def initialize_sources(self, currency, pubkey, log_stream): """ Initialize sources for a given pubkey if no source exists locally """ - one_source = self._repo.get_one(currency=currency) - if not one_source: - log_stream("Requesting sources") - try: - sources_data = await self._bma_connector.get(currency, bma.tx.sources, - req_args={'pubkey': pubkey}) + log_stream("Requesting sources") + try: + sources_data = await self._bma_connector.get(currency, bma.tx.sources, + req_args={'pubkey': pubkey}) - log_stream("Found {0} sources".format(len(sources_data['sources']))) - for i, s in enumerate(sources_data['sources']): - source = Source(currency=currency, pubkey=pubkey, - identifier=s['identifier'], - type=s['type'], - noffset=s['noffset'], - amount=s['amount'], - base=s['base']) - self._repo.insert(source) - await asyncio.sleep(0) - log_stream("{0}/{1} sources".format(i, len(sources_data['sources']))) - except errors.DuniterError as e: - raise + log_stream("Found {0} sources".format(len(sources_data['sources']))) + for i, s in enumerate(sources_data['sources']): + source = Source(currency=currency, pubkey=pubkey, + identifier=s['identifier'], + type=s['type'], + noffset=s['noffset'], + amount=s['amount'], + base=s['base']) + self.commit(source) + log_stream("{0}/{1} sources".format(i, len(sources_data['sources']))) + except errors.DuniterError as e: + raise def amount(self, currency, pubkey): """ @@ -75,4 +77,7 @@ class SourcesProcessor: :return: """ for s in sources: - self._repo.drop(s) \ No newline at end of file + self._repo.drop(s) + + def drop_all_of(self, currency, pubkey): + self._repo.drop_all(currency=currency, pubkey=pubkey) diff --git a/src/sakia/data/repositories/sources.py b/src/sakia/data/repositories/sources.py index 93c08dd4adce4b39fee857abd75788125f5e5a3f..4f34bc64e589ca513f38081a07940cfbf28f9b72 100644 --- a/src/sakia/data/repositories/sources.py +++ b/src/sakia/data/repositories/sources.py @@ -69,3 +69,13 @@ class SourcesRepo: WHERE identifier=?""", where_fields) + def drop_all(self, **filter): + filters = [] + values = [] + for k, v in filter.items(): + value = v + filters.append("{key} = ?".format(key=k)) + values.append(value) + + request = "DELETE FROM sources WHERE {filters}".format(filters=" AND ".join(filters)) + self._conn.execute(request, tuple(values)) \ No newline at end of file diff --git a/src/sakia/gui/navigation/informations/controller.py b/src/sakia/gui/navigation/informations/controller.py index 21107dbcdad3324bfd32ad18009c3634707b571b..074699c162bba386ad12f271e4de6a62c618fda5 100644 --- a/src/sakia/gui/navigation/informations/controller.py +++ b/src/sakia/gui/navigation/informations/controller.py @@ -52,6 +52,8 @@ class InformationsController(QObject): app.identity_changed.connect(informations.handle_identity_change) app.new_transfer.connect(informations.refresh_localized_data) app.new_dividend.connect(informations.refresh_localized_data) + app.referential_changed.connect(informations.refresh_localized_data) + app.sources_refreshed.connect(informations.refresh_localized_data) return informations @asyncify diff --git a/src/sakia/gui/navigation/txhistory/controller.py b/src/sakia/gui/navigation/txhistory/controller.py index b9f210448467d1595f5b30182f8f83df5e655a42..314ba91c972eb7323fa4882c2bdcd890fe5e308a 100644 --- a/src/sakia/gui/navigation/txhistory/controller.py +++ b/src/sakia/gui/navigation/txhistory/controller.py @@ -41,6 +41,7 @@ class TxHistoryController(QObject): txhistory = cls(view, model) model.setParent(txhistory) app.referential_changed.connect(txhistory.refresh_balance) + app.sources_refreshed.connect(txhistory.refresh_balance) return txhistory def refresh_minimum_maximum(self): diff --git a/src/sakia/services/blockchain.py b/src/sakia/services/blockchain.py index b05dfd35467d2020920816eb2d01a613d4bf28b2..c355fd850b5ba285645607140a249be5bcd4f1fb 100644 --- a/src/sakia/services/blockchain.py +++ b/src/sakia/services/blockchain.py @@ -10,7 +10,8 @@ class BlockchainService(QObject): Blockchain service is managing new blocks received to update data locally """ - def __init__(self, app, currency, blockchain_processor, bma_connector, identities_service, transactions_service): + def __init__(self, app, currency, blockchain_processor, bma_connector, + identities_service, transactions_service, sources_service): """ Constructor the identities service @@ -20,6 +21,7 @@ class BlockchainService(QObject): :param sakia.data.connectors.BmaConnector bma_connector: The connector to BMA API :param sakia.services.IdentitiesService identities_service: The identities service :param sakia.services.TransactionsService transactions_service: The transactions service + :param sakia.services.SourcesService sources_service: The sources service """ super().__init__() self.app = app @@ -28,6 +30,7 @@ class BlockchainService(QObject): self.currency = currency self._identities_service = identities_service self._transactions_service = transactions_service + self._sources_service = sources_service self._logger = logging.getLogger('sakia') async def handle_blockchain_progress(self, network_blockstamp): @@ -44,6 +47,7 @@ class BlockchainService(QObject): if len(blocks) > 0: identities = await self._identities_service.handle_new_blocks(blocks) changed_tx, new_tx, new_dividends = await self._transactions_service.handle_new_blocks(blocks) + await self._sources_service.refresh_sources() self._blockchain_processor.handle_new_blocks(self.currency, blocks) self.app.db.commit() for tx in changed_tx: @@ -54,6 +58,7 @@ class BlockchainService(QObject): self.app.new_dividend.emit(ud) for idty in identities: self.app.identity_changed.emit(idty) + self.app.sources_refreshed.emit() except (NoPeerAvailable, DuniterError) as e: self._logger.debug(str(e)) diff --git a/src/sakia/services/sources.py b/src/sakia/services/sources.py index 5ef98b7a492897a8d26233500ba1743e3894a881..fe082a28252da85dd09e59041c64f3e3f80c8939 100644 --- a/src/sakia/services/sources.py +++ b/src/sakia/services/sources.py @@ -1,7 +1,7 @@ from PyQt5.QtCore import QObject -from duniterpy.api import bma -import math +from duniterpy.api import bma, errors import logging +from sakia.data.entities import Source class SourcesServices(QObject): @@ -9,19 +9,39 @@ class SourcesServices(QObject): Source service is managing sources received to update data locally """ - def __init__(self, currency, sources_processor, bma_connector): + def __init__(self, currency, sources_processor, connections_processor, bma_connector): """ Constructor the identities service :param str currency: The currency name of the community - :param sakia.data.processors.SourceProcessor sources_processor: the sources processor for given currency + :param sakia.data.processors.SourcesProcessor sources_processor: the sources processor for given currency + :param sakia.data.processors.ConnectionsProcessor connections_processor: the connections processor :param sakia.data.connectors.BmaConnector bma_connector: The connector to BMA API """ super().__init__() self._sources_processor = sources_processor + self._connections_processor = connections_processor self._bma_connector = bma_connector self.currency = currency self._logger = logging.getLogger('sakia') def amount(self, pubkey): return self._sources_processor.amount(self.currency, pubkey) + + async def refresh_sources(self): + connections_pubkeys = [c.pubkey for c in self._connections_processor.connections_to(self.currency)] + for pubkey in connections_pubkeys: + sources_data = await self._bma_connector.get(self.currency, bma.tx.sources, + req_args={'pubkey': pubkey}) + + self._logger.debug("Found {0} sources".format(len(sources_data['sources']))) + self._sources_processor.drop_all_of(currency=self.currency, pubkey=pubkey) + for i, s in enumerate(sources_data['sources']): + source = Source(currency=self.currency, pubkey=pubkey, + identifier=s['identifier'], + type=s['type'], + noffset=s['noffset'], + amount=s['amount'], + base=s['base']) + self._sources_processor.commit(source) + self._logger.debug("{0}/{1} sources".format(i, len(sources_data['sources']))) diff --git a/src/sakia/services/transactions.py b/src/sakia/services/transactions.py index dafae7b738cfb431ccd2829ec4efd66eafb15cef..c537d333bad69ffbd1450e88ebbbf0ff2fda7b85 100644 --- a/src/sakia/services/transactions.py +++ b/src/sakia/services/transactions.py @@ -1,6 +1,7 @@ from PyQt5.QtCore import QObject from sakia.data.entities.transaction import parse_transaction_doc from duniterpy.documents import Transaction as TransactionDoc +from duniterpy.documents import SimpleTransaction from sakia.data.entities import Dividend from duniterpy.api import bma import logging