diff --git a/src/sakia/app.py b/src/sakia/app.py index d66c5424e4d9dcc287c45c40be3d80d2b83e50bd..c2c44e0411e6dd79f37ef8f4e8021c1e9c090a07 100644 --- a/src/sakia/app.py +++ b/src/sakia/app.py @@ -214,7 +214,7 @@ class Application(QObject): logging.debug("Found version : {0}".format(latest_version)) logging.debug("Current version : {0}".format(__version__)) self.available_version = version - except (aiohttp.ClientError, asyncio.TimeoutError) as e: + except (aiohttp.errors.ClientError, aiohttp.errors.ServerDisconnectedError, asyncio.TimeoutError) as e: self._logger.debug("Could not connect to github : {0}".format(str(e))) def save_parameters(self, parameters): diff --git a/src/sakia/data/entities/dividend.py b/src/sakia/data/entities/dividend.py index 75c70b4e8721643b7924c05a3298b5c5f04164a8..9139e2ebaea4565b0d2358d4bf6247873da27b8d 100644 --- a/src/sakia/data/entities/dividend.py +++ b/src/sakia/data/entities/dividend.py @@ -3,9 +3,9 @@ import attr @attr.s() class Dividend: - currency = attr.ib(convert=str) - pubkey = attr.ib(convert=str) - block_number = attr.ib(convert=int) + currency = attr.ib(convert=str, cmp=True, hash=True) + pubkey = attr.ib(convert=str, cmp=True, hash=True) + block_number = attr.ib(convert=int, cmp=True, hash=True) timestamp = attr.ib(convert=int) amount = attr.ib(convert=int, cmp=False, hash=False) base = attr.ib(convert=int, cmp=False, hash=False) diff --git a/src/sakia/data/entities/transaction.py b/src/sakia/data/entities/transaction.py index f00a1cb1fccf5be882dd666f0f2fdf100898739e..2c64617ccfbf2bd166b44d92f97bcf1d03d3176c 100644 --- a/src/sakia/data/entities/transaction.py +++ b/src/sakia/data/entities/transaction.py @@ -95,9 +95,9 @@ class Transaction: REFUSED = 8 DROPPED = 16 - currency = attr.ib(convert=str) - pubkey = attr.ib(convert=str) - sha_hash = attr.ib(convert=str) + currency = attr.ib(convert=str, cmp=True, hash=True) + pubkey = attr.ib(convert=str, cmp=True, hash=True) + sha_hash = attr.ib(convert=str, cmp=True, hash=True) written_block = attr.ib(convert=int, cmp=False) blockstamp = attr.ib(convert=block_uid, cmp=False) timestamp = attr.ib(convert=int, cmp=False) diff --git a/src/sakia/data/repositories/meta.py b/src/sakia/data/repositories/meta.py index 3b1fb7c3634347cde24b78f09767dfc7d71e8e13..bd08ef3d504a8092c58e3f4a16d6b40e2a6b7f19 100644 --- a/src/sakia/data/repositories/meta.py +++ b/src/sakia/data/repositories/meta.py @@ -36,8 +36,13 @@ class SakiaDatabase: sqlite3.register_adapter(BlockUID, str) sqlite3.register_adapter(bool, int) sqlite3.register_converter("BOOLEAN", lambda v: bool(int(v))) + + def total_amount(amount, amount_base): + return amount * 10 ** amount_base + db_path = os.path.join(options.config_path, profile_name, options.currency + ".db") con = sqlite3.connect(db_path, detect_types=sqlite3.PARSE_DECLTYPES) + con.create_function("total_amount", 2, total_amount) meta = SakiaDatabase(con, ConnectionsRepo(con), IdentitiesRepo(con), BlockchainsRepo(con), CertificationsRepo(con), TransactionsRepo(con), NodesRepo(con), SourcesRepo(con), DividendsRepo(con), ContactsRepo(con)) diff --git a/src/sakia/data/repositories/transactions.py b/src/sakia/data/repositories/transactions.py index 0a9a771785cdbefdfe7e9cdbd7dfcab34b402291..4a657a61b9c5fb433567cd0c1c08a8bd8aa6c958 100644 --- a/src/sakia/data/repositories/transactions.py +++ b/src/sakia/data/repositories/transactions.py @@ -1,6 +1,6 @@ import attr -from ..entities import Transaction +from ..entities import Transaction, Dividend @attr.s(frozen=True) diff --git a/src/sakia/gui/navigation/txhistory/controller.py b/src/sakia/gui/navigation/txhistory/controller.py index acf2ee357ec8309b239fef0e35c1d55d9d27bebd..4737d2625921ab6a3a46af9d5e29a9c47cb01dcf 100644 --- a/src/sakia/gui/navigation/txhistory/controller.py +++ b/src/sakia/gui/navigation/txhistory/controller.py @@ -51,6 +51,7 @@ class TxHistoryController(QObject): app.referential_changed.connect(txhistory.refresh_balance) app.sources_refreshed.connect(txhistory.refresh_balance) txhistory.view_in_wot.connect(app.view_in_wot) + txhistory.view.spin_page.valueChanged.connect(model.change_page) transfer.accepted.connect(view.clear) transfer.rejected.connect(view.clear) return txhistory @@ -65,6 +66,7 @@ class TxHistoryController(QObject): def refresh(self): self.refresh_minimum_maximum() self.refresh_balance() + self.refresh_pages() @asyncify async def notification_reception(self, received_list): @@ -79,6 +81,10 @@ class TxHistoryController(QObject): localized_amount = self.model.localized_balance() self.view.set_balance(localized_amount) + def refresh_pages(self): + pages = self.model.max_pages() + self.view.set_max_pages(pages) + def history_context_menu(self, point): index = self.view.table_history.indexAt(point) valid, identities, transfer = self.model.table_data(index) diff --git a/src/sakia/gui/navigation/txhistory/delegate.py b/src/sakia/gui/navigation/txhistory/delegate.py index 80779be0c1389e935acd68dd43506ffc68ce37bd..4c28df7dd587a51b8679940a999c8b08baf13ecf 100644 --- a/src/sakia/gui/navigation/txhistory/delegate.py +++ b/src/sakia/gui/navigation/txhistory/delegate.py @@ -13,7 +13,7 @@ class TxHistoryDelegate(QStyledItemDelegate): style = QApplication.style() doc = QTextDocument() - if index.column() == HistoryTableModel.columns_types.index('uid'): + if index.column() == HistoryTableModel.columns_types.index('pubkey'): doc.setHtml(option.text) else: doc.setPlainText(option.text) @@ -39,7 +39,7 @@ class TxHistoryDelegate(QStyledItemDelegate): self.initStyleOption(option, index) doc = QTextDocument() - if index.column() == HistoryTableModel.columns_types.index('uid'): + if index.column() == HistoryTableModel.columns_types.index('pubkey'): doc.setHtml(option.text) else: doc.setPlainText("") diff --git a/src/sakia/gui/navigation/txhistory/model.py b/src/sakia/gui/navigation/txhistory/model.py index 3b0cd432ca3a642823c76f9e5f7747b3d6cacacf..fbdc1fd345d10ac8cf2937454b74040c0e1232fe 100644 --- a/src/sakia/gui/navigation/txhistory/model.py +++ b/src/sakia/gui/navigation/txhistory/model.py @@ -1,5 +1,5 @@ from PyQt5.QtCore import QObject -from .table_model import HistoryTableModel, TxFilterProxyModel +from .table_model import HistoryTableModel from PyQt5.QtCore import Qt, QDateTime, QTime, pyqtSignal, QModelIndex from sakia.errors import NoPeerAvailable from duniterpy.api import errors @@ -32,7 +32,6 @@ class TxHistoryModel(QObject): self.transactions_service = transactions_service self.sources_service = sources_service self._model = None - self._proxy = None def init_history_table_model(self, ts_from, ts_to): """ @@ -41,19 +40,21 @@ class TxHistoryModel(QObject): :param int ts_to: date to where to filter tx :return: """ - self._model = HistoryTableModel(self, self.app, self.connection, + self._model = HistoryTableModel(self, self.app, self.connection, ts_from, ts_to, self.identities_service, self.transactions_service) - self._proxy = TxFilterProxyModel(self, ts_from, ts_to, self.blockchain_service) - self._proxy.setSourceModel(self._model) - self._proxy.setDynamicSortFilter(True) - self._proxy.setSortRole(Qt.DisplayRole) self._model.init_transfers() - self.app.new_transfer.connect(self._model.add_transfer) - self.app.new_dividend.connect(self._model.add_dividend) + self.app.new_transfer.connect(self._model.init_transfers) + self.app.new_dividend.connect(self._model.init_transfers) self.app.transaction_state_changed.connect(self._model.change_transfer) self.app.referential_changed.connect(self._model.modelReset) - return self._proxy + return self._model + + def change_page(self, page): + self._model.set_current_page(page) + + def max_pages(self): + return self._model.pages() def table_data(self, index): """ @@ -62,11 +63,9 @@ class TxHistoryModel(QObject): :return: tuple containing (Identity, Transfer) """ if index.isValid() and index.row() < self.table_model.rowCount(QModelIndex()): - source_index = self.table_model.mapToSource(index) - - pubkey_col = self.table_model.sourceModel().columns_types.index('pubkey') - pubkey_index = self.table_model.sourceModel().index(source_index.row(), pubkey_col) - pubkeys = self.table_model.sourceModel().data(pubkey_index, Qt.DisplayRole) + pubkey_col = self._model.columns_types.index('pubkey') + pubkey_index = self._model.index(index.row(), pubkey_col) + pubkeys = self._model.data(pubkey_index, Qt.DisplayRole) identities_or_pubkeys = [] for pubkey in pubkeys: identity = self.identities_service.get_identity(pubkey) @@ -74,7 +73,7 @@ class TxHistoryModel(QObject): identities_or_pubkeys.append(identity) else: identities_or_pubkeys.append(pubkey) - transfer = self._model.transfers_data[source_index.row()][self._model.columns_types.index('raw_data')] + transfer = self._model.transfers_data[index.row()][self._model.columns_types.index('raw_data')] return True, identities_or_pubkeys, transfer return False, [], None @@ -123,7 +122,7 @@ class TxHistoryModel(QObject): @property def table_model(self): - return self._proxy + return self._model def notifications(self): return self.app.parameters.notifications diff --git a/src/sakia/gui/navigation/txhistory/sql_adapter.py b/src/sakia/gui/navigation/txhistory/sql_adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..5a73f786e4418eedf09d4c01a9610ce8a4362f6b --- /dev/null +++ b/src/sakia/gui/navigation/txhistory/sql_adapter.py @@ -0,0 +1,123 @@ +import attr + + +TX_HISTORY_REQUEST = """ +SELECT + transactions.ts, + transactions.pubkey, + total_amount((amount * -1), amountbase) as amount, + transactions.comment , + transactions.sha_hash, + transactions.written_on + FROM transactions + WHERE transactions.currency = ? + and transactions.pubkey =? + AND transactions.ts >= ? + and transactions.ts <= ? + AND transactions.issuers LIKE "%{pubkey}%" +UNION ALL +SELECT + transactions.ts, + transactions.pubkey, + total_amount(amount, amountbase) as amount, + transactions.comment , + transactions.sha_hash, + transactions.written_on + FROM transactions + WHERE transactions.currency = ? + and transactions.pubkey =? + AND transactions.ts >= ? + and transactions.ts <= ? + AND transactions.receivers LIKE "%{pubkey}%" +UNION ALL +SELECT + dividends.timestamp as ts, + dividends.pubkey , + total_amount(amount, base) as amount, + NULL as comment, + NULL as sha_hash, + dividends.block_number AS written_on + FROM dividends + WHERE dividends.currency = ? and dividends.pubkey =? AND dividends.timestamp >= ? and dividends.timestamp <= ? +""" + +PAGE_LENGTH = 50 + + +@attr.s(frozen=True) +class TxHistorySqlAdapter: + _conn = attr.ib() # :type sqlite3.Connection + + def _transfers_and_dividends(self, currency, pubkey, ts_from, ts_to, offset=0, limit=1000, + sort_by="currency", sort_order="ASC"): + """ + Get all transfers in the database on a given currency from or to a pubkey + + :param str pubkey: the criterions of the lookup + :rtype: List[sakia.data.entities.Transaction] + """ + request = (TX_HISTORY_REQUEST + """ +ORDER BY {sort_by} {sort_order} +LIMIT {limit} OFFSET {offset}""").format(offset=offset, + limit=limit, + sort_by=sort_by, + sort_order=sort_order, + pubkey=pubkey + ) + c = self._conn.execute(request, (currency, pubkey, ts_from, ts_to, + currency, pubkey, ts_from, ts_to, + currency, pubkey, ts_from, ts_to)) + datas = c.fetchall() + if datas: + return datas + return [] + + def _transfers_and_dividends_count(self, currency, pubkey, ts_from, ts_to): + """ + Get all transfers in the database on a given currency from or to a pubkey + + :param str pubkey: the criterions of the lookup + :rtype: List[sakia.data.entities.Transaction] + """ + request = """ +SELECT COUNT(*) +FROM ( +""" + TX_HISTORY_REQUEST + ")" + c = self._conn.execute(request, (currency, pubkey, ts_from, ts_to, + currency, pubkey, ts_from, ts_to, + currency, pubkey, ts_from, ts_to)) + datas = c.fetchone() + if datas: + return datas[0] + return 0 + + def transfers_and_dividends(self, currency, pubkey, page, ts_from, ts_to, sort_by, sort_order): + """ + Get all transfers and dividends from or to a given pubkey + :param str currency: + :param str pubkey: + :param int page: + :param int ts_from: + :param int ts_to: + :return: the list of Transaction entities + :rtype: List[sakia.data.entities.Transaction] + """ + return self._transfers_and_dividends(currency, pubkey, ts_from, ts_to, + offset=page*PAGE_LENGTH, + limit=PAGE_LENGTH, + sort_by=sort_by, sort_order=sort_order) + + def pages(self, currency, pubkey, ts_from, ts_to): + """ + Get all transfers and dividends from or to a given pubkey + :param str currency: + :param str pubkey: + :param int page: + :param int ts_from: + :param int ts_to: + :return: the list of Transaction entities + :rtype: List[sakia.data.entities.Transaction] + """ + count = self._transfers_and_dividends_count(currency, pubkey, ts_from, ts_to) + return int(count / PAGE_LENGTH) + 1 + diff --git a/src/sakia/gui/navigation/txhistory/table_model.py b/src/sakia/gui/navigation/txhistory/table_model.py index 562ce339eb5a1ea1e6e82ef570af557135d99381..3c8ef2c5ce0b72b24cfbe496cd65f5f64f395985 100644 --- a/src/sakia/gui/navigation/txhistory/table_model.py +++ b/src/sakia/gui/navigation/txhistory/table_model.py @@ -7,154 +7,8 @@ from PyQt5.QtGui import QFont, QColor from sakia.data.entities import Transaction from sakia.constants import MAX_CONFIRMATIONS from sakia.data.processors import BlockchainProcessor - - -class TxFilterProxyModel(QSortFilterProxyModel): - def __init__(self, parent, ts_from, ts_to, blockchain_service): - """ - History of all transactions - :param PyQt5.QtWidgets.QWidget parent: parent widget - :param int ts_from: the min timestamp of latest tx - :param in ts_to: the max timestamp of most recent tx - :param sakia.services.BlockchainService blockchain_service: the blockchain service - """ - super().__init__(parent) - self.app = None - self.ts_from = ts_from - self.ts_to = ts_to - self.blockchain_service = blockchain_service - self.blockchain_processor = BlockchainProcessor.instanciate(blockchain_service.app) - - def set_period(self, ts_from, ts_to): - """ - Filter table by given timestamps - """ - logging.debug("Filtering from {0} to {1}".format( - datetime.datetime.fromtimestamp(ts_from).isoformat(' '), - datetime.datetime.fromtimestamp(ts_to).isoformat(' ')) - ) - self.beginResetModel() - self.ts_from = ts_from - self.ts_to = ts_to - self.endResetModel() - - def filterAcceptsRow(self, sourceRow, sourceParent): - def in_period(date_ts): - return date_ts in range(self.ts_from, self.ts_to) - - source_model = self.sourceModel() - date_col = source_model.columns_types.index('date') - source_index = source_model.index(sourceRow, date_col) - date = source_model.data(source_index, Qt.DisplayRole) - - return in_period(date) - - def columnCount(self, parent): - return self.sourceModel().columnCount(None) - 6 - - def setSourceModel(self, source_model): - self.app = source_model.app - super().setSourceModel(source_model) - - def lessThan(self, left, right): - """ - Sort table by given column number. - """ - source_model = self.sourceModel() - left_data = source_model.data(left, Qt.DisplayRole) - right_data = source_model.data(right, Qt.DisplayRole) - if left_data == "": - return self.sortOrder() == Qt.DescendingOrder - elif right_data == "": - return self.sortOrder() == Qt.AscendingOrder - if left_data == right_data: - txid_col = source_model.columns_types.index('txid') - txid_left = source_model.index(left.row(), txid_col) - txid_right = source_model.index(right.row(), txid_col) - return txid_left < txid_right - - return left_data < right_data - - def data(self, index, role): - source_index = self.mapToSource(index) - model = self.sourceModel() - source_data = model.data(source_index, role) - state_col = model.columns_types.index('state') - state_index = model.index(source_index.row(), state_col) - state_data = model.data(state_index, Qt.DisplayRole) - - block_col = model.columns_types.index('block_number') - block_index = model.index(source_index.row(), block_col) - block_data = model.data(block_index, Qt.DisplayRole) - - if state_data == Transaction.VALIDATED and block_data: - current_confirmations = self.blockchain_service.current_buid().number - block_data - else: - current_confirmations = 0 - - if role == Qt.DisplayRole: - if source_index.column() == model.columns_types.index('uid'): - return "<p>" + source_data.replace('\n', "<br>") + "</p>" - if source_index.column() == model.columns_types.index('date'): - ts = self.blockchain_processor.adjusted_ts(model.connection.currency, source_data) - return QLocale.toString( - QLocale(), - QDateTime.fromTime_t(ts).date(), - QLocale.dateFormat(QLocale(), QLocale.ShortFormat) - ) + " BAT" - if source_index.column() == model.columns_types.index('amount'): - amount = self.app.current_ref.instance(source_data, model.connection.currency, - self.app, block_data).diff_localized(False, False) - return amount - return source_data - - if role == Qt.FontRole: - font = QFont() - if state_data == Transaction.AWAITING or \ - (state_data == Transaction.VALIDATED and current_confirmations < MAX_CONFIRMATIONS): - font.setItalic(True) - elif state_data == Transaction.REFUSED: - font.setItalic(True) - elif state_data == Transaction.TO_SEND: - font.setBold(True) - else: - font.setItalic(False) - return font - - if role == Qt.ForegroundRole: - if state_data == Transaction.REFUSED: - return QColor(Qt.darkGray) - elif state_data == Transaction.TO_SEND: - return QColor(Qt.blue) - if source_index.column() == model.columns_types.index('amount'): - if source_data < 0: - return QColor(Qt.darkRed) - elif state_data == HistoryTableModel.DIVIDEND: - return QColor(Qt.darkBlue) - if state_data == Transaction.AWAITING or \ - (state_data == Transaction.VALIDATED and current_confirmations == 0): - return QColor("#ffb000") - - if role == Qt.TextAlignmentRole: - if self.sourceModel().columns_types.index('amount'): - return Qt.AlignRight | Qt.AlignVCenter - if source_index.column() == model.columns_types.index('date'): - return Qt.AlignCenter - - if role == Qt.ToolTipRole: - if source_index.column() == model.columns_types.index('date'): - ts = self.blockchain_processor.adjusted_ts(model.connection.currency, source_data) - return QDateTime.fromTime_t(ts).toString(Qt.SystemLocaleLongDate) - - if state_data == Transaction.VALIDATED or state_data == Transaction.AWAITING: - if current_confirmations >= MAX_CONFIRMATIONS: - return None - elif self.app.parameters.expert_mode: - return self.tr("{0} / {1} confirmations").format(current_confirmations, MAX_CONFIRMATIONS) - else: - confirmation = current_confirmations / MAX_CONFIRMATIONS * 100 - confirmation = 100 if confirmation > 100 else confirmation - return self.tr("Confirming... {0} %").format(QLocale().toString(float(confirmation), 'f', 0)) +from .sql_adapter import TxHistorySqlAdapter +from sakia.data.repositories import TransactionsRepo, DividendsRepo class HistoryTableModel(QAbstractTableModel): @@ -166,7 +20,7 @@ class HistoryTableModel(QAbstractTableModel): columns_types = ( 'date', - 'uid', + 'pubkey', 'amount', 'comment', 'state', @@ -177,14 +31,21 @@ class HistoryTableModel(QAbstractTableModel): 'raw_data' ) + columns_to_sql = { + 'date': "ts", + "pubkey": "pubkey", + "amount": "amount", + "comment": "comment" + } + columns_headers = ( QT_TRANSLATE_NOOP("HistoryTableModel", 'Date'), - QT_TRANSLATE_NOOP("HistoryTableModel", 'UID/Public key'), + QT_TRANSLATE_NOOP("HistoryTableModel", 'Public key'), QT_TRANSLATE_NOOP("HistoryTableModel", 'Amount'), QT_TRANSLATE_NOOP("HistoryTableModel", 'Comment') ) - def __init__(self, parent, app, connection, identities_service, transactions_service): + def __init__(self, parent, app, connection, ts_from, ts_to, identities_service, transactions_service): """ History of all transactions :param PyQt5.QtWidgets.QWidget parent: parent widget @@ -198,37 +59,50 @@ class HistoryTableModel(QAbstractTableModel): self.connection = connection self.blockchain_processor = BlockchainProcessor.instanciate(app) self.identities_service = identities_service - self.transactions_service = transactions_service + self.sql_adapter = TxHistorySqlAdapter(self.app.db.conn) + self.transactions_repo = TransactionsRepo(self.app.db.conn) + self.dividends_repo = DividendsRepo(self.app.db.conn) + self.current_page = 0 + self.ts_from = ts_from + self.ts_to = ts_to + self.main_column_id = HistoryTableModel.columns_types[0] + self.order = Qt.AscendingOrder self.transfers_data = [] - def transfers(self): + def set_period(self, ts_from, ts_to): """ - Transfer - :rtype: List[sakia.data.entities.Transfer] + Filter table by given timestamps """ - return self.transactions_service.transfers(self.connection.pubkey) + logging.debug("Filtering from {0} to {1}".format( + datetime.datetime.fromtimestamp(ts_from).isoformat(' '), + datetime.datetime.fromtimestamp(ts_to).isoformat(' ')) + ) + self.ts_from = ts_from + self.ts_to = ts_to + self.init_transfers() + + def set_current_page(self, page): + self.current_page = page + self.init_transfers() - def dividends(self): + def pages(self): + return self.sql_adapter.pages(self.app.currency, + self.connection.pubkey, + ts_from=self.ts_from, + ts_to=self.ts_to) + + def transfers_and_dividends(self): """ Transfer - :rtype: List[sakia.data.entities.Dividend] + :rtype: List[sakia.data.entities.Transfer] """ - return self.transactions_service.dividends(self.connection.pubkey) - - def add_transfer(self, connection, transfer): - if self.connection == connection: - self.beginInsertRows(QModelIndex(), len(self.transfers_data), len(self.transfers_data)) - if self.connection.pubkey in transfer.issuers: - self.transfers_data.append(self.data_sent(transfer)) - if self.connection.pubkey in transfer.receivers: - self.transfers_data.append(self.data_received(transfer)) - self.endInsertRows() - - def add_dividend(self, connection, dividend): - if self.connection == connection: - self.beginInsertRows(QModelIndex(), len(self.transfers_data), len(self.transfers_data)) - self.transfers_data.append(self.data_dividend(dividend)) - self.endInsertRows() + return self.sql_adapter.transfers_and_dividends(self.app.currency, + self.connection.pubkey, + page=self.current_page, + ts_from=self.ts_from, + ts_to=self.ts_to, + sort_by=HistoryTableModel.columns_to_sql[self.main_column_id], + sort_order= "ASC" if Qt.AscendingOrder else "DESC") def change_transfer(self, transfer): for i, data in enumerate(self.transfers_data): @@ -260,7 +134,7 @@ class HistoryTableModel(QAbstractTableModel): for issuer in transfer.issuers: identity = self.identities_service.get_identity(issuer) if identity: - senders.append(identity.uid) + senders.append(issuer + " (" + identity.uid + ")") else: senders.append(issuer) @@ -284,7 +158,7 @@ class HistoryTableModel(QAbstractTableModel): for receiver in transfer.receivers: identity = self.identities_service.get_identity(receiver) if identity: - receivers.append(identity.uid) + receivers.append(receiver + " (" + identity.uid + ")") else: receivers.append(receiver) @@ -304,7 +178,7 @@ class HistoryTableModel(QAbstractTableModel): amount = dividend.amount * 10**dividend.base identity = self.identities_service.get_identity(dividend.pubkey) if identity: - receiver = identity.uid + receiver = dividend.pubkey + " (" + identity.uid + ")" else: receiver = dividend.pubkey @@ -315,28 +189,40 @@ class HistoryTableModel(QAbstractTableModel): def init_transfers(self): self.beginResetModel() self.transfers_data = [] - transfers = self.transfers() - for transfer in transfers: - if transfer.state != Transaction.DROPPED: - if self.connection.pubkey in transfer.issuers: - self.transfers_data.append(self.data_sent(transfer)) - if self.connection.pubkey in transfer.receivers: - self.transfers_data.append(self.data_received(transfer)) - dividends = self.dividends() - for dividend in dividends: - self.transfers_data.append(self.data_dividend(dividend)) + transfers_and_dividends = self.transfers_and_dividends() + for data in transfers_and_dividends: + if data[4]: # If data is transfer, it has a sha_hash column + transfer = self.transactions_repo.get_one(currency=self.app.currency, + pubkey=self.connection.pubkey, + sha_hash=data[4]) + if transfer.state != Transaction.DROPPED: + if self.connection.pubkey in transfer.issuers: + self.transfers_data.append(self.data_sent(transfer)) + if self.connection.pubkey in transfer.receivers: + self.transfers_data.append(self.data_received(transfer)) + else: + # else we get the dividend depending on the block number + dividend = self.dividends_repo.get_one(currency=self.app.currency, + pubkey=self.connection.pubkey, + block_number=data[5]) + self.transfers_data.append(self.data_dividend(dividend)) self.endResetModel() def rowCount(self, parent): return len(self.transfers_data) def columnCount(self, parent): - return len(HistoryTableModel.columns_types) + return len(HistoryTableModel.columns_types) - 6 + + def sort(self, main_column, order): + self.main_column_id = self.columns_types[main_column] + self.order = order + self.init_transfers() def headerData(self, section, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: if HistoryTableModel.columns_types[section] == 'amount': - dividend, base = self.blockchain_processor.last_ud(self.transactions_service.currency) + dividend, base = self.blockchain_processor.last_ud(self.app.currency) header = '{:}'.format(HistoryTableModel.columns_headers[section]) if self.app.current_ref.base_str(base): header += " ({:})".format(self.app.current_ref.base_str(base)) @@ -350,8 +236,78 @@ class HistoryTableModel(QAbstractTableModel): if not index.isValid(): return QVariant() - if role in (Qt.DisplayRole, Qt.ForegroundRole, Qt.ToolTipRole): - return self.transfers_data[row][col] + source_data = self.transfers_data[row][col] + state_data = self.transfers_data[row][HistoryTableModel.columns_types.index('state')] + block_data = self.transfers_data[row][HistoryTableModel.columns_types.index('block_number')] + + if state_data == Transaction.VALIDATED and block_data: + current_confirmations = self.blockchain_processor.current_buid(self.app.currency).number - block_data + else: + current_confirmations = 0 + + if role == Qt.DisplayRole: + if col == HistoryTableModel.columns_types.index('pubkey'): + return "<p>" + source_data.replace('\n', "<br>") + "</p>" + if col == HistoryTableModel.columns_types.index('date'): + ts = self.blockchain_processor.adjusted_ts(self.connection.currency, source_data) + return QLocale.toString( + QLocale(), + QDateTime.fromTime_t(ts).date(), + QLocale.dateFormat(QLocale(), QLocale.ShortFormat) + ) + " BAT" + if col == HistoryTableModel.columns_types.index('amount'): + amount = self.app.current_ref.instance(source_data, self.connection.currency, + self.app, block_data).diff_localized(False, False) + return amount + return source_data + + if role == Qt.FontRole: + font = QFont() + if state_data == Transaction.AWAITING or \ + (state_data == Transaction.VALIDATED and current_confirmations < MAX_CONFIRMATIONS): + font.setItalic(True) + elif state_data == Transaction.REFUSED: + font.setItalic(True) + elif state_data == Transaction.TO_SEND: + font.setBold(True) + else: + font.setItalic(False) + return font + + if role == Qt.ForegroundRole: + if state_data == Transaction.REFUSED: + return QColor(Qt.darkGray) + elif state_data == Transaction.TO_SEND: + return QColor(Qt.blue) + if col == HistoryTableModel.columns_types.index('amount'): + if source_data < 0: + return QColor(Qt.darkRed) + elif state_data == HistoryTableModel.DIVIDEND: + return QColor(Qt.darkBlue) + if state_data == Transaction.AWAITING or \ + (state_data == Transaction.VALIDATED and current_confirmations == 0): + return QColor("#ffb000") + + if role == Qt.TextAlignmentRole: + if HistoryTableModel.columns_types.index('amount'): + return Qt.AlignRight | Qt.AlignVCenter + if col == HistoryTableModel.columns_types.index('date'): + return Qt.AlignCenter + + if role == Qt.ToolTipRole: + if col == HistoryTableModel.columns_types.index('date'): + ts = self.blockchain_processor.adjusted_ts(self.connection.currency, source_data) + return QDateTime.fromTime_t(ts).toString(Qt.SystemLocaleLongDate) + + if state_data == Transaction.VALIDATED or state_data == Transaction.AWAITING: + if current_confirmations >= MAX_CONFIRMATIONS: + return None + elif self.app.parameters.expert_mode: + return self.tr("{0} / {1} confirmations").format(current_confirmations, MAX_CONFIRMATIONS) + else: + confirmation = current_confirmations / MAX_CONFIRMATIONS * 100 + confirmation = 100 if confirmation > 100 else confirmation + return self.tr("Confirming... {0} %").format(QLocale().toString(float(confirmation), 'f', 0)) def flags(self, index): return Qt.ItemIsSelectable | Qt.ItemIsEnabled diff --git a/src/sakia/gui/navigation/txhistory/txhistory.ui b/src/sakia/gui/navigation/txhistory/txhistory.ui index 98d997ee6e1b20315aaaacb0d8c2ca28fbd97185..35efb75571bdb48d686d73eb3896d220348c4029 100644 --- a/src/sakia/gui/navigation/txhistory/txhistory.ui +++ b/src/sakia/gui/navigation/txhistory/txhistory.ui @@ -169,6 +169,36 @@ </item> </layout> </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <property name="topMargin"> + <number>6</number> + </property> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Page :</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spin_page"/> + </item> + </layout> + </item> </layout> </widget> </widget> diff --git a/src/sakia/gui/navigation/txhistory/view.py b/src/sakia/gui/navigation/txhistory/view.py index 3756e486a00658a881661dfb81cbf6876e0abfa3..5a4fa9903c41faa851f997a0c1203bb24c8ebad9 100644 --- a/src/sakia/gui/navigation/txhistory/view.py +++ b/src/sakia/gui/navigation/txhistory/view.py @@ -52,6 +52,9 @@ class TxHistoryView(QWidget, Ui_TxHistoryWidget): self.date_to.setDateTime(maximum) self.date_to.setMaximumDateTime(maximum) + def set_max_pages(self, pages): + self.spin_page.setMaximum(pages) + def set_balance(self, balance): """ Display given balance