From 64a4e7813f36a21844b677a4c42a66dc826d6956 Mon Sep 17 00:00:00 2001 From: Inso <insomniak.fr@gmail.com> Date: Fri, 13 Mar 2015 08:16:47 +0100 Subject: [PATCH] Docstrings + a bit of code cleaning --- src/cutecoin/core/account.py | 144 ++++++++++++---- src/cutecoin/core/app.py | 89 +++++++++- src/cutecoin/core/community.py | 191 +++++++++++++++++++++- src/cutecoin/core/graph.py | 2 +- src/cutecoin/core/net/network.py | 80 ++++++--- src/cutecoin/core/net/node.py | 30 +++- src/cutecoin/core/person.py | 105 ++++++++++-- src/cutecoin/core/transfer.py | 83 +++++++++- src/cutecoin/core/wallet.py | 107 +++++++++++- src/cutecoin/core/watchers/blockchain.py | 2 +- src/cutecoin/gui/community_tab.py | 4 +- src/cutecoin/gui/currency_tab.py | 6 +- src/cutecoin/gui/informations_tab.py | 4 +- src/cutecoin/gui/mainwindow.py | 3 +- src/cutecoin/gui/process_cfg_community.py | 2 +- src/cutecoin/gui/wallets_tab.py | 2 +- src/cutecoin/models/community.py | 2 +- src/cutecoin/models/members.py | 4 +- src/cutecoin/models/network.py | 1 - 19 files changed, 747 insertions(+), 114 deletions(-) diff --git a/src/cutecoin/core/account.py b/src/cutecoin/core/account.py index 1c678750..24772e50 100644 --- a/src/cutecoin/core/account.py +++ b/src/cutecoin/core/account.py @@ -6,8 +6,6 @@ Created on 1 févr. 2014 from ucoinpy import PROTOCOL_VERSION from ucoinpy.api import bma -from ucoinpy.api.bma import ConnectionHandler -from ucoinpy.documents.peer import Peer from ucoinpy.documents.certification import SelfCertification, Certification from ucoinpy.documents.membership import Membership from ucoinpy.key import SigningKey @@ -20,7 +18,7 @@ from PyQt5.QtCore import QObject, pyqtSignal, Qt from .wallet import Wallet from .community import Community from .person import Person -from ..tools.exceptions import NoPeerAvailable, ContactAlreadyExists +from ..tools.exceptions import ContactAlreadyExists def quantitative(units, community): @@ -63,31 +61,53 @@ class Account(QObject): loading_progressed = pyqtSignal(int, int) - def __init__(self, salt, pubkey, name, communities, wallets, contacts, - dead_communities): + def __init__(self, salt, pubkey, name, communities, wallets, contacts): ''' - Constructor + Create an account + + :param str salt: The root key salt + :param str pubkey: Known account pubkey. Used to check that password \ + is OK by comparing (salt, given_passwd) = (pubkey, privkey) \ + with known pubkey + :param str name: The account name, same as network identity uid + :param array communities: Community objects referenced by this account + :param array wallets: Wallet objects owned by this account + :param array contacts: Contacts of this account + + .. warnings:: The class methods create and load should be used to create an account ''' super().__init__() self.salt = salt self.pubkey = pubkey self.name = name self.communities = communities - self.dead_communities = dead_communities self.wallets = wallets self.contacts = contacts self.referential = 'Units' @classmethod - def create(cls, name, communities, wallets, confpath): + def create(cls, name): ''' - Constructor + Factory method to create an empty account object + This new account doesn't have any key and it should be given + one later + It doesn't have any community nor does it have wallets. + Communities could be added later, wallets will be managed + by its wallet pool size. + + :param str name: The account name, same as network identity uid + :return: A new empty account object ''' - account = cls(None, None, name, communities, wallets, [], []) + account = cls(None, None, name, [], [], [], []) return account @classmethod def load(cls, json_data): + ''' + Factory method to create an Account object from its json view. + :param dict json_data: The account view as a json dict + :return: A new account object created from the json datas + ''' salt = json_data['salt'] pubkey = json_data['pubkey'] @@ -102,25 +122,35 @@ class Account(QObject): wallets.append(Wallet.load(data)) communities = [] - dead_communities = [] for data in json_data['communities']: community = Community.load(data) communities.append(community) account = cls(salt, pubkey, name, communities, wallets, - contacts, dead_communities) + contacts) return account def __eq__(self, other): + ''' + :return: True if account.pubkey == other.pubkey + ''' if other is not None: return other.pubkey == self.pubkey else: return False def check_password(self, password): + ''' + Method to verify the key password validity + + :param str password: The key password + :return: True if the generated pubkey is the same as the account + .. warnings:: Generates a new temporary SigningKey + ''' key = SigningKey(self.salt, password) return (key.pubkey == self.pubkey) +#TODO: Contacts should be pure json, not Person objects def add_contact(self, person): same_contact = [contact for contact in self.contacts if person.pubkey == contact.pubkey] @@ -130,11 +160,21 @@ class Account(QObject): self.contacts.append(person) def add_community(self, community): - logging.debug("Adding a community") + ''' + Add a community to the account + + :param community: A community object to add + ''' self.communities.append(community) return community def refresh_cache(self): + ''' + Refresh the local account cache + This needs n_wallets * n_communities cache refreshing to end + + .. note:: emit the Account pyqtSignal loading_progressed during refresh + ''' loaded_wallets = 0 def progressing(value, maximum): @@ -166,15 +206,29 @@ class Account(QObject): return Account.referentials[self.referential][3].format(currency) def set_walletpool_size(self, size, password): + ''' + Change the size of the wallet pool + + :param int size: The new size of the wallet pool + :param str password: The password of the account, same for all wallets + ''' logging.debug("Defining wallet pool size") if len(self.wallets) < size: for i in range(len(self.wallets), size): - wallet = Wallet.create(i, self.salt, password, "Wallet {0}".format(i)) + wallet = Wallet.create(i, self.salt, password, + "Wallet {0}".format(i)) self.wallets.append(wallet) else: self.wallets = self.wallets[:size] def certify(self, password, community, pubkey): + ''' + Certify an other identity + + :param str password: The account SigningKey password + :param community: The community target of the certification + :param str pubkey: The certified identity pubkey + ''' certified = Person.lookup(pubkey, community) blockid = community.current_blockid() @@ -196,14 +250,14 @@ class Account(QObject): logging.debug("Posted data : {0}".format(data)) community.broadcast(bma.wot.Add, {}, data) - def sources(self, community): - sources = [] - for w in self.wallets: - for s in w.sources(community): - sources.append(s) - return sources - def transfers(self, community): + ''' + Get all transfers done in a community by all the wallets + owned by this account + + :param community: The target community of this request + :return: All account wallets transfers + ''' sent = [] for w in self.wallets: for transfer in w.transfers(community): @@ -211,20 +265,35 @@ class Account(QObject): return sent def amount(self, community): + ''' + Get amount of money owned in a community by all the wallets + owned by this account + + :param community: The target community of this request + :return: The value of all wallets values accumulated + ''' value = 0 for w in self.wallets: value += w.value(community) return value def member_of(self, community): - pubkeys = community.members_pubkeys() - if self.pubkey not in pubkeys: - logging.debug("{0} not found in members : {1}".format(self.pubkey, - pubkeys)) - return False - return True + ''' + Check if this account identity is a member of a community + + :param community: The target community of this request + :return: True if the account is a member of the target community + ''' + self_person = Person.lookup(self.pubkey, community) + return self_person.is_member() + + def send_selfcert(self, password, community): + ''' + Send our self certification to a target community - def send_pubkey(self, password, community): + :param str password: The account SigningKey password + :param community: The community target of the self certification + ''' selfcert = SelfCertification(PROTOCOL_VERSION, community.currency, self.pubkey, @@ -238,7 +307,14 @@ class Account(QObject): 'self_': selfcert.signed_raw(), 'other': []}) - def send_membership(self, password, community, type): + def send_membership(self, password, community, mstype): + ''' + Send a membership document to a target community + + :param str password: The account SigningKey password + :param community: The community target of the membership document + :param str mstype: The type of membership demand. "IN" to join, "OUT" to leave + ''' self_ = Person.lookup(self.pubkey, community) selfcert = self_.selfcert(community) @@ -246,7 +322,7 @@ class Account(QObject): membership = Membership(PROTOCOL_VERSION, community.currency, selfcert.pubkey, blockid['number'], - blockid['hash'], type, selfcert.uid, + blockid['hash'], mstype, selfcert.uid, selfcert.timestamp, None) key = SigningKey(self.salt, password) membership.sign([key]) @@ -255,9 +331,13 @@ class Account(QObject): {'membership': membership.signed_raw()}) def jsonify(self): + ''' + Get the account in a json format. + + :return: A dict view of the account to be saved as json + ''' data_communities = [] - communities = self.communities + self.dead_communities - for c in communities: + for c in self.communities: data_communities.append(c.jsonify()) data_wallets = [] diff --git a/src/cutecoin/core/app.py b/src/cutecoin/core/app.py index 7ef3a31d..591de4ca 100644 --- a/src/cutecoin/core/app.py +++ b/src/cutecoin/core/app.py @@ -24,13 +24,16 @@ class Application(QObject): ''' Managing core application datas : Accounts list and general configuration + Saving and loading the application state ''' loading_progressed = pyqtSignal(int, int) def __init__(self, argv): ''' - Constructor + Create a new "cutecoin" application + + :param argv: The argv parameters of the call ''' super().__init__() self.accounts = {} @@ -40,6 +43,12 @@ class Application(QObject): self.load() def get_account(self, name): + ''' + Load an account then return it + + :param str name: The account name + :return: The loaded account if it's a success, else return None + ''' self.load_account(name) if name in self.accounts.keys(): return self.accounts[name] @@ -47,13 +56,18 @@ class Application(QObject): return None def create_account(self, name): + ''' + Create a new account from its name + + :param str name: The account name + :return: The new account + :raise: NameAlreadyExists if the account name is already used locally + ''' for a in self.accounts: if a == name: raise NameAlreadyExists(a) account = Account.create(name, - [], - [], config.parameters) return account @@ -62,11 +76,22 @@ class Application(QObject): self.accounts[account.name] = account def delete_account(self, account): + ''' + Delete an account. + Current account changes to None if it is deleted. + ''' self.accounts.pop(account.name) if self.current_account == account: self.current_account = None def change_current_account(self, account): + ''' + Change current account displayed and refresh its cache. + + :param account: The account object to display + .. note:: Emits the application pyqtSignal loading_progressed + during cache refresh + ''' def progressing(value, maximum): self.loading_progressed.emit(value, maximum) @@ -77,6 +102,13 @@ class Application(QObject): self.current_account = account def load(self): + ''' + Load a saved application state from the data file. + Loads only jsonified objects but not their cache. + + If the standard application state file can't be found, + no error is raised. + ''' self.load_persons() try: logging.debug("Loading data...") @@ -90,6 +122,10 @@ class Application(QObject): pass def load_persons(self): + ''' + Load the Person instances of the person module. + Each instance is unique, and can be find by its public key. + ''' try: persons_path = os.path.join(config.parameters['home'], '__persons__') @@ -100,6 +136,11 @@ class Application(QObject): pass def load_account(self, account_name): + ''' + Load an account from its name + + :param str account_name: The account name + ''' account_path = os.path.join(config.parameters['home'], account_name, 'properties') with open(account_path, 'r') as json_data: @@ -109,6 +150,11 @@ class Application(QObject): self.accounts[account_name] = account def load_cache(self, account): + ''' + Load an account cache + + :param account: The account object to load the cache + ''' for community in account.communities: community_path = os.path.join(config.parameters['home'], account.name, '__cache__', @@ -122,7 +168,7 @@ class Application(QObject): with open(network_path, 'r') as json_data: data = json.load(json_data) if 'version' in data and data['version'] == __version__: - community.load_network(data) + community.load_merge_network(data) else: os.remove(network_path) @@ -146,6 +192,11 @@ class Application(QObject): os.remove(wallet_path) def save(self, account): + ''' + Save an account + + :param account: The account object to save + ''' with open(config.parameters['data'], 'w') as outfile: json.dump(self.jsonify(), outfile, indent=4, sort_keys=True) account_path = os.path.join(config.parameters['home'], @@ -162,6 +213,9 @@ class Application(QObject): shutil.rmtree(account_path) def save_persons(self): + ''' + Save the person module cache + ''' persons_path = os.path.join(config.parameters['home'], '__persons__') with open(persons_path, 'w')as outfile: @@ -170,6 +224,11 @@ class Application(QObject): json.dump(data, outfile, indent=4, sort_keys=True) def save_cache(self, account): + ''' + Save the cache of an account + + :param account: The account object to save the cache + ''' if not os.path.exists(os.path.join(config.parameters['home'], account.name, '__cache__')): os.makedirs(os.path.join(config.parameters['home'], @@ -202,6 +261,12 @@ class Application(QObject): json.dump(data, outfile, indent=4, sort_keys=True) def import_account(self, file, name): + ''' + Import an account from a tar file + + :param str file: The file path of the tar file + :param str name: The account name + ''' with tarfile.open(file, "r") as tar: path = os.path.join(config.parameters['home'], name) @@ -222,6 +287,12 @@ class Application(QObject): self.save(account) def export_account(self, file, account): + ''' + Export an account to a tar file + + :param str file: The filepath of the tar file + :param account: The account object to export + ''' with tarfile.open(file, "w") as tar: for file in ["properties"]: path = os.path.join(config.parameters['home'], @@ -229,6 +300,11 @@ class Application(QObject): tar.add(path, file) def jsonify_accounts(self): + ''' + Jsonify an account + + :return: The account as a dict to format as json + ''' data = [] logging.debug("{0}".format(self.accounts)) for account in self.accounts: @@ -236,6 +312,11 @@ class Application(QObject): return data def jsonify(self): + ''' + Jsonify the app datas + + :return: The accounts of the app to format as json + ''' data = {'default_account': self.default_account, 'local_accounts': self.jsonify_accounts()} return data diff --git a/src/cutecoin/core/community.py b/src/cutecoin/core/community.py index 762dd502..171fc813 100644 --- a/src/cutecoin/core/community.py +++ b/src/cutecoin/core/community.py @@ -21,11 +21,19 @@ class Cache(): hash(bma.wot.Lookup)] def __init__(self, community): + ''' + Init an empty cache + ''' self.latest_block = 0 self.community = community self.data = {} def load_from_json(self, data): + ''' + Put data in the cache from json datas. + + :param dict data: The cache in json format + ''' self.data = {} for entry in data['cache']: key = entry['key'] @@ -35,6 +43,11 @@ class Cache(): self.latest_block = data['latest_block'] def jsonify(self): + ''' + Get the cache in json format + + :return: The cache as a dict in json format + ''' data = {k: self.data[k] for k in self.data.keys() if k[0] in Cache._saved_requests} entries = [] @@ -45,11 +58,23 @@ class Cache(): 'cache': entries} def refresh(self): + ''' + Refreshing the cache just clears every last requests which + cannot be saved because they can change from one block to another. + ''' self.latest_block = self.community.current_blockid()['number'] self.data = {k: self.data[k] for k in self.data.keys() if k[0] in Cache._saved_requests} def request(self, request, req_args={}, get_args={}): + ''' + Send a cached request to a community. + If the request was already sent in current block, return last value get. + + :param request: The request bma class + :param req_args: The arguments passed to the request constructor + :param get_args: The arguments passed to the requests __get__ method + ''' cache_key = (hash(request), hash(tuple(frozenset(sorted(req_args.keys())))), hash(tuple(frozenset(sorted(req_args.items())))), @@ -72,12 +97,21 @@ class Cache(): class Community(object): ''' - classdocs + A community is a group of nodes using the same currency. + + .. warning:: The currency name is supposed to be unique in cutecoin + but nothing exists in ucoin to assert that a currency name is unique. ''' def __init__(self, currency, network): ''' - A community is a group of nodes using the same currency. + Initialize community attributes with a currency and a network. + + :param str currency: The currency name of the community. + :param network: The network of the community + + .. warning:: The community object should be created using its factory + class methods. ''' self.currency = currency self._network = network @@ -87,6 +121,11 @@ class Community(object): @classmethod def create(cls, node): + ''' + Create a community from its first node. + + :param node: The first Node of the community + ''' network = Network.create(node) community = cls(node.currency, network) logging.debug("Creating community") @@ -94,31 +133,70 @@ class Community(object): @classmethod def load(cls, json_data): + ''' + Load a community from json + + :param dict json_data: The community as a dict in json format + ''' currency = json_data['currency'] network = Network.from_json(currency, json_data['peers']) community = cls(currency, network) return community - def load_network(self, json_data): - self._network.merge_with_json(json_data['network']) + def load_merge_network(self, json_data): + ''' + Load the last state of the network. + This network is merged with the network currently + known network. + + :param dict json_data: + ''' + self._network.merge_with_json(json_data) def load_cache(self, json_data): + ''' + Load the community cache. + + :param dict json_data: The community as a dict in json format. + ''' self._cache.load_from_json(json_data) def jsonify_cache(self): + ''' + Get the cache jsonified. + + :return: The cache as a dict in json format. + ''' return self._cache.jsonify() def jsonify_network(self): - return {'network': self._network.jsonify()} + ''' + Get the network jsonified. + ''' + return self._network.jsonify() + @property def name(self): + ''' + The community name is its currency name. + + :return: The community name + ''' return self.currency def __eq__(self, other): + ''' + :return: True if this community has the same currency name as the other one. + ''' return (other.currency == self.currency) @property def short_currency(self): + ''' + Format the currency name to a short one + + :return: The currency name in a shot format. + ''' words = re.split('[_\W]+', self.currency) shortened = "" if len(words) > 1: @@ -131,12 +209,22 @@ class Community(object): @property def currency_symbol(self): + ''' + Format the currency name to a symbol one. + + :return: The currency name as a utf-8 circled symbol. + ''' letter = self.currency[0] u = ord('\u24B6') + ord(letter) - ord('A') return chr(u) @property def dividend(self): + ''' + Get the last generated community universal dividend. + + :return: The last UD or 1 if no UD was generated. + ''' block = self.get_ud_block() if block: return block['dividend'] @@ -144,6 +232,12 @@ class Community(object): return 1 def get_ud_block(self, x=0): + ''' + Get a block with universal dividend + + :param int x: Get the 'x' older block with UD in it + :return: The last block with universal dividend. + ''' blocks = self.request(bma.blockchain.UD)['result']['blocks'] if len(blocks) > 0: block_number = blocks[len(blocks)-(1+x)] @@ -155,6 +249,11 @@ class Community(object): @property def monetary_mass(self): + ''' + Get the community monetary mass + + :return: The monetary mass value + ''' try: block = self.request(bma.blockchain.Current) return block['monetaryMass'] @@ -164,6 +263,11 @@ class Community(object): @property def nb_members(self): + ''' + Get the community members number + + :return: The community members number + ''' try: block = self.request(bma.blockchain.Current) return block['membersCount'] @@ -173,16 +277,44 @@ class Community(object): @property def nodes(self): + ''' + Get the known community nodes + + :return: All community known nodes + ''' return self._network.all_nodes @property def network(self): + ''' + Get the community network instance. + + :return: The community network instance. + ''' return self._network + @property + def parameters(self): + ''' + + ''' + return self.request(bma.blockchain.Parameters) + + @property def add_peer(self, peer): + ''' + Add a peer to the community. + + :param peer: The new peer as a ucoinpy Peer object. + ''' self._network.add_node(Node.from_peer(peer)) def get_block(self, number=None): + ''' + Get a block + + :param int number: The block number. If none, returns current block. + ''' if number is None: data = self.request(bma.blockchain.Current) else: @@ -193,6 +325,11 @@ class Community(object): data['signature'])) def current_blockid(self): + ''' + Get the current block id. + + :return: The current block ID as [NUMBER-HASH] format. + ''' try: block = self.request(bma.blockchain.Current, cached=False) signed_raw = "{0}{1}\n".format(block['raw'], block['signature']) @@ -209,14 +346,27 @@ class Community(object): def members_pubkeys(self): ''' Listing members pubkeys of a community + + :return: All members pubkeys. ''' memberships = self.request(bma.wot.Members) return [m['pubkey'] for m in memberships["results"]] def refresh_cache(self): + ''' + Start the refresh processing of the cache + ''' self._cache.refresh() def request(self, request, req_args={}, get_args={}, cached=True): + ''' + Start a request to the community. + + :param request: A ucoinpy bma request class + :param req_args: Arguments to pass to the request constructor + :param get_args: Arguments to pass to the request __get__ method + :return: The returned data + ''' if cached: return self._cache.request(request, req_args, get_args) else: @@ -244,6 +394,15 @@ class Community(object): raise NoPeerAvailable(self.currency, len(nodes)) def post(self, request, req_args={}, post_args={}): + ''' + Post data to a community. + Only sends the data to one node. + + :param request: A ucoinpy bma request class + :param req_args: Arguments to pass to the request constructor + :param post_args: Arguments to pass to the request __post__ method + :return: The returned data + ''' nodes = self._network.online_nodes for node in nodes: req = request(node.endpoint.conn_handler(), **req_args) @@ -259,6 +418,18 @@ class Community(object): raise NoPeerAvailable(self.currency, len(nodes)) def broadcast(self, request, req_args={}, post_args={}): + ''' + Broadcast data to a community. + Sends the data to all knew nodes. + + :param request: A ucoinpy bma request class + :param req_args: Arguments to pass to the request constructor + :param post_args: Arguments to pass to the request __post__ method + :return: The returned data + + .. note:: If one node accept the requests (returns 200), + the broadcast is considered accepted by the network. + ''' tries = 0 ok = False value_error = None @@ -283,9 +454,11 @@ class Community(object): raise NoPeerAvailable(self.currency, len(nodes)) def jsonify(self): + ''' + Jsonify the community datas. + + :return: The community as a dict in json format. + ''' data = {'currency': self.currency, 'peers': self._network.jsonify()} - return data - - def get_parameters(self): - return self.request(bma.blockchain.Parameters) + return data \ No newline at end of file diff --git a/src/cutecoin/core/graph.py b/src/cutecoin/core/graph.py index e768f308..ee11732d 100644 --- a/src/cutecoin/core/graph.py +++ b/src/cutecoin/core/graph.py @@ -15,7 +15,7 @@ class Graph(object): """ self.community = community - self.signature_validity = self.community.get_parameters()['sigValidity'] + self.signature_validity = self.community.parameters['sigValidity'] #  arc considered strong during 75% of signature validity time self.ARC_STATUS_STRONG_time = int(self.signature_validity * 0.75) # graph empty if None parameter diff --git a/src/cutecoin/core/net/network.py b/src/cutecoin/core/net/network.py index af0c247a..bab907d6 100644 --- a/src/cutecoin/core/net/network.py +++ b/src/cutecoin/core/net/network.py @@ -17,13 +17,17 @@ from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot class Network(QObject): ''' - classdocs + A network is managing nodes polling and crawling of a + given community. ''' nodes_changed = pyqtSignal() def __init__(self, currency, nodes): ''' - Constructor + Constructor of a network + + :param str currency: The currency name of the community + :param list nodes: The nodes of the network ''' super().__init__() self.currency = currency @@ -33,25 +37,29 @@ class Network(QObject): self.must_crawl = False @classmethod - def from_json(cls, currency, json_data): + def create(cls, node): ''' - We load the nodes which we know for sure since we - used them at the community creation + Create a new network with one knew node + Crawls the nodes from the first node to build the + community network + + :param node: The first knew node of the network ''' - nodes = [] - for data in json_data: - node = Node.from_json(currency, data) - nodes.append(node) - logging.debug("Loading : {:}".format(data['pubkey'])) + nodes = [node] + network = cls(node.currency, nodes) + nodes = network.crawling() block_max = max([n.block for n in nodes]) for node in nodes: node.check_sync(block_max) - return cls(currency, nodes) + network._nodes = nodes + return network def merge_with_json(self, json_data): ''' - We merge with dynamic nodes detected when we + We merge with knew nodes when we last stopped cutecoin + + :param dict json_data: Nodes in json format ''' for data in json_data: node = Node.from_json(self.currency, data) @@ -60,41 +68,66 @@ class Network(QObject): self._nodes = self.crawling() @classmethod - def create(cls, node): - nodes = [node] - network = cls(node.currency, nodes) - nodes = network.crawling() + def from_json(cls, currency, json_data): + ''' + Load a network from a configured community + + :param str currency: The currency name of a community + :param dict json_data: A json_data view of a network + ''' + nodes = [] + for data in json_data: + node = Node.from_json(currency, data) + nodes.append(node) + logging.debug("Loading : {:}".format(data['pubkey'])) block_max = max([n.block for n in nodes]) for node in nodes: node.check_sync(block_max) - network._nodes = nodes - return network + return cls(currency, nodes) def jsonify(self): + ''' + Get the network in json format. + + :return: The network as a dict in json format. + ''' data = [] for node in self._nodes: data.append(node.jsonify()) return data - def __del__(self): - self.must_crawl = False - def stop_crawling(self): + ''' + Stop network nodes crawling. + ''' self.must_crawl = False @property def online_nodes(self): + ''' + Get nodes which are in the ONLINE state. + ''' return [n for n in self._nodes if n.state == Node.ONLINE] @property def all_nodes(self): + ''' + Get all knew nodes. + ''' return self._nodes.copy() def add_nodes(self, node): + ''' + Add a node to the network. + ''' self._nodes.append(node) node.changed.connect(self.nodes_changed) def start_perpetual_crawling(self): + ''' + Start crawling which never stops. + To stop this crawling, call "stop_crawling" method. + ''' self.must_crawl = True while self.must_crawl: nodes = self.crawling(interval=10) @@ -112,6 +145,11 @@ class Network(QObject): n.changed.connect(self.nodes_changed) def crawling(self, interval=0): + ''' + One network crawling. + + :param int interval: The interval between two nodes request. + ''' nodes = [] traversed_pubkeys = [] for n in self._nodes.copy(): diff --git a/src/cutecoin/core/net/node.py b/src/cutecoin/core/net/node.py index 3c7ef40a..9ddefd1c 100644 --- a/src/cutecoin/core/net/node.py +++ b/src/cutecoin/core/net/node.py @@ -17,7 +17,12 @@ from PyQt5.QtCore import QObject, pyqtSignal class Node(QObject): ''' - classdocs + A node is a peer seend from the client point of view. + This node can have multiple states : + - ONLINE : The node is available for requests + - OFFLINE: The node is disconnected + - DESYNCED : The node is online but is desynced from the network + - CORRUPTED : The node is corrupted, some weird behaviour is going on ''' ONLINE = 1 @@ -29,7 +34,13 @@ class Node(QObject): def __init__(self, currency, endpoints, pubkey, block, state): ''' - Constructor + Constructor of a node + + :param str currency: The currency name of this node community + :param list endpoints: Endpoints ucoinpy objects + :param str pubkey: The node pubkey + :param int block: The node last current block + :param state: The state of the node ''' super().__init__() self._endpoints = endpoints @@ -41,6 +52,14 @@ class Node(QObject): @classmethod def from_address(cls, currency, address, port): + ''' + Factory method to get a node from a given address + + :param str currency: The node currency. None if we don't know\ + the currency it should have, for example if its the first one we add + :param str address: The node address + :param int port: The node port + ''' peer_data = bma.network.Peering(ConnectionHandler(address, port)).get() peer = Peer.from_signed_raw("{0}{1}\n".format(peer_data['raw'], @@ -56,6 +75,13 @@ class Node(QObject): @classmethod def from_peer(cls, currency, peer): + ''' + Factory method to get a node from a peer document. + + :param str currency: The node currency. None if we don't know\ + the currency it should have, for example if its the first one we add + :param peer: The peer document + ''' if currency is not None: if peer.currency != currency: raise InvalidNodeCurrency(peer.currency, currency) diff --git a/src/cutecoin/core/person.py b/src/cutecoin/core/person.py index bcaa2d8a..153300c5 100644 --- a/src/cutecoin/core/person.py +++ b/src/cutecoin/core/person.py @@ -65,15 +65,20 @@ class cached(object): return functools.partial(self, inst) +#TODO: Change Person to Identity ? class Person(object): ''' - A person with a name, a fingerprint and an email + A person with a name and a pubkey ''' _instances = {} def __init__(self, name, pubkey, cache): ''' - Constructor + Initializing a person object. + + :param str name: The person name, also known as its uid on the network + :param str pubkey: The person pubkey + :param cache: The last returned values of the person properties. ''' self.name = name self.pubkey = pubkey @@ -83,9 +88,17 @@ class Person(object): @classmethod def lookup(cls, pubkey, community, cached=True): ''' - Create a person from the pubkey found in a community + Get a person from the pubkey found in a community + + :param str pubkey: The person pubkey + :param community: The community in which to look for the pubkey + :param bool cached: True if the person should be searched in the + cache before requesting the community. + + :return: A new person if the pubkey was unknown or\ + the known instance if pubkey was already known. ''' - if pubkey in Person._instances: + if cached and pubkey in Person._instances: return Person._instances[pubkey] else: data = community.request(bma.wot.Lookup, req_args={'search': pubkey}, @@ -104,10 +117,18 @@ class Person(object): Person._instances[pubkey] = person logging.debug("{0}".format(Person._instances.keys())) return person - raise PersonNotFoundError(pubkey, community.name()) + raise PersonNotFoundError(pubkey, community.name) @classmethod def from_metadata(cls, metadata): + ''' + Get a person from a metadata dict. + A metadata dict has a 'text' key corresponding to the person name, + and a 'id' key corresponding to the person pubkey. + + :param dict metadata: The person metadata + :return: A new person if pubkey wasn't knwon, else the existing instance. + ''' name = metadata['text'] pubkey = metadata['id'] if pubkey in Person._instances: @@ -119,17 +140,20 @@ class Person(object): @classmethod #TODO: Remove name from person, contats should not use the person class - def from_json(cls, json_person): + def from_json(cls, json_data): ''' Create a person from json data + + :param dict json_data: The person as a dict in json format + :return: A new person if pubkey wasn't known, else a new person instance. ''' - pubkey = json_person['pubkey'] + pubkey = json_data['pubkey'] if pubkey in Person._instances: return Person._instances[pubkey] else: - name = json_person['name'] - if 'cache' in json_person: - cache = json_person['cache'] + name = json_data['name'] + if 'cache' in json_data: + cache = json_data['cache'] else: cache = {} @@ -138,6 +162,13 @@ class Person(object): return person def selfcert(self, community): + ''' + Get the person self certification. + This request is not cached in the person object. + + :param community: The community target to request the self certification + :return: A SelfCertification ucoinpy object + ''' data = community.request(bma.wot.Lookup, req_args={'search': self.pubkey}) logging.debug(data) timestamp = 0 @@ -157,9 +188,17 @@ class Person(object): timestamp, name, signature) - raise PersonNotFoundError(self.pubkey, community.name()) + raise PersonNotFoundError(self.pubkey, community.name) +#TODO: Cache this data by returning only the timestamp instead of a datetime object def get_join_date(self, community): + ''' + Get the person join date. + This request is not cached in the person object. + + :param community: The community target to request the join date + :return: A datetime object + ''' try: search = community.request(bma.blockchain.Membership, {'search': self.pubkey}) membership_data = None @@ -170,10 +209,17 @@ class Person(object): return None except ValueError as e: if '400' in str(e): - raise MembershipNotFoundError(self.pubkey, community.name()) + raise MembershipNotFoundError(self.pubkey, community.name) +#TODO: Manage 'OUT' memberships @cached def membership(self, community): + ''' + Get the person last membership document. + + :param community: The community target to request the join date + :return: The membership data in BMA json format + ''' try: search = community.request(bma.blockchain.Membership, {'search': self.pubkey}) @@ -188,15 +234,21 @@ class Person(object): membership_data = ms if membership_data is None: - raise MembershipNotFoundError(self.pubkey, community.name()) + raise MembershipNotFoundError(self.pubkey, community.name) except ValueError as e: if '400' in str(e): - raise MembershipNotFoundError(self.pubkey, community.name()) + raise MembershipNotFoundError(self.pubkey, community.name) return membership_data @cached def is_member(self, community): + ''' + Check if the person is a member of a community + + :param community: The community target to request the join date + :return: True if the person is a member of a community + ''' try: certifiers = community.request(bma.wot.CertifiersOf, {'search': self.pubkey}) return certifiers['isMember'] @@ -205,6 +257,12 @@ class Person(object): @cached def certifiers_of(self, community): + ''' + Get the list of this person certifiers + + :param community: The community target to request the join date + :return: The list of the certifiers of this community in BMA json format + ''' try: certifiers = community.request(bma.wot.CertifiersOf, {'search': self.pubkey}) except ValueError as e: @@ -237,6 +295,12 @@ class Person(object): @cached def certified_by(self, community): + ''' + Get the list of persons certified by this person + + :param community: The community target to request the join date + :return: The list of the certified persons of this community in BMA json format + ''' try: certified_list = community.request(bma.wot.CertifiedBy, {'search': self.pubkey}) except ValueError as e: @@ -262,6 +326,15 @@ class Person(object): return certified_list['certifications'] def reload(self, func, community): + ''' + Reload a cached property of this person in a community. + This method is thread safe. + This method clears the cache entry for this community and get it back. + + :param func: The cached property to reload + :param community: The community to request for data + :return: True if a changed was made by the reload. + ''' self._cache_mutex.lock() if community.currency not in self._cache: self._cache[community.currency] = {} @@ -289,6 +362,10 @@ class Person(object): return change def jsonify(self): + ''' + Get the community as dict in json format. + :return: The community as a dict in json format + ''' data = {'name': self.name, 'pubkey': self.pubkey, 'cache': self._cache} diff --git a/src/cutecoin/core/transfer.py b/src/cutecoin/core/transfer.py index 5979b661..ddf144c1 100644 --- a/src/cutecoin/core/transfer.py +++ b/src/cutecoin/core/transfer.py @@ -10,18 +10,35 @@ from ucoinpy.documents.transaction import Transaction class Transfer(object): ''' - A transaction + A transfer is the lifecycle of a transaction. + TO_SEND means the transaction wasn't sent yet + AWAITING means the transaction is waiting for a blockchain validation + VALIDATED means the transaction was registered in the blockchain + REFUSED means the transaction took too long to be registered in the blockchain, + therefore it is considered as refused + DROPPED means the transaction was canceled locally. It can still be validated + in the blockchain if it was sent, if the guy is unlucky ;) ''' TO_SEND = 0 AWAITING = 1 VALIDATED = 2 REFUSED = 3 - SENT = 4 DROPPED = 5 def __init__(self, txdoc, state, metadata): ''' - Constructor + The constructor of a transfer. + Check for metadata keys which must be present : + - receiver + - block + - time + - issuer + - amount + - comment + + :param txdoc: The Transaction ucoinpy object + :param state: The state of the Transfer (TO_SEND, AWAITING, VALIDATED, REFUSED or DROPPED) + :param metadata: The transfer metadata ''' assert('receiver' in metadata) assert('block' in metadata) @@ -36,25 +53,40 @@ class Transfer(object): @classmethod def initiate(cls, metadata): + ''' + Create a new transfer in a "TO_SEND" state. + ''' return cls(None, Transfer.TO_SEND, metadata) @classmethod def create_validated(cls, txdoc, metadata): + ''' + Create a new transfer in a "VALIDATED" state. + ''' return cls(txdoc, Transfer.VALIDATED, metadata) - @property - def metadata(self): - return self._metadata - @classmethod def load(cls, data): + ''' + Create a new transfer from a dict in json format. + ''' if data['state'] is Transfer.TO_SEND: txdoc = None else: txdoc = Transaction.from_signed_raw(data['txdoc']) return cls(txdoc, data['state'], data['metadata']) + @property + def metadata(self): + ''' + :return: this transfer metadata + ''' + return self._metadata + def jsonify(self): + ''' + :return: The transfer as a dict in json format + ''' if self.txdoc: txraw = self.txdoc.signed_raw() else: @@ -64,6 +96,14 @@ class Transfer(object): 'metadata': self._metadata} def send(self, txdoc, community): + ''' + Send a transaction and update the transfer state to AWAITING if accepted. + If the transaction was refused (return code != 200), state becomes REFUSED + The txdoc is saved as the transfer txdoc. + + :param txdoc: A transaction ucoinpy object + :param community: The community target of the transaction + ''' try: self.txdoc = txdoc community.broadcast(bma.tx.Process, @@ -78,27 +118,54 @@ class Transfer(object): self._metadata['time'] = community.get_block().mediantime def check_registered(self, tx, block, time): + ''' + Check if the transfer was registered in a block. + Update the transfer state to VALIDATED if it was registered. + + :param tx: A transaction ucoinpy object found in the block + :param int block: The block number checked + :param int time: The time of the block + ''' if tx.signed_raw() == self.txdoc.signed_raw(): self.state = Transfer.VALIDATED self._metadata['block'] = block self._metadata['time'] = time def check_refused(self, block): + ''' + Check if the transfer was refused + If more than 15 blocks were mined since the transaction + transfer, it is considered as refused. + + :param int block: The current block number + ''' if block > self._metadata['block'] + 15: self.state = Transfer.REFUSED def drop(self): + ''' + Cancel the transfer locally. + The transfer state becomes "DROPPED". + ''' self.state = Transfer.DROPPED class Received(Transfer): def __init__(self, txdoc, metadata): ''' - Constructor + A transfer were the receiver is the local user. + + :param txdoc: The transaction document of the received transfer + :param metadata: The metadata of the transfer ''' super().__init__(txdoc, Transfer.VALIDATED, metadata) @classmethod def load(cls, data): + ''' + Create a transfer from a dict in json format. + + :param data: The transfer as a dict in json format + ''' txdoc = Transaction.from_signed_raw(data['txdoc']) return cls(txdoc, data['metadata']) diff --git a/src/cutecoin/core/wallet.py b/src/cutecoin/core/wallet.py index 037b72cc..e7ef3d75 100644 --- a/src/cutecoin/core/wallet.py +++ b/src/cutecoin/core/wallet.py @@ -60,6 +60,7 @@ class Cache(): def transfers(self): return [t for t in self._transfers if t.state != Transfer.DROPPED] +#TODO: Refactor to reduce this method size and split it to more methods def refresh(self, community): current_block = 0 try: @@ -153,7 +154,11 @@ class Wallet(QObject): def __init__(self, walletid, pubkey, name): ''' - Constructor + Constructor of a wallet object + + :param int walletid: The wallet number, unique between all wallets + :param str pubkey: The wallet pubkey + :param str name: The wallet name ''' super().__init__() self.coins = [] @@ -164,6 +169,14 @@ class Wallet(QObject): @classmethod def create(cls, walletid, salt, password, name): + ''' + Factory method to create a new wallet + + :param int walletid: The wallet number, unique between all wallets + :param str salt: The account salt + :param str password: The account password + :param str name: The account name + ''' if walletid == 0: key = SigningKey(salt, password) else: @@ -172,32 +185,58 @@ class Wallet(QObject): @classmethod def load(cls, json_data): + ''' + Factory method to load a saved wallet. + + :param dict json_data: The wallet as a dict in json format + ''' walletid = json_data['walletid'] pubkey = json_data['pubkey'] name = json_data['name'] return cls(walletid, pubkey, name) - def __eq__(self, other): - return (self.keyid == other.keyid) - def load_caches(self, json_data): + ''' + Load this wallet caches. + Each cache correspond to one different community. + + :param dict json_data: The caches as a dict in json format + ''' for currency in json_data: if currency != 'version': self.caches[currency] = Cache(self) self.caches[currency].load_from_json(json_data[currency]) def jsonify_caches(self): + ''' + Get this wallet caches as json. + + :return: The wallet caches as a dict in json format + ''' data = {} for currency in self.caches: data[currency] = self.caches[currency].jsonify() return data def refresh_cache(self, community): + ''' + Refresh the cache of this wallet for the specified community. + + :param community: The community to refresh its cache + ''' if community.currency not in self.caches: self.caches[community.currency] = Cache(self) self.caches[community.currency].refresh(community) def check_password(self, salt, password): + ''' + Check if wallet password is ok. + + :param salt: The account salt + :param password: The given password + :return: True if (salt, password) generates the good public key + .. warning:: Generates a new temporary SigningKey from salt and password + ''' key = None if self.walletid == 0: key = SigningKey(salt, password) @@ -205,22 +244,39 @@ class Wallet(QObject): key = SigningKey("{0}{1}".format(salt, self.walletid), password) return (key.pubkey == self.pubkey) - def show_value(self, community): - return self.referential(community) - def relative_value(self, community): + ''' + Get wallet value relative to last generated UD + + :param community: The community to get value + :return: The wallet relative value + ''' value = self.value(community) ud = community.dividend relative_value = value / float(ud) return relative_value def value(self, community): + ''' + Get wallet absolute value + + :param community: The community to get value + :return: The wallet absolute value + ''' value = 0 for s in self.sources(community): value += s.amount return value def tx_inputs(self, amount, community): + ''' + Get inputs to generate a transaction with a given amount of money + + :param int amount: The amount target value + :param community: The community target of the transaction + + :return: The list of inputs to use in the transaction document + ''' value = 0 inputs = [] cache = self.caches[community.currency] @@ -239,6 +295,15 @@ class Wallet(QObject): len(inputs), amount) def tx_outputs(self, pubkey, amount, inputs): + ''' + Get outputs to generate a transaction with a given amount of money + + :param str pubkey: The target pubkey of the transaction + :param int amount: The amount to send + :param list inputs: The inputs used to send the given amount of money + + :return: The list of outputs to use in the transaction document + ''' outputs = [] inputs_value = 0 for i in inputs: @@ -253,7 +318,16 @@ class Wallet(QObject): def send_money(self, salt, password, community, recipient, amount, message): - + ''' + Send money to a given recipient in a specified community + + :param str salt: The account salt + :param str password: The account password + :param community: The community target of the transfer + :param str recipient: The pubkey of the recipient + :param int amount: The amount of money to transfer + :param str message: The message to send with the transfer + ''' time = community.get_block().mediantime block_number = community.current_blockid()['number'] key = None @@ -292,6 +366,12 @@ class Wallet(QObject): transfer.send(tx, community) def sources(self, community): + ''' + Get available sources in a given community + + :param community: The community where we want available sources + :return: List of InputSource ucoinpy objects + ''' data = community.request(bma.tx.Sources, req_args={'pubkey': self.pubkey}) tx = [] @@ -300,9 +380,20 @@ class Wallet(QObject): return tx def transfers(self, community): + ''' + Get all transfers objects of this wallet + + :param community: The community we want to get the executed transfers + :return: A list of Transfer objects + ''' return self.caches[community.currency].transfers def jsonify(self): + ''' + Get the wallet as json format. + + :return: The wallet as a dict in json format. + ''' return {'walletid': self.walletid, 'pubkey': self.pubkey, 'name': self.name} diff --git a/src/cutecoin/core/watchers/blockchain.py b/src/cutecoin/core/watchers/blockchain.py index b2f6861f..73ca32a3 100644 --- a/src/cutecoin/core/watchers/blockchain.py +++ b/src/cutecoin/core/watchers/blockchain.py @@ -16,7 +16,7 @@ class BlockchainWatcher(QObject): super().__init__() self.account = account self.community = community - self.time_to_wait = int(self.community.get_parameters()['avgGenTime'] / 10) + self.time_to_wait = int(self.community.parameters['avgGenTime'] / 10) self.exiting = False blockid = self.community.current_blockid() self.last_block = blockid['number'] diff --git a/src/cutecoin/gui/community_tab.py b/src/cutecoin/gui/community_tab.py index 9588ec9d..f54c835c 100644 --- a/src/cutecoin/gui/community_tab.py +++ b/src/cutecoin/gui/community_tab.py @@ -123,7 +123,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): def send_money_to_member(self, person): dialog = TransferMoneyDialog(self.account, self.password_asker) dialog.edit_pubkey.setText(person.pubkey) - dialog.combo_community.setCurrentText(self.community.name()) + dialog.combo_community.setCurrentText(self.community.name) dialog.radio_pubkey.setChecked(True) if dialog.exec_() == QDialog.Accepted: currency_tab = self.window().currencies_tabwidget.currentWidget() @@ -131,7 +131,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): def certify_member(self, person): dialog = CertificationDialog(self.account, self.password_asker) - dialog.combo_community.setCurrentText(self.community.name()) + dialog.combo_community.setCurrentText(self.community.name) dialog.edit_pubkey.setText(person.pubkey) dialog.radio_pubkey.setChecked(True) dialog.exec_() diff --git a/src/cutecoin/gui/currency_tab.py b/src/cutecoin/gui/currency_tab.py index f6efa8ac..bb0d3cab 100644 --- a/src/cutecoin/gui/currency_tab.py +++ b/src/cutecoin/gui/currency_tab.py @@ -73,10 +73,10 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): try: join_block = person.membership(self.community)['blockNumber'] join_date = self.community.get_block(join_block).mediantime - parameters = self.community.get_parameters() + parameters = self.community.parameters expiration_date = join_date + parameters['sigValidity'] current_time = time.time() - sig_validity = self.community.get_parameters()['sigValidity'] + sig_validity = self.community.parameters['sigValidity'] warning_expiration_time = int(sig_validity / 3) will_expire_soon = (current_time > expiration_date - warning_expiration_time) @@ -251,7 +251,7 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): wallet_index = [w.pubkey for w in self.app.current_account.wallets].index(sender) dialog.combo_wallets.setCurrentIndex(wallet_index) dialog.edit_pubkey.setText(transfer.metadata['receiver']) - dialog.combo_community.setCurrentText(self.community.name()) + dialog.combo_community.setCurrentText(self.community.name) dialog.spinbox_amount.setValue(transfer.metadata['amount']) dialog.radio_pubkey.setChecked(True) dialog.edit_message.setText(transfer.metadata['comment']) diff --git a/src/cutecoin/gui/informations_tab.py b/src/cutecoin/gui/informations_tab.py index e96b6f1a..e5c6bebf 100644 --- a/src/cutecoin/gui/informations_tab.py +++ b/src/cutecoin/gui/informations_tab.py @@ -30,9 +30,9 @@ class InformationsTabWidget(QWidget, Ui_InformationsTabWidget): def refresh(self): #  try to request money parameters try: - params = self.community.get_parameters() + params = self.community.parameters except Exception as e: - logging.debug('community get_parameters error : ' + str(e)) + logging.debug('community parameters error : ' + str(e)) return False #  try to request money variables from last ud block diff --git a/src/cutecoin/gui/mainwindow.py b/src/cutecoin/gui/mainwindow.py index 3b5a204c..c9c0775b 100644 --- a/src/cutecoin/gui/mainwindow.py +++ b/src/cutecoin/gui/mainwindow.py @@ -97,6 +97,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.loader.loaded.connect(self.loader_thread.quit) self.loader.connection_error.connect(self.display_error) self.loader_thread.started.connect(self.loader.load) + #TODO: There are too much refresh() calls on startup self.refresh() def open_add_account_dialog(self): @@ -227,7 +228,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): tab_currency.refresh() self.currencies_tabwidget.addTab(tab_currency, QIcon(":/icons/currency_icon"), - community.name()) + community.name) except NoPeerAvailable as e: QMessageBox.critical(self, "Could not join {0}".format(community.currency), str(e), diff --git a/src/cutecoin/gui/process_cfg_community.py b/src/cutecoin/gui/process_cfg_community.py index f0356966..4f115ec6 100644 --- a/src/cutecoin/gui/process_cfg_community.py +++ b/src/cutecoin/gui/process_cfg_community.py @@ -184,7 +184,7 @@ Would you like to publish the key ?""".format(self.account.pubkey)) if self.password_asker.result() == QDialog.Rejected: return try: - self.account.send_pubkey(password, self.community) + self.account.send_selfcert(password, self.community) except ValueError as e: QMessageBox.critical(self, "Pubkey publishing error", e.message) diff --git a/src/cutecoin/gui/wallets_tab.py b/src/cutecoin/gui/wallets_tab.py index a688f162..b75b6917 100644 --- a/src/cutecoin/gui/wallets_tab.py +++ b/src/cutecoin/gui/wallets_tab.py @@ -33,7 +33,7 @@ class WalletsTabWidget(QWidget, Ui_WalletsTab): self.refresh() def refresh(self): - parameters = self.community.get_parameters() + parameters = self.community.parameters last_renewal = "" expiration = "" certifiers = 0 diff --git a/src/cutecoin/models/community.py b/src/cutecoin/models/community.py index 1987a5a4..81b8fbd0 100644 --- a/src/cutecoin/models/community.py +++ b/src/cutecoin/models/community.py @@ -9,7 +9,7 @@ class CommunityItemModel(object): def __init__(self, community, communities_item=None): self.communities_item = communities_item - self.community_text = community.name() + self.community_text = community.name self.main_node_items = [] def appendChild(self, item): diff --git a/src/cutecoin/models/members.py b/src/cutecoin/models/members.py index 72ce5482..fe48c7c2 100644 --- a/src/cutecoin/models/members.py +++ b/src/cutecoin/models/members.py @@ -36,7 +36,7 @@ class MembersFilterProxyModel(QSortFilterProxyModel): expiration_index = self.sourceModel().index(source_index.row(), expiration_col) expiration_data = self.sourceModel().data(expiration_index, Qt.DisplayRole) current_time = QDateTime().currentDateTime().toMSecsSinceEpoch() - sig_validity = self.community.get_parameters()['sigValidity'] + sig_validity = self.community.parameters['sigValidity'] warning_expiration_time = int(sig_validity / 3) #logging.debug("{0} > {1}".format(current_time, expiration_data)) will_expire_soon = (current_time > expiration_data*1000 - warning_expiration_time*1000) @@ -94,7 +94,7 @@ class MembersTableModel(QAbstractTableModel): person = Person.lookup(pubkey, self.community) join_block = person.membership(self.community)['blockNumber'] join_date = self.community.get_block(join_block).mediantime - parameters = self.community.get_parameters() + parameters = self.community.parameters expiration_date = join_date + parameters['sigValidity'] return (person.name, pubkey, join_date, expiration_date) diff --git a/src/cutecoin/models/network.py b/src/cutecoin/models/network.py index 946d1302..528e1632 100644 --- a/src/cutecoin/models/network.py +++ b/src/cutecoin/models/network.py @@ -131,7 +131,6 @@ class NetworkTableModel(QAbstractTableModel): Node.CORRUPTED: QColor(Qt.darkRed) } return colors[node.state] - #TODO: Display colors depending on node state def flags(self, index): return Qt.ItemIsSelectable | Qt.ItemIsEnabled -- GitLab