From 66c0adebb7e4cd0de3340957420bdcac1781e23a Mon Sep 17 00:00:00 2001 From: Inso <insomniak.fr@gmail.com> Date: Sun, 8 Feb 2015 10:56:53 +0100 Subject: [PATCH] Display members in table Show members in table with columns for pubkey, uid, join date and expiration Display members who will expire in 15 days as red --- lib/ucoinpy/api/bma/blockchain/__init__.py | 9 ++- res/ui/community_tab.ui | 30 ++++---- src/cutecoin/core/community.py | 6 +- src/cutecoin/core/person.py | 25 ++++++- src/cutecoin/gui/community_tab.py | 13 ++-- src/cutecoin/gui/mainwindow.py | 2 +- src/cutecoin/models/members.py | 85 +++++++++++++++++++--- src/cutecoin/tools/exceptions.py | 19 +++++ 8 files changed, 148 insertions(+), 41 deletions(-) diff --git a/lib/ucoinpy/api/bma/blockchain/__init__.py b/lib/ucoinpy/api/bma/blockchain/__init__.py index 03890265..f594d7f1 100644 --- a/lib/ucoinpy/api/bma/blockchain/__init__.py +++ b/lib/ucoinpy/api/bma/blockchain/__init__.py @@ -34,13 +34,20 @@ class Parameters(Blockchain): class Membership(Blockchain): - """POST a Membership document.""" + """GET/POST a Membership document.""" + def __init__(self, connection_handler, search=None): + super().__init__(connection_handler) + self.search = search def __post__(self, **kwargs): assert 'membership' in kwargs return self.requests_post('/membership', **kwargs).json() + def __get__(self, **kwargs): + assert self.search is not None + return self.requests_get('/memberships/%s' % self.search, **kwargs).json() + class Block(Blockchain): """GET/POST a block from/to the blockchain.""" diff --git a/res/ui/community_tab.ui b/res/ui/community_tab.ui index e6b722a8..2155cb19 100644 --- a/res/ui/community_tab.ui +++ b/res/ui/community_tab.ui @@ -43,10 +43,22 @@ <item> <layout class="QVBoxLayout" name="verticalLayout_6"> <item> - <widget class="QListView" name="list_community_members"> + <widget class="QTableView" name="table_community_members"> <property name="contextMenuPolicy"> <enum>Qt::CustomContextMenu</enum> </property> + <property name="sortingEnabled"> + <bool>true</bool> + </property> + <attribute name="horizontalHeaderShowSortIndicator" stdset="0"> + <bool>true</bool> + </attribute> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderVisible"> + <bool>false</bool> + </attribute> </widget> </item> <item> @@ -137,22 +149,6 @@ </hint> </hints> </connection> - <connection> - <sender>list_community_members</sender> - <signal>customContextMenuRequested(QPoint)</signal> - <receiver>CommunityTabWidget</receiver> - <slot>member_context_menu(QPoint)</slot> - <hints> - <hint type="sourcelabel"> - <x>210</x> - <y>179</y> - </hint> - <hint type="destinationlabel"> - <x>210</x> - <y>184</y> - </hint> - </hints> - </connection> </connections> <slots> <slot>member_context_menu(QPoint)</slot> diff --git a/src/cutecoin/core/community.py b/src/cutecoin/core/community.py index 52d4e787..aa02d2b5 100644 --- a/src/cutecoin/core/community.py +++ b/src/cutecoin/core/community.py @@ -236,11 +236,7 @@ class Community(object): Listing members pubkeys of a community ''' memberships = self.request(bma.wot.Members) - members = [] - logging.debug(memberships) - for m in memberships["results"]: - members.append(m['pubkey']) - return members + return [m['pubkey'] for m in memberships["results"]] def refresh_cache(self): self._cache.refresh() diff --git a/src/cutecoin/core/person.py b/src/cutecoin/core/person.py index 37cec6fb..67c0419b 100644 --- a/src/cutecoin/core/person.py +++ b/src/cutecoin/core/person.py @@ -8,7 +8,9 @@ import logging from ucoinpy.api import bma from ucoinpy import PROTOCOL_VERSION from ucoinpy.documents.certification import SelfCertification -from cutecoin.tools.exceptions import PersonNotFoundError +from ucoinpy.documents.membership import Membership +from cutecoin.tools.exceptions import PersonNotFoundError,\ + MembershipNotFoundError class Person(object): @@ -76,6 +78,27 @@ class Person(object): signature) raise PersonNotFoundError(self.pubkey, community.name()) + def membership(self, community): + search = community.request(bma.blockchain.Membership, + {'search': self.pubkey}) + block_number = 0 + for ms in search['memberships']: + if ms['blockNumber'] >= block_number: + if 'type' in ms: + if ms['type'] is 'IN': + membership_data = ms + else: + membership_data = ms + + if membership_data is None: + raise MembershipNotFoundError(self.pubkey(), community.name()) + + membership = Membership(PROTOCOL_VERSION, community.currency, self.pubkey, + membership_data['blockNumber'], + membership_data['blockHash'], 'IN', search['uid'], + search['sigDate'], None) + return membership + def jsonify(self): data = {'name': self.name, 'pubkey': self.pubkey} diff --git a/src/cutecoin/gui/community_tab.py b/src/cutecoin/gui/community_tab.py index 19d19764..909fd84f 100644 --- a/src/cutecoin/gui/community_tab.py +++ b/src/cutecoin/gui/community_tab.py @@ -8,7 +8,7 @@ import logging from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QWidget, QMessageBox, QAction, QMenu, QDialog, QLineEdit -from ..models.members import MembersListModel +from ..models.members import MembersFilterProxyModel, MembersTableModel from ..gen_resources.community_tab_uic import Ui_CommunityTabWidget from .add_contact import AddContactDialog from .wot_tab import WotTabWidget @@ -33,7 +33,10 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): self.community = community self.account = account self.password_asker = password_asker - self.list_community_members.setModel(MembersListModel(community)) + members_model = MembersTableModel(community) + proxy_members = MembersFilterProxyModel() + proxy_members.setSourceModel(members_model) + self.table_community_members.setModel(proxy_members) if self.account.member_of(self.community): self.button_membership.setText("Renew membership") @@ -47,8 +50,8 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): "Wot") def member_context_menu(self, point): - index = self.list_community_members.indexAt(point) - model = self.list_community_members.model() + index = self.table_community_members.indexAt(point) + model = self.table_community_members.model() if index.row() < model.rowCount(None): member = model.members[index.row()] logging.debug(member) @@ -70,7 +73,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): menu.addAction(send_money) menu.addAction(certify) # Show the context menu. - menu.exec_(self.list_community_members.mapToGlobal(point)) + menu.exec_(self.table_community_members.mapToGlobal(point)) def add_member_as_contact(self): dialog = AddContactDialog(self.account, self.window()) diff --git a/src/cutecoin/gui/mainwindow.py b/src/cutecoin/gui/mainwindow.py index f651ed22..392c12bc 100644 --- a/src/cutecoin/gui/mainwindow.py +++ b/src/cutecoin/gui/mainwindow.py @@ -122,7 +122,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): date = QDate.currentDate() self.label_time.setText("- {0} -".format(date.toString("dd/MM/yyyy"))) next_day = date.addDays(1) - current_time = QDateTime().toMSecsSinceEpoch() + current_time = QDateTime().currentDateTime().toMSecsSinceEpoch() next_time = QDateTime(next_day).toMSecsSinceEpoch() timer = QTimer() timer.timeout.connect(self.update_time) diff --git a/src/cutecoin/models/members.py b/src/cutecoin/models/members.py index 11e9cd66..4b56de02 100644 --- a/src/cutecoin/models/members.py +++ b/src/cutecoin/models/members.py @@ -4,11 +4,57 @@ Created on 5 févr. 2014 @author: inso ''' -from cutecoin.core.person import Person -from PyQt5.QtCore import QAbstractListModel, Qt +from ucoinpy.api import bma +from ..core.person import Person +from PyQt5.QtCore import QAbstractTableModel, QSortFilterProxyModel, Qt, \ + QDateTime +from PyQt5.QtGui import QColor +import logging -class MembersListModel(QAbstractListModel): +class MembersFilterProxyModel(QSortFilterProxyModel): + def __init__(self, parent=None): + super().__init__(parent) + self.community = None + + def setSourceModel(self, sourceModel): + self.community = sourceModel.community + super().setSourceModel(sourceModel) + + def lessThan(self, left, right): + """ + Sort table by given column number. + """ + left_data = self.sourceModel().data(left, Qt.DisplayRole) + right_data = self.sourceModel().data(right, Qt.DisplayRole) + return (left_data < right_data) + + def data(self, index, role): + source_index = self.mapToSource(index) + source_data = self.sourceModel().data(source_index, role) + expiration_col = self.sourceModel().columns.index('Expiration') + expiration_index = self.sourceModel().index(source_index.row(), expiration_col) + expiration_data = self.sourceModel().data(expiration_index, Qt.DisplayRole) + current_time = QDateTime().currentDateTime().toMSecsSinceEpoch() + #logging.debug("{0} > {1}".format(current_time, expiration_data)) + will_expire_soon = (current_time > expiration_data *1000 - 15*24*3600*1000) + if role == Qt.DisplayRole: + if source_index.column() == self.sourceModel().columns.index('Join date'): + date = QDateTime.fromTime_t(source_data) + return date.date() + if source_index.column() == self.sourceModel().columns.index('Expiration'): + date = QDateTime.fromTime_t(source_data) + return date.date() + if source_index.column() == self.sourceModel().columns.index('Pubkey'): + return "pub:{0}".format(source_data[:5]) + + if role == Qt.ForegroundRole: + if will_expire_soon: + return QColor(Qt.red) + return source_data + + +class MembersTableModel(QAbstractTableModel): ''' A Qt abstract item model to display communities in a tree @@ -18,20 +64,37 @@ class MembersListModel(QAbstractListModel): ''' Constructor ''' - super(MembersListModel, self).__init__(parent) - pubkeys = community.members_pubkeys() - self.members = [] - for p in pubkeys: - self.members.append(Person.lookup(p, community)) + super().__init__(parent) + self.community = community + self.columns = ('UID', 'Pubkey', 'Join date', 'Expiration') + + @property + def pubkeys(self): + return self.community.members_pubkeys() def rowCount(self, parent): - return len(self.members) + return len(self.pubkeys) + + def columnCount(self, parent): + return len(self.columns) + + def headerData(self, section, orientation, role): + if role == Qt.DisplayRole: + return self.columns[section] + + def member_data(self, pubkey): + person = Person.lookup(pubkey, self.community) + join_block = person.membership(self.community).block_number + join_date = self.community.get_block(join_block).time + parameters = self.community.get_parameters() + expiration_date = join_date + parameters['sigValidity'] + return (person.name, pubkey, join_date, expiration_date) def data(self, index, role): if role == Qt.DisplayRole: row = index.row() - value = self.members[row].name - return value + col = index.column() + return self.member_data(self.pubkeys[row])[col] def flags(self, index): return Qt.ItemIsSelectable | Qt.ItemIsEnabled diff --git a/src/cutecoin/tools/exceptions.py b/src/cutecoin/tools/exceptions.py index 27317ae6..1cbc25c4 100644 --- a/src/cutecoin/tools/exceptions.py +++ b/src/cutecoin/tools/exceptions.py @@ -50,6 +50,25 @@ class PersonNotFoundError(Error): " not found ") +class MembershipNotFoundError(Error): + + ''' + Exception raised when looking for a person in a community + who isnt present in key list + ''' + + def __init__(self, value, community): + ''' + Constructor + ''' + super() .__init__( + "Membership searched by " + + value + + " in " + + community + + " not found ") + + class AlgorithmNotImplemented(Error): ''' -- GitLab