diff --git a/.travis.yml b/.travis.yml index a5d3fe0b4ef7f8812665202272ebac2a9c8ebd7f..0a357aacca72442cba8e3bf52595d4f5977f86b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,10 +25,13 @@ install: - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION cx_freeze pyqt5 libpng=1.5.13 libsodium=1.0.3 - source activate test-environment - ldd $HOME/miniconda/envs/test-environment/lib/qt5/plugins/platforms/*.so + - pip install coveralls - pip install pylibscrypt - pip install libnacl - pip install requests - pip install base58 + - pip install quamash + - pip install asyncio - python gen_resources.py - python gen_translations.py - python setup.py build @@ -45,4 +48,8 @@ script: - export QT_QPA_PLATFORM_PLUGIN_PATH=$HOME/miniconda/envs/test-environment/lib/qt5/plugins/platforms; - export QT_PLUGIN_PATH=$HOME/miniconda/envs/test-environment/lib/qt5/plugins - export QT_XKB_CONFIG_ROOT=/usr/share/X11/xkb - - python run_tests.py + - coverage run --source=cutecoin.core,cutecoin.gui,cutecoin.models run_tests.py + +after_success: + - coverage -rm + - coveralls diff --git a/README.md b/README.md index 631489fac59ba919841e221bbfe2056b0e742bbc..7b6fdffe71a6afd103207106c37b0cc508def006 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ -Landscape | [](https://landscape.io/github/ucoin-io/cutecoin/dev) +<!-- Landscape | [](https://landscape.io/github/ucoin-io/cutecoin/dev) --> + +Coveralls | [](https://coveralls.io/r/ucoin-io/cutecoin) Travis | [](https://travis-ci.org/ucoin-io/cutecoin) Appveyor | [](https://ci.appveyor.com/project/Insoleet/cutecoin/branch/dev) +Weblate | [](http://weblate.ucoin.io/engage/cutecoin/?utm_source=widget) +  cutecoin diff --git a/appveyor.yml b/appveyor.yml index def1f44150216195421cac86675e010b587e8859..e92970cf03e289905d3d52696a5cacc3a7c858e1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -52,6 +52,5 @@ deploy: draft: true prerelease: true on: - branch: master # release from master branch only appveyor_repo_tag: true # deploy on tag push only diff --git a/ci/appveyor/build.cmd b/ci/appveyor/build.cmd index a4b659a1654ecf67af7ce8221828f585d06cca46..91e23da46c5ed6971035621b17bdd0bfc563cafa 100644 --- a/ci/appveyor/build.cmd +++ b/ci/appveyor/build.cmd @@ -15,6 +15,8 @@ pip install pylibscrypt pip install libnacl pip install requests pip install base58 +pip install quamash +pip install asyncio python gen_resources.py if %errorlevel% neq 0 exit /b 1s diff --git a/doc/uml/api.png b/doc/uml/api.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e3336901352dc647ce916c06bc2eb880eae469 Binary files /dev/null and b/doc/uml/api.png differ diff --git a/doc/uml/api.pu b/doc/uml/api.pu new file mode 100644 index 0000000000000000000000000000000000000000..f4a6ca46ace9b1302ecc225a51a719b5e9698648 --- /dev/null +++ b/doc/uml/api.pu @@ -0,0 +1,21 @@ +@startuml + +package api { + package api.bma { + class BMADataAccess { + {static} _cache + {static} _request(req : Request, network) + {static} _post(req : Request, network) + {static} _broadcast(req : Request, network) + } + BMADataAccess ..> api.bma.API + } + package api.es { + class ESDataAccess { + } + ESDataAccess ..> api.es.API + } + +} + +@enduml \ No newline at end of file diff --git a/doc/uml/core-classes.png b/doc/uml/core-classes.png new file mode 100644 index 0000000000000000000000000000000000000000..000636eb518c8e441bb799c053286adc6ede60d0 Binary files /dev/null and b/doc/uml/core-classes.png differ diff --git a/doc/uml/core-classes.pu b/doc/uml/core-classes.pu new file mode 100644 index 0000000000000000000000000000000000000000..fc8d1e6d972241369d76c3d582a79dc3d6f28762 --- /dev/null +++ b/doc/uml/core-classes.pu @@ -0,0 +1,101 @@ +@startuml + +package core { + class App { + -- Signals -- + current_account_changed(str : account_name) + data_changed() + -- Slots -- + -- Properties -- + current_account + accounts + -- Methods -- + } + App --* Account : accounts + + class Account { + -- Signals -- + wallets_changed(int : nb_wallets) + community_added(int : index) + community_removed(int : index) + data_changed() + -- Slots -- + -- Properties -- + communities + wallets + -- Methods -- + } + Account "1" --* "*" Wallet + Account "1" --* "*" Community + + class Wallet { + -- Signals -- + money_received(Transfer) + money_sent(Transfer) + name_changed(str : new_name + data_changed() + -- Slots -- + -- Properties -- + transfers + -- Methods -- + } + Wallet "1" --* "*" Transfer + + class Transfer { + -- Signals -- + state_changed(int : new_state) + -- Slots -- + -- Properties -- + -- Methods -- + } + + class Community { + -- Signals -- + members_changed() + data_changed() + -- Slots -- + -- Properties -- + network + -- Methods -- + + } + App --> Identity + class Identity { + {static} _identities + {static} load(data : dict) + {static} lookup(search : str) + } + +} + + +package net { + class Network { + -- Signals -- + node_found(int : index) + node_removed(int : index) + block_found(int : block_number) + -- Slots -- + -- Properties -- + nodes + root_nodes + -- Methods -- + } + Community "1" --* "1" Network + Network "1" --* "*" Node + + class Node { + -- Signals -- + changed() + -- Slots -- + -- Properties -- + endpoints + pubkey + uid + block + state + -- Methods -- + } + +} +@enduml \ No newline at end of file diff --git a/doc/uml/cutecoin.png b/doc/uml/cutecoin.png new file mode 100644 index 0000000000000000000000000000000000000000..59780e445fce620ad5f355c3d3f8ae1a95c92025 Binary files /dev/null and b/doc/uml/cutecoin.png differ diff --git a/doc/uml/cutecoin.pu b/doc/uml/cutecoin.pu new file mode 100644 index 0000000000000000000000000000000000000000..d79f67dfb4614935631fd76663cdd922aac6b841 --- /dev/null +++ b/doc/uml/cutecoin.pu @@ -0,0 +1,36 @@ +@startuml + +!include core-classes.pu +!include gui-classes.pu +!include models-classes.pu +!include api.pu + +MainWindow "1" --> "1" App + +CertificationDialog --> Community +TransferDialog --> Community + +CurrencyTab "1" --> "1" Community + +CommunityTab -right-> IdentitiesFilterProxyModel +NetworkTab -right-> NetworkFilterProxyModel +WalletTab -right-> WalletsFilterProxyModel + +WalletsFilterProxyModel -up-> Wallet +NetworkFilterProxyModel -up-> Network +TxHistoryFilterProxyModel -up-> Transfer + +ConfigureAccountDialog --> CommunitiesListModel +ConfigureCommunityDialog --> RootNodesTableModel + +ConfigureAccountDialog --> Account +ConfigureCommunityDialog --> Community + +Account ..> BMADataAccess +Community ..> BMADataAccess +Wallet ..> BMADataAccess +Transfer ..> BMADataAccess +Identity ..> BMADataAccess +BMADataAccess .left.> Network + +@enduml \ No newline at end of file diff --git a/doc/uml/gui-classes.png b/doc/uml/gui-classes.png new file mode 100644 index 0000000000000000000000000000000000000000..7df50d51d4ae74fb4f99c2b47eac4b31cc633b15 Binary files /dev/null and b/doc/uml/gui-classes.png differ diff --git a/doc/uml/gui-classes.pu b/doc/uml/gui-classes.pu new file mode 100644 index 0000000000000000000000000000000000000000..f8a0bc964fc2dd6a5714d56dab6da971bdd5e2cd --- /dev/null +++ b/doc/uml/gui-classes.pu @@ -0,0 +1,58 @@ +@startuml + + +package gui { + class MainWindow { + } + MainWindow "1" --* "*" CurrencyTab + + class CurrencyTab { + } + CurrencyTab "1" --* "1" CommunityTab + CurrencyTab "1" --* "1" WalletTab + CurrencyTab "1" --* "1" InformationsTab + CurrencyTab "1" --* "1" TransactionsTab + + class CommunityTab { + } + + CommunityTab "1" --* "1" IdentitiesTab + CommunityTab "1" --* "1" WotTab + + class WalletTab { + } + + class InformationsTab { + } + + class TransactionsTab { + } + + class NetworkTab { + } + + CurrencyTab "1" --* "1" NetworkTab + + class IdentitiesTab { + } + + class WotTab { + } + package dialogs { + class CertificationDialog + class TransferDialog + class ContactDialog + class ConfigureAccountDialog + class ConfigureCommunityDialog + } + + MainWindow --> CertificationDialog + MainWindow --> TransferDialog + MainWindow --> ContactDialog + MainWindow --> ConfigureAccountDialog + ConfigureAccountDialog --> ConfigureCommunityDialog + + class Wot + WotTab --> Wot +} +@enduml \ No newline at end of file diff --git a/doc/uml/models-classes.png b/doc/uml/models-classes.png new file mode 100644 index 0000000000000000000000000000000000000000..54fec65a2ab178127054ae952f4b55b7e0afbc52 Binary files /dev/null and b/doc/uml/models-classes.png differ diff --git a/doc/uml/models-classes.pu b/doc/uml/models-classes.pu new file mode 100644 index 0000000000000000000000000000000000000000..1650739d5d4c65bfa48ba299c543b4d019cde42b --- /dev/null +++ b/doc/uml/models-classes.pu @@ -0,0 +1,39 @@ +@startuml + +package models { + class WalletsFilterProxyModel { + } + + WalletsFilterProxyModel --> WalletsTableModel : source + + class WalletsTableModel { + } + + class IdentitiesFilterProxyModel { + } + IdentitiesFilterProxyModel --> IdentitiesTableModel : source + + class IdentitiesTableModel { + } + + class NetworkFilterProxyModel { + } + NetworkFilterProxyModel --> NetworkTableModel : source + + class NetworkTableModel { + } + + class TxHistoryFilterProxyModel { + } + TxHistoryFilterProxyModel --> TxHistoryTableModel : source + class TxHistoryTableModel { + } + + class CommunitiesListModel { + } + + class RootNodesTableModel { + } +} + +@enduml \ No newline at end of file diff --git a/doc/uml/network.png b/doc/uml/network.png new file mode 100644 index 0000000000000000000000000000000000000000..853e39c21f10c5fabcb74d702194ce1822209727 Binary files /dev/null and b/doc/uml/network.png differ diff --git a/doc/uml/network.pu b/doc/uml/network.pu new file mode 100644 index 0000000000000000000000000000000000000000..d89859f85bb60f8291a322f6bab241ccc3cd0a02 --- /dev/null +++ b/doc/uml/network.pu @@ -0,0 +1,36 @@ +@startuml + +Network -->o Node : Connect to node_received() +Network -> Node : Starts network discovery +activate Node +Node -> QNetworkManager : HTTP GET peering/peers?leaves=true +create QNetworkReply +QNetworkManager -> QNetworkReply : Instantiate +Node <- QNetworkManager : QNetworkReply +Node -->o QNetworkReply : Connect to finished() +Network <- Node +deactivate Node +... Request is processed ... +Node <-- QNetworkReply : finished() +destroy QNetworkReply +alt "root" hash changed +loop "for all leaves changed" +activate Node +Node -> QNetworkManager : HTTP GET peering/peers/leaf=leaf_hash +create QNetworkReply +QNetworkManager -> QNetworkReply : Instantiate +Node <- QNetworkManager : QNetworkReply +Node -->o QNetworkReply : Connect to finished() +end +end +... Requests is processed ... +Node <-- QNetworkReply : finished() +destroy QNetworkReply +Network <-- Node : node_received() +ref over Network +New node is instanciated +if pubkey not known yet. +It starts it's own +network discovery +end ref +@enduml \ No newline at end of file diff --git a/doc/uml/requests.png b/doc/uml/requests.png new file mode 100644 index 0000000000000000000000000000000000000000..d629a6789802e9eadb3f092a198497c02b4ddcc2 Binary files /dev/null and b/doc/uml/requests.png differ diff --git a/doc/uml/requests.pu b/doc/uml/requests.pu new file mode 100644 index 0000000000000000000000000000000000000000..8ea91136f68aba9771cf9f438f89790f508b4341 --- /dev/null +++ b/doc/uml/requests.pu @@ -0,0 +1,50 @@ +@startuml + +QModel -->o "Core Component" : Connect to data_changed() +QModel -> "Core Component" : Data access +activate "Core Component" +"Core Component" -> Community : Request data +Community -> Cache : Request cache +ref over Cache +Data is obsolete +(new block mined +since last caching) +end ref +Cache -> QNetworkManager : HTTP GET +create QNetworkReply +QNetworkManager -> QNetworkReply : Instantiate +Cache <- QNetworkManager : QNetworkReply +create ReceiverSlot +Cache -> ReceiverSlot : Instantiate Slot +QNetworkReply o<-- ReceiverSlot : Connect to finished() +Community <- Cache : Cached data +"Core Component" <- Community : Cached data +"Core Component" -> "Core Component" : Compute data +QModel <- "Core Component" : Data +deactivate "Core Component" + +...Network request is processed... + +ReceiverSlot <-- QNetworkReply : finished() +activate ReceiverSlot +ReceiverSlot -> Cache : Update cache data +ReceiverSlot -> "Core Component" : emit data_changed() +deactivate ReceiverSlot +destroy ReceiverSlot +destroy QNetworkReply +||| +QModel <-- "Core Component" : data_changed() +QModel -> "Core Component" : Data access +activate "Core Component" +ref over "Core Component", Community +Community is requested again, +and last cached data are returned +No new block mined, so no HTTP GET +initialized between cache +and QNetworkManager +end ref +QModel <- "Core Component" : Data +deactivate "Core Component" + + +@enduml \ No newline at end of file diff --git a/doc/uml/tx_lifecycle.png b/doc/uml/tx_lifecycle.png index 0e7061b3438870f7c6a7c4fae11a191f971e9234..ff35f12c2605e32c5e03428e10e77b0ddfb9e1e1 100644 Binary files a/doc/uml/tx_lifecycle.png and b/doc/uml/tx_lifecycle.png differ diff --git a/lib/ucoinpy/__init__.py b/lib/ucoinpy/__init__.py index 354362cd909050638e202e6df9b96384bf8fc7c0..3b1345bc8e6afcb68399f864af138a40ab4cbe0f 100644 --- a/lib/ucoinpy/__init__.py +++ b/lib/ucoinpy/__init__.py @@ -19,3 +19,5 @@ PROTOCOL_VERSION="1" MANAGED_API=["BASIC_MERKLED_API"] + +from . import api, documents, key \ No newline at end of file diff --git a/lib/ucoinpy/documents/__init__.py b/lib/ucoinpy/documents/__init__.py index 198b62b474c770b7440a0ac72c8d2d8d14eb61f7..9763c366155f445dd55be9022f8d2cacf381036a 100644 --- a/lib/ucoinpy/documents/__init__.py +++ b/lib/ucoinpy/documents/__init__.py @@ -1,8 +1,8 @@ -''' +""" Created on 3 déc. 2014 @author: inso -''' +""" import base58 import base64 import re @@ -23,10 +23,10 @@ class Document: self.signatures = [] def sign(self, keys): - ''' + """ Sign the current document. Warning : current signatures will be replaced with the new ones. - ''' + """ self.signatures = [] for key in keys: signing = base64.b64encode(key.signature(bytes(self.raw(), 'ascii'))) @@ -34,10 +34,10 @@ class Document: self.signatures.append(signing.decode("ascii")) def signed_raw(self): - ''' + """ If keys are None, returns the raw + current signatures If keys are present, returns the raw signed by these keys - ''' + """ raw = self.raw() signed = "\n".join(self.signatures) signed_raw = raw + signed + "\n" diff --git a/lib/ucoinpy/documents/block.py b/lib/ucoinpy/documents/block.py index 58a1b3bf8e9d29543fbd65b956b9bc144d0b2f04..a02a550dac8f71302bf3140503ceae7c98a40bd6 100644 --- a/lib/ucoinpy/documents/block.py +++ b/lib/ucoinpy/documents/block.py @@ -1,8 +1,8 @@ -''' +""" Created on 2 déc. 2014 @author: inso -''' +""" from .. import PROTOCOL_VERSION from . import Document @@ -15,7 +15,7 @@ import logging class Block(Document): - ''' + """ Version: VERSION Type: Block Currency: CURRENCY @@ -52,7 +52,7 @@ Transactions: COMPACT_TRANSACTION ... BOTTOM_SIGNATURE - ''' + """ re_type = re.compile("Type: (Block)\n") re_noonce = re.compile("Nonce: ([0-9]+)\n") @@ -82,9 +82,9 @@ BOTTOM_SIGNATURE parameters, members_count, identities, joiners, actives, leavers, excluded, certifications, transactions, signature): - ''' + """ Constructor - ''' + """ super().__init__(version, currency, [signature]) self.noonce = noonce self.number = number @@ -293,4 +293,4 @@ PreviousIssuer: {1}\n".format(self.prev_hash, self.prev_issuer) for transaction in self.transactions: doc += "{0}\n".format(transaction.compact()) - return doc + return doc \ No newline at end of file diff --git a/lib/ucoinpy/documents/certification.py b/lib/ucoinpy/documents/certification.py index 3792839e593b9f46c2078b91fe867bad586192ac..d7e79adb550dcc1ce15c10dc20fa7e0629c40758 100644 --- a/lib/ucoinpy/documents/certification.py +++ b/lib/ucoinpy/documents/certification.py @@ -1,8 +1,8 @@ -''' +""" Created on 2 déc. 2014 @author: inso -''' +""" import re import base64 import logging @@ -11,9 +11,9 @@ from . import Document class SelfCertification(Document): - ''' + """ A document discribing a self certification. - ''' + """ re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):([A-Za-z0-9+/]+(?:=|==)?):([0-9]+):([^\n]+)\n") re_uid = re.compile("UID:([^\n]+)\n") @@ -48,9 +48,9 @@ META:TS:{1} class Certification(Document): - ''' + """ A document describing a certification. - ''' + """ re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):\ ([1-9A-Za-z][^OIl]{42,45}):([0-9]+):([A-Za-z0-9+/]+(?:=|==)?)\n") @@ -58,9 +58,9 @@ class Certification(Document): def __init__(self, version, currency, pubkey_from, pubkey_to, blocknumber, blockhash, signature): - ''' + """ Constructor - ''' + """ super().__init__(version, currency, [signature]) self.pubkey_from = pubkey_from self.pubkey_to = pubkey_to @@ -85,10 +85,10 @@ class Certification(Document): def sign(self, selfcert, keys): - ''' + """ Sign the current document. Warning : current signatures will be replaced with the new ones. - ''' + """ self.signatures = [] for key in keys: signing = base64.b64encode(key.signature(bytes(self.raw(selfcert), 'ascii'))) @@ -107,13 +107,13 @@ class Certification(Document): class Revocation(Document): - ''' + """ A document describing a self-revocation. - ''' + """ def __init__(self, version, currency, signature): - ''' + """ Constructor - ''' + """ super().__init__(version, currency, [signature]) def raw(self, selfcert): @@ -121,10 +121,10 @@ class Revocation(Document): """.format(selfcert.signed_raw()) def sign(self, selfcert, keys): - ''' + """ Sign the current document. Warning : current signatures will be replaced with the new ones. - ''' + """ self.signatures = [] for key in keys: signing = base64.b64encode(key.signature(bytes(self.raw(selfcert), 'ascii'))) diff --git a/lib/ucoinpy/documents/membership.py b/lib/ucoinpy/documents/membership.py index af71a3a0e07c381decd50e6207a5513ac63f935a..570110e29772f1ed473f02a5c788cc4505cbe2ca 100644 --- a/lib/ucoinpy/documents/membership.py +++ b/lib/ucoinpy/documents/membership.py @@ -1,8 +1,8 @@ -''' +""" Created on 2 déc. 2014 @author: inso -''' +""" from .. import PROTOCOL_VERSION from . import Document @@ -10,7 +10,7 @@ import re class Membership(Document): - ''' + """ This is a utility class to generate membership documents : Version: VERSION Type: Membership @@ -20,7 +20,7 @@ class Membership(Document): Membership: MEMBERSHIP_TYPE UserID: USER_ID CertTS: CERTIFICATION_TS - ''' + """ # PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):([A-Za-z0-9+/]+(?:=|==)?):\ @@ -36,9 +36,9 @@ class Membership(Document): def __init__(self, version, currency, issuer, block_number, block_hash, membership_type, uid, cert_ts, signature): - ''' + """ Constructor - ''' + """ super().__init__(version, currency, [signature]) self.issuer = issuer self.block_number = block_number diff --git a/lib/ucoinpy/documents/peer.py b/lib/ucoinpy/documents/peer.py index 6b437a6d3f72f05fcf65292075fac474dc273402..6ca7606c6429b7521318720b0336649676a70dd6 100644 --- a/lib/ucoinpy/documents/peer.py +++ b/lib/ucoinpy/documents/peer.py @@ -1,8 +1,8 @@ -''' +""" Created on 2 déc. 2014 @author: inso -''' +""" import re diff --git a/lib/ucoinpy/documents/status.py b/lib/ucoinpy/documents/status.py index 2ba47ea89c585d9af302c7272a68943f103b0460..f11e7542c338f57831f6972f11a260a10d0d0254 100644 --- a/lib/ucoinpy/documents/status.py +++ b/lib/ucoinpy/documents/status.py @@ -1,15 +1,15 @@ -''' +""" Created on 2 déc. 2014 @author: inso -''' +""" import re from . import Document class Status(Document): - ''' + """ Version: VERSION Type: Status Currency: CURRENCY_NAME @@ -17,7 +17,7 @@ class Status(Document): Block: BLOCK From: SENDER To: RECIPIENT - ''' + """ re_type = re.compile("Type: (Status)") re_status = re.compile("Status: (NEW|NEW_BACK|UP|UP_BACK|DOWN)") @@ -27,9 +27,9 @@ class Status(Document): def __init__(self, version, currency, status, blockid, sender, recipient, signature): - ''' + """ Constructor - ''' + """ super().__init__(version, currency, [signature]) self.status = status @@ -70,12 +70,12 @@ class Status(Document): sender, recipient, signature) def raw(self): - return '''Version: {0} + return """Version: {0} Type: Status Currency: {1} Status: {2} Block: {3} From: {4} To: {5} -'''.format(self.version, self.currency, self.status, +""".format(self.version, self.currency, self.status, self.blockid, self.sender, self.recipient) diff --git a/lib/ucoinpy/documents/transaction.py b/lib/ucoinpy/documents/transaction.py index e84313f8c27a002eb5e4fd12fb69b7eafe1c9736..0a9d40d21b797cd1dfd7f35bcaac230c73170c6a 100644 --- a/lib/ucoinpy/documents/transaction.py +++ b/lib/ucoinpy/documents/transaction.py @@ -1,15 +1,15 @@ -''' +""" Created on 2 déc. 2014 @author: inso -''' +""" from . import Document import re import logging class Transaction(Document): - ''' + """ Document format : Version: VERSION Type: Transaction @@ -38,7 +38,7 @@ PUBLIC_KEY:AMOUNT COMMENT SIGNATURE ... - ''' + """ re_type = re.compile("Type: (Transaction)\n") re_header = re.compile("TX:([0-9]+):([0-9]+):([0-9]+):([0-9]+):(0|1)\n") @@ -51,9 +51,9 @@ SIGNATURE def __init__(self, version, currency, issuers, inputs, outputs, comment, signatures): - ''' + """ Constructor - ''' + """ super().__init__(version, currency, signatures) self.issuers = issuers @@ -181,9 +181,9 @@ Issuers: return doc def compact(self): - ''' + """ Return a transaction in its compact format. - ''' + """ """TX:VERSION:NB_ISSUERS:NB_INPUTS:NB_OUTPUTS:HAS_COMMENT PUBLIC_KEY:INDEX ... @@ -213,26 +213,26 @@ COMMENT class SimpleTransaction(Transaction): - ''' + """ As transaction class, but for only one issuer. ... - ''' + """ def __init__(self, version, currency, issuer, single_input, outputs, comment, signature): - ''' + """ Constructor - ''' + """ super().__init__(version, currency, [issuer], [single_input], outputs, comment, [signature]) class InputSource(): - ''' + """ A Transaction INPUT Compact : INDEX:SOURCE:FINGERPRINT:AMOUNT - ''' + """ re_inline = re.compile("([0-9]+):(D|T):([0-9]+):\ ([0-9a-fA-F]{5,40}):([0-9]+)\n") re_compact = re.compile("([0-9]+):(D|T):([0-9a-fA-F]{5,40}):([0-9]+)\n") @@ -278,9 +278,9 @@ class InputSource(): class OutputSource(): - ''' + """ A Transaction OUTPUT - ''' + """ re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):([0-9]+)") def __init__(self, pubkey, amount): diff --git a/lib/ucoinpy/key/__init__.py b/lib/ucoinpy/key/__init__.py index 191bf3786c47f93b4fa9daaf4f10f58874063482..f0813ef87b1962e027593dd312548c61d78c2f5d 100644 --- a/lib/ucoinpy/key/__init__.py +++ b/lib/ucoinpy/key/__init__.py @@ -1,8 +1,8 @@ -''' +""" Ucoin public and private keys @author: inso -''' +""" import base58 import base64 diff --git a/lib/ucoinpy/key/hdwallet.py b/lib/ucoinpy/key/hdwallet.py index dffb5fffa8515b8d8433e9af2b965bf16ae3b459..1a135d7da916fb4155d1fb8116859ec753d2e91d 100644 --- a/lib/ucoinpy/key/hdwallet.py +++ b/lib/ucoinpy/key/hdwallet.py @@ -1,9 +1,9 @@ -''' +""" HD Wallet inspired from Bip32 wallets. @author: inso -''' -''' +""" +""" import os import hmac import hashlib @@ -375,4 +375,4 @@ if __name__ == "__main__": print "* [Chain m/0/2147483647h/1/2147483646h/2]" m = m.ChildKey(2) m.dump() -''' \ No newline at end of file +""" \ No newline at end of file diff --git a/res/i18n/ts/de_DE.ts b/res/i18n/ts/de_DE.ts index bed787b6f79fe7dfccbddd85c300f2bf7ca23d4f..fcd8312d6f990213d2d381570a6ba29bfaddc24d 100644 --- a/res/i18n/ts/de_DE.ts +++ b/res/i18n/ts/de_DE.ts @@ -16,37 +16,37 @@ <context> <name>Account</name> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"/> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Units</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"/> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>UD {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"/> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>UD</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"/> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Q0 {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"/> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Quant Z-sum</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"/> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>R0 {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"/> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Relat Z-sum</source> <translation type="unfinished"></translation> </message> @@ -132,7 +132,7 @@ <context> <name>CertificationDialog</name> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="58"/> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> <source>Certification</source> <translation type="unfinished"></translation> </message> @@ -162,35 +162,30 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="49"/> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> <source>Success certifying {0} from {1}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="53"/> - <source>Something wrong happened : {0}</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="58"/> - <source>Couldn't connect to network : {0}</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="63"/> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="76"/> + <location filename="../../../src/cutecoin/gui/certification.py" line="77"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="79"/> + <location filename="../../../src/cutecoin/gui/certification.py" line="80"/> <source>Not a member</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>CommunityConfigurationDialog</name> @@ -210,30 +205,35 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="100"/> + <location filename="../../ui/community_cfg.ui" line="124"/> <source>Communities nodes</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="118"/> + <location filename="../../ui/community_cfg.ui" line="142"/> <source>Server</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="138"/> + <location filename="../../ui/community_cfg.ui" line="162"/> <source>Add</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="159"/> + <location filename="../../ui/community_cfg.ui" line="183"/> <source>Previous</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="182"/> + <location filename="../../ui/community_cfg.ui" line="206"/> <source>Next</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../ui/community_cfg.ui" line="98"/> + <source>Check node connectivity</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>CommunityTabWidget</name> @@ -273,7 +273,7 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="334"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="351"/> <source>Renew membership</source> <translation type="unfinished"></translation> </message> @@ -283,153 +283,127 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="57"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="59"/> <source>Web of Trust</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="58"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="60"/> <source>Members</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="61"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="63"/> <source>Direct connections</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="79"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="104"/> <source>Informations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="83"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="107"/> <source>Add as contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="87"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="111"/> <source>Send money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="91"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="115"/> <source>Certify identity</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="95"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="119"/> <source>View in Web of Trust</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="202"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> <source>Membership</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="172"/> - <source>Success sending membership demand</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="174"/> - <source>Join demand error</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="177"/> - <source>Key not sent to community</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="177"/> - <source>"Your key wasn't sent in the community. -You can't request a membership.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="259"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> <source>Network error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="259"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> <source>Couldn't connect to network : {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="242"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="190"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="196"/> <source>Are you sure ? Sending a leaving demand cannot be canceled. The process to join back the community later will have to be done again.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="202"/> - <source>Success sending leaving demand</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="204"/> - <source>Leaving demand error</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="263"/> - <source>Error</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="216"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="209"/> <source>Are you sure ? Publishing your UID can be canceled by Revoke UID.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="227"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> <source>UID Publishing</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="227"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> <source>Success publishing your UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="230"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="223"/> <source>Publish UID error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="242"/> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> <source>Are you sure ? Revoking your UID can only success if it is not already validated by the network.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="253"/> - <source>UID Revoking</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="358"/> + <source>Send membership demand</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="253"/> - <source>Success revoking your UID</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> + <source>Success sending Membership demand</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="256"/> - <source>Revoke UID error</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Revoke</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="341"/> - <source>Send membership demand</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Success sending Revoke demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Self Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Success sending Self Certification document</source> <translation type="unfinished"></translation> </message> </context> @@ -497,65 +471,55 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="77"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="72"/> <source>Wallets</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="85"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="76"/> <source>Transactions</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="94"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="84"/> <source>Community</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="100"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="80"/> <source>Informations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="112"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="88"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="145"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> <source>Membership expiration</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="145"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> <source><b>Warning : Membership expiration in {0} days</b></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="150"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> <source>Certifications number</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="150"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> <source><b>Warning : You are certified by only {0} persons, need {1}</b></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="183"/> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="161"/> <source> Block {0}</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="206"/> - <source>Received {0} {1} from {2} transfers</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="210"/> - <source>New transactions received</source> - <translation type="unfinished"></translation> - </message> </context> <context> <name>DialogMember</name> @@ -583,27 +547,27 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>HistoryTableModel</name> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"/> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Date</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"/> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>UID/Public key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"/> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Payment</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"/> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Deposit</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"/> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Comment</source> <translation type="unfinished"></translation> </message> @@ -653,22 +617,22 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>IdentitiesTableModel</name> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="85"/> + <location filename="../../../src/cutecoin/models/identities.py" line="84"/> <source>UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="86"/> + <location filename="../../../src/cutecoin/models/identities.py" line="85"/> <source>Pubkey</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="87"/> + <location filename="../../../src/cutecoin/models/identities.py" line="86"/> <source>Renewed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="88"/> + <location filename="../../../src/cutecoin/models/identities.py" line="87"/> <source>Expiration</source> <translation type="unfinished"></translation> </message> @@ -779,56 +743,27 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> - <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - </table> - </source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Universal Dividend UD(t) in</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> - <source>Monetary Mass M(t) in</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Members N(t)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> - <source>Monetary Mass per member M(t)/N(t) in</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> - <source>Actual growth c = UD(t)/[M(t-1)/N(t-1)]</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Next UD date and time (t+1)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="165"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="202"/> <source>No Universal Dividend created yet.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source> <table cellpadding="5"> <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> @@ -839,37 +774,32 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>{:2.0%} / {:} days</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>Fundamental growth (c) / Delta time (dt)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> - <source>UD(t+1) = MAX { UD(t) ; c &#215; M(t) / N(t) }</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>Universal Dividend (formula)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>{:} = MAX {{ {:} {:} ; {:2.0%} &#215; {:} {:} / {:} }}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>Universal Dividend (computed)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source> <table cellpadding="5"> <tr><td align="right"><b>{:2.0%} / {:} days</b></td><td>{:}</td></tr> @@ -885,47 +815,47 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Fundamental growth (c)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Initial Universal Dividend UD(0) in</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Time period (dt) in days (86400 seconds) between two UD</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Number of blocks used for calculating median time</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The average time in seconds for writing 1 block (wished time)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The number of blocks required to evaluate again PoWMin value</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The number of previous blocks to check for personalized difficulty</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The percent of previous issuers to reach for personalized difficulty</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source> <table cellpadding="5"> <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> @@ -939,35 +869,75 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Minimum delay between 2 identical certifications (in days)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Maximum age of a valid signature (in days)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Minimum quantity of signatures to be part of the WoT</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Minimum quantity of valid made certifications to be part of the WoT for distance rule</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Maximum age of a valid membership (in days)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"/> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Maximum distance between each WoT member and a newcomer</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass M(t-1) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass per member M(t-1)/N(t) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Actual growth c = UD(t)/[M(t-1)/N(t)]</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Last UD date and time (t)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>UD(t+1) = MAX { UD(t) ; c &#215; M(t) / N(t+1) }</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>MainWindow</name> @@ -1082,22 +1052,17 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="176"/> - <source>Loading account {0}</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="242"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="190"/> <source>Latest release : {version}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="249"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="197"/> <source>Download link</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="256"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="204"/> <source> <h1>Cutecoin</h1> @@ -1117,42 +1082,42 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="281"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="229"/> <source>Please get the latest release {version}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="324"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="272"/> <source>Edit</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="327"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="275"/> <source>Delete</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="343"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="291"/> <source>CuteCoin {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="368"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="316"/> <source>CuteCoin {0} - Account : {1}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="388"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="336"/> <source>Export an account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="389"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="337"/> <source>All account files (*.acc)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="390"/> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="338"/> <source>Export</source> <translation type="unfinished"></translation> </message> @@ -1202,57 +1167,57 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>NetworkFilterProxyModel</name> <message> - <location filename="../../../src/cutecoin/models/network.py" line="39"/> + <location filename="../../../src/cutecoin/models/network.py" line="41"/> <source>Address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="40"/> + <location filename="../../../src/cutecoin/models/network.py" line="42"/> <source>Port</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="41"/> + <location filename="../../../src/cutecoin/models/network.py" line="43"/> <source>Block</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="42"/> + <location filename="../../../src/cutecoin/models/network.py" line="44"/> <source>UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="43"/> + <location filename="../../../src/cutecoin/models/network.py" line="45"/> <source>Member</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="44"/> + <location filename="../../../src/cutecoin/models/network.py" line="46"/> <source>Pubkey</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="45"/> + <location filename="../../../src/cutecoin/models/network.py" line="47"/> <source>Software</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="46"/> + <location filename="../../../src/cutecoin/models/network.py" line="48"/> <source>Version</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="59"/> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> <source>yes</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="59"/> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> <source>no</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="59"/> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> <source>offline</source> <translation type="unfinished"></translation> </message> @@ -1265,35 +1230,40 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/network_tab.py" line="51"/> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="61"/> <source>Unset root node</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/network_tab.py" line="57"/> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="67"/> <source>Set as root node</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="73"/> + <source>Open in browser</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>NetworkTableModel</name> <message> - <location filename="../../../src/cutecoin/models/network.py" line="112"/> + <location filename="../../../src/cutecoin/models/network.py" line="114"/> <source>Online</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="113"/> + <location filename="../../../src/cutecoin/models/network.py" line="115"/> <source>Offline</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="114"/> + <location filename="../../../src/cutecoin/models/network.py" line="116"/> <source>Unsynchronized</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="115"/> + <location filename="../../../src/cutecoin/models/network.py" line="117"/> <source>Corrupted</source> <translation type="unfinished"></translation> </message> @@ -1339,28 +1309,48 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>PreferencesDialog</name> <message> - <location filename="../../../src/cutecoin/gui/preferences.py" line="45"/> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> <source>Preferences</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/preferences.ui" line="22"/> + <location filename="../../ui/preferences.ui" line="108"/> <source>Default account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/preferences.ui" line="36"/> - <source>Default referential</source> + <location filename="../../ui/preferences.ui" line="205"/> + <source>Language</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/preferences.ui" line="50"/> - <source>Language</source> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> + <source>A restart is needed to apply your new preferences.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/preferences.py" line="45"/> - <source>A restart is needed to apply your new preferences.</source> + <location filename="../../ui/preferences.ui" line="122"/> + <source>Default &referential</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="159"/> + <source>Enable expert mode</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="191"/> + <source>Digits after commas </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="239"/> + <source>Maximize Window at Startup</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="266"/> + <source>Enable notifications</source> <translation type="unfinished"></translation> </message> </context> @@ -1377,32 +1367,32 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="186"/> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="187"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="207"/> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> <source>Public key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="207"/> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> <source>These parameters pubkeys are : {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="256"/> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="248"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="233"/> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="233"/> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> <source>This action will delete your account locally. Please note your key parameters (salt and password) if you wish to recover it later. Your account won't be removed from the networks it joined. @@ -1413,12 +1403,12 @@ Are you sure ?</source> <context> <name>ProcessConfigureCommunity</name> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="117"/> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="126"/> <source>Configure community {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="120"/> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="129"/> <source>Add a community</source> <translation type="unfinished"></translation> </message> @@ -1428,17 +1418,17 @@ Are you sure ?</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="187"/> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="192"/> <source>Delete</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="198"/> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> <source>Pubkey not found</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="198"/> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> <source>The public key of your account wasn't found in the community. : {0} @@ -1447,18 +1437,18 @@ Would you like to publish the key ?</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="209"/> - <source>Pubkey publishing error</source> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>UID Publishing</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="212"/> - <source>Network error</source> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>Success publishing your UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="212"/> - <source>Couldn't connect to network : {0}</source> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> + <source>{0} : {1}</source> <translation type="unfinished"></translation> </message> </context> @@ -1473,71 +1463,81 @@ Would you like to publish the key ?</source> <context> <name>TransactionsTabWidget</name> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="104"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="123"/> <source><b>Deposits</b> {:} {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="108"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="127"/> <source><b>Payments</b> {:} {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="112"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="131"/> <source><b>Balance</b> {:} {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="121"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="140"/> <source>Actions</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="134"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="153"/> <source>Send again</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="139"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="158"/> <source>Cancel</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="145"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="164"/> <source>Informations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="150"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="169"/> <source>Add as contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="155"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="174"/> <source>Send money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="161"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="180"/> <source>View in Web of Trust</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="166"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="185"/> <source>Copy pubkey to clipboard</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="203"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="203"/> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> <source>Are you sure ? This money transfer will be removed and not sent.</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="93"/> + <source>Received {0} {1} from {2} transfers</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="97"/> + <source>New transactions received</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>TransferMoneyDialog</name> @@ -1597,39 +1597,33 @@ This money transfer will be removed and not sent.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="92"/> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> <source>Money transfer</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="64"/> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> <source>No amount. Please give the transfert amount</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="78"/> - <source>Success transfering {0} {1} to {2}</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="83"/> - <source>Something wrong happened : {0}</source> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>Error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="88"/> - <source>This transaction could not be sent on this block -Please try again later</source> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Transfer</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="92"/> - <source>Couldn't connect to network : {0}</source> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Success sending money to {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="97"/> - <source>Error</source> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>{0} : {1}</source> <translation type="unfinished"></translation> </message> </context> @@ -1664,7 +1658,7 @@ Please try again later</source> <context> <name>WalletsTabWidget</name> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="70"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> <source> <table cellpadding="5"> <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> @@ -1675,27 +1669,27 @@ Please try again later</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="70"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> <source>Membership</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="70"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> <source>Last renewal on {:}, expiration on {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source>Your web of trust</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source>Certified by {:} members; Certifier of {:} members</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source> <table cellpadding="5"> <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> @@ -1706,12 +1700,12 @@ Please try again later</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source>Not a member</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source> <table cellpadding="5"> <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> @@ -1721,42 +1715,42 @@ Please try again later</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>Your money share </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>{:.2f}%</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>Your part </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>{:} {:} in [{:} ; {:}] {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="173"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="181"/> <source>New Wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="176"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="184"/> <source>Rename</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="180"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="188"/> <source>Copy pubkey to clipboard</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="185"/> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="193"/> <source>Transfer to...</source> <translation type="unfinished"></translation> </message> @@ -1764,17 +1758,17 @@ Please try again later</source> <context> <name>WalletsTableModel</name> <message> - <location filename="../../../src/cutecoin/models/wallets.py" line="70"/> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> <source>Name</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/wallets.py" line="70"/> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> <source>Amount</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/wallets.py" line="70"/> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> <source>Pubkey</source> <translation type="unfinished"></translation> </message> @@ -1818,7 +1812,7 @@ Please try again later</source> <context> <name>self.config_dialog</name> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="89"/> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="92"/> <source>Ok</source> <translation type="unfinished"></translation> </message> diff --git a/res/i18n/ts/es_ES.ts b/res/i18n/ts/es_ES.ts index 8318eb6ab294aa5afbbd7ddf38e89c4215f76957..7f8fc217f784def6cd2f4eafb1d32fbbafe58c9d 100644 --- a/res/i18n/ts/es_ES.ts +++ b/res/i18n/ts/es_ES.ts @@ -1,15 +1,14 @@ -<?xml version='1.0' encoding='utf-8'?> -<!DOCTYPE TS> -<TS version="2.0" language="es_ES" sourcelanguage=""> +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="2.0" language="es_ES" sourcelanguage=""> <context> <name>AboutPopup</name> <message> - <location filename="../../ui/about.ui" line="14"></location> + <location filename="../../ui/about.ui" line="14"/> <source>About</source> <translation>Sobre</translation> </message> <message> - <location filename="../../ui/about.ui" line="22"></location> + <location filename="../../ui/about.ui" line="22"/> <source>label</source> <translation type="unfinished"></translation> </message> @@ -17,37 +16,37 @@ <context> <name>Account</name> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Units</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>UD {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>UD</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Q0 {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Quant Z-sum</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>R0 {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Relat Z-sum</source> <translation type="unfinished"></translation> </message> @@ -55,77 +54,77 @@ <context> <name>AccountConfigurationDialog</name> <message> - <location filename="../../ui/account_cfg.ui" line="14"></location> + <location filename="../../ui/account_cfg.ui" line="14"/> <source>Add an account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="30"></location> + <location filename="../../ui/account_cfg.ui" line="30"/> <source>Account parameters</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="51"></location> + <location filename="../../ui/account_cfg.ui" line="51"/> <source>Account name (uid)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="68"></location> + <location filename="../../ui/account_cfg.ui" line="68"/> <source>Wallets</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="111"></location> + <location filename="../../ui/account_cfg.ui" line="111"/> <source>Delete account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="140"></location> + <location filename="../../ui/account_cfg.ui" line="140"/> <source>Key parameters</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="170"></location> + <location filename="../../ui/account_cfg.ui" line="170"/> <source>CryptoID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="180"></location> + <location filename="../../ui/account_cfg.ui" line="180"/> <source>Your password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="193"></location> + <location filename="../../ui/account_cfg.ui" line="193"/> <source>Please repeat your password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="212"></location> + <location filename="../../ui/account_cfg.ui" line="212"/> <source>Show public key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="242"></location> + <location filename="../../ui/account_cfg.ui" line="242"/> <source>Communities membership</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="257"></location> + <location filename="../../ui/account_cfg.ui" line="257"/> <source>Add a community</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="264"></location> + <location filename="../../ui/account_cfg.ui" line="264"/> <source>Remove selected community</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="288"></location> + <location filename="../../ui/account_cfg.ui" line="288"/> <source>Previous</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="308"></location> + <location filename="../../ui/account_cfg.ui" line="308"/> <source>Next</source> <translation type="unfinished"></translation> </message> @@ -133,326 +132,300 @@ <context> <name>CertificationDialog</name> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="58"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> <source>Certification</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/certification.ui" line="20"></location> + <location filename="../../ui/certification.ui" line="20"/> <source>Community</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/certification.ui" line="32"></location> + <location filename="../../ui/certification.ui" line="32"/> <source>Certify user</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/certification.ui" line="40"></location> + <location filename="../../ui/certification.ui" line="40"/> <source>Contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/certification.ui" line="61"></location> + <location filename="../../ui/certification.ui" line="61"/> <source>User public key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/certification.ui" line="80"></location> + <location filename="../../ui/certification.ui" line="80"/> <source>Key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="49"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> <source>Success certifying {0} from {1}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="53"></location> - <source>Something wrong happened : {0}</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="58"></location> - <source>Couldn't connect to network : {0}</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="63"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="76"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="77"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="79"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="80"/> <source>Not a member</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>CommunityConfigurationDialog</name> <message> - <location filename="../../ui/community_cfg.ui" line="17"></location> + <location filename="../../ui/community_cfg.ui" line="17"/> <source>Add a community</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="46"></location> + <location filename="../../ui/community_cfg.ui" line="46"/> <source>Please enter the address of a node :</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="61"></location> + <location filename="../../ui/community_cfg.ui" line="61"/> <source>:</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="100"></location> + <location filename="../../ui/community_cfg.ui" line="124"/> <source>Communities nodes</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="118"></location> + <location filename="../../ui/community_cfg.ui" line="142"/> <source>Server</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="138"></location> + <location filename="../../ui/community_cfg.ui" line="162"/> <source>Add</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="159"></location> + <location filename="../../ui/community_cfg.ui" line="183"/> <source>Previous</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="182"></location> + <location filename="../../ui/community_cfg.ui" line="206"/> <source>Next</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../ui/community_cfg.ui" line="98"/> + <source>Check node connectivity</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>CommunityTabWidget</name> <message> - <location filename="../../ui/community_tab.ui" line="17"></location> + <location filename="../../ui/community_tab.ui" line="17"/> <source>communityTabWidget</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="40"></location> + <location filename="../../ui/community_tab.ui" line="40"/> <source>Identities</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="53"></location> + <location filename="../../ui/community_tab.ui" line="53"/> <source>Research a pubkey, an uid...</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="60"></location> + <location filename="../../ui/community_tab.ui" line="60"/> <source>Search</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="118"></location> + <location filename="../../ui/community_tab.ui" line="118"/> <source>Quality : </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="125"></location> + <location filename="../../ui/community_tab.ui" line="125"/> <source>Publish UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="132"></location> + <location filename="../../ui/community_tab.ui" line="132"/> <source>Revoke UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="334"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="351"/> <source>Renew membership</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="146"></location> + <location filename="../../ui/community_tab.ui" line="146"/> <source>Send leaving demand</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="57"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="59"/> <source>Web of Trust</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="58"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="60"/> <source>Members</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="61"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="63"/> <source>Direct connections</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="79"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="104"/> <source>Informations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="83"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="107"/> <source>Add as contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="87"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="111"/> <source>Send money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="91"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="115"/> <source>Certify identity</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="95"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="119"/> <source>View in Web of Trust</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="202"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> <source>Membership</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="172"></location> - <source>Success sending membership demand</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="174"></location> - <source>Join demand error</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="177"></location> - <source>Key not sent to community</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="177"></location> - <source>"Your key wasn't sent in the community. -You can't request a membership.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="259"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> <source>Network error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="259"></location> - <source>Couldn't connect to network : {0}</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> + <source>Couldn't connect to network : {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="242"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="190"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="196"/> <source>Are you sure ? Sending a leaving demand cannot be canceled. The process to join back the community later will have to be done again.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="202"></location> - <source>Success sending leaving demand</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="204"></location> - <source>Leaving demand error</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="263"></location> - <source>Error</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="216"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="209"/> <source>Are you sure ? Publishing your UID can be canceled by Revoke UID.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="227"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> <source>UID Publishing</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="227"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> <source>Success publishing your UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="230"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="223"/> <source>Publish UID error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="242"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> <source>Are you sure ? Revoking your UID can only success if it is not already validated by the network.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="253"></location> - <source>UID Revoking</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="358"/> + <source>Send membership demand</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="253"></location> - <source>Success revoking your UID</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> + <source>Success sending Membership demand</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="256"></location> - <source>Revoke UID error</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Revoke</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="341"></location> - <source>Send membership demand</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Success sending Revoke demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Self Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Success sending Self Certification document</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ConfigureContactDialog</name> <message> - <location filename="../../ui/contact.ui" line="14"></location> + <location filename="../../ui/contact.ui" line="14"/> <source>Add a contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/contact.ui" line="22"></location> + <location filename="../../ui/contact.ui" line="22"/> <source>Name</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/contact.ui" line="36"></location> + <location filename="../../ui/contact.ui" line="36"/> <source>Pubkey</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/contact.py" line="58"></location> + <location filename="../../../src/cutecoin/gui/contact.py" line="58"/> <source>Contact already exists</source> <translation type="unfinished"></translation> </message> @@ -460,22 +433,22 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>CreateWalletDialog</name> <message> - <location filename="../../ui/create_wallet.ui" line="14"></location> + <location filename="../../ui/create_wallet.ui" line="14"/> <source>Create a new wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/create_wallet.ui" line="45"></location> + <location filename="../../ui/create_wallet.ui" line="45"/> <source>Wallet name :</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/create_wallet.ui" line="83"></location> + <location filename="../../ui/create_wallet.ui" line="83"/> <source>Previous</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/create_wallet.ui" line="103"></location> + <location filename="../../ui/create_wallet.ui" line="103"/> <source>Next</source> <translation type="unfinished"></translation> </message> @@ -483,100 +456,90 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>CurrencyTabWidget</name> <message> - <location filename="../../ui/currency_tab.ui" line="14"></location> + <location filename="../../ui/currency_tab.ui" line="14"/> <source>Form</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="43"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="43"/> <source>Warning : Your membership is expiring soon.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="45"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="45"/> <source>Warning : Your could miss certifications soon.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="77"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="72"/> <source>Wallets</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="85"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="76"/> <source>Transactions</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="94"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="84"/> <source>Community</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="100"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="80"/> <source>Informations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="112"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="88"/> <source>Network</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="145"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> <source>Membership expiration</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="145"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> <source><b>Warning : Membership expiration in {0} days</b></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="150"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> <source>Certifications number</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="150"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> <source><b>Warning : You are certified by only {0} persons, need {1}</b></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="183"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="161"/> <source> Block {0}</source> <translation type="unfinished"></translation> </message> - <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="206"></location> - <source>Received {0} {1} from {2} transfers</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="210"></location> - <source>New transactions received</source> - <translation type="unfinished"></translation> - </message> </context> <context> <name>DialogMember</name> <message> - <location filename="../../ui/member.ui" line="14"></location> + <location filename="../../ui/member.ui" line="14"/> <source>Informations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/member.ui" line="34"></location> + <location filename="../../ui/member.ui" line="34"/> <source>Member</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/member.ui" line="65"></location> + <location filename="../../ui/member.ui" line="65"/> <source>uid</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/member.ui" line="72"></location> + <location filename="../../ui/member.ui" line="72"/> <source>properties</source> <translation type="unfinished"></translation> </message> @@ -584,27 +547,27 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>HistoryTableModel</name> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Date</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>UID/Public key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Payment</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Deposit</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Comment</source> <translation type="unfinished"></translation> </message> @@ -612,37 +575,37 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>HomeScreenWidget</name> <message> - <location filename="../../ui/homescreen.ui" line="20"></location> + <location filename="../../ui/homescreen.ui" line="20"/> <source>Form</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/homescreen.ui" line="49"></location> + <location filename="../../ui/homescreen.ui" line="49"/> <source><html><head/><body><p><br/></p></body></html></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/homescreen.ui" line="67"></location> + <location filename="../../ui/homescreen.ui" line="67"/> <source>Create a new account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/homescreen.ui" line="100"></location> + <location filename="../../ui/homescreen.ui" line="100"/> <source>Import an existing account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/homescreen.ui" line="127"></location> + <location filename="../../ui/homescreen.ui" line="127"/> <source>Get to know more about ucoin</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/homescreen.py" line="35"></location> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="35"/> <source>Please get the latest release {version}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/homescreen.py" line="39"></location> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="39"/> <source> <h1>Welcome to Cutecoin {version}</h1> <h2>{version_info}</h2> @@ -654,22 +617,22 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>IdentitiesTableModel</name> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="85"></location> + <location filename="../../../src/cutecoin/models/identities.py" line="84"/> <source>UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="86"></location> + <location filename="../../../src/cutecoin/models/identities.py" line="85"/> <source>Pubkey</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="87"></location> + <location filename="../../../src/cutecoin/models/identities.py" line="86"/> <source>Renewed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="88"></location> + <location filename="../../../src/cutecoin/models/identities.py" line="87"/> <source>Expiration</source> <translation type="unfinished"></translation> </message> @@ -677,57 +640,57 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>ImportAccountDialog</name> <message> - <location filename="../../ui/import_account.ui" line="14"></location> + <location filename="../../ui/import_account.ui" line="14"/> <source>Import an account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/import_account.ui" line="25"></location> + <location filename="../../ui/import_account.ui" line="25"/> <source>Import a file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/import_account.ui" line="36"></location> + <location filename="../../ui/import_account.ui" line="36"/> <source>Name of the account :</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="34"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="34"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="38"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> <source>Account import</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="38"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> <source>Account imported succefully !</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="43"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> <source>Import an account file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="43"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> <source>All account files (*.acc)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="58"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="58"/> <source>Please enter a name</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="63"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="63"/> <source>Name already exists</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="67"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="67"/> <source>File is not an account format</source> <translation type="unfinished"></translation> </message> @@ -735,370 +698,371 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>InformationsTabWidget</name> <message> - <location filename="../../ui/informations_tab.ui" line="14"></location> + <location filename="../../ui/informations_tab.ui" line="14"/> <source>Form</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="52"></location> + <location filename="../../ui/informations_tab.ui" line="52"/> <source>General</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="61"></location> + <location filename="../../ui/informations_tab.ui" line="61"/> <source>label_general</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="77"></location> + <location filename="../../ui/informations_tab.ui" line="77"/> <source>Rules</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="83"></location> + <location filename="../../ui/informations_tab.ui" line="83"/> <source>label_rules</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="96"></location> + <location filename="../../ui/informations_tab.ui" line="96"/> <source>Money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="102"></location> + <location filename="../../ui/informations_tab.ui" line="102"/> <source>label_money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="115"></location> + <location filename="../../ui/informations_tab.ui" line="115"/> <source>WoT</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="121"></location> + <location filename="../../ui/informations_tab.ui" line="121"/> <source>label_wot</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> - <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - </table> - </source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Universal Dividend UD(t) in</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> - <source>Monetary Mass M(t) in</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Members N(t)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> - <source>Monetary Mass per member M(t)/N(t) in</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> - <source>Actual growth c = UD(t)/[M(t-1)/N(t-1)]</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Next UD date and time (t+1)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="165"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="202"/> <source>No Universal Dividend created yet.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>{:2.0%} / {:} days</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>Fundamental growth (c) / Delta time (dt)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> - <source>UD(t+1) = MAX { UD(t) ; c &#215; M(t) / N(t) }</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>Universal Dividend (formula)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>{:} = MAX {{ {:} {:} ; {:2.0%} &#215; {:} {:} / {:} }}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>Universal Dividend (computed)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:2.0%} / {:} days</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:2.0%}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:2.0%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:2.0%}</b></td><td>{:}</td></tr> </table> </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Fundamental growth (c)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Initial Universal Dividend UD(0) in</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Time period (dt) in days (86400 seconds) between two UD</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Number of blocks used for calculating median time</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The average time in seconds for writing 1 block (wished time)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The number of blocks required to evaluate again PoWMin value</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The number of previous blocks to check for personalized difficulty</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The percent of previous issuers to reach for personalized difficulty</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Minimum delay between 2 identical certifications (in days)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Maximum age of a valid signature (in days)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Minimum quantity of signatures to be part of the WoT</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Minimum quantity of valid made certifications to be part of the WoT for distance rule</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Maximum age of a valid membership (in days)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Maximum distance between each WoT member and a newcomer</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass M(t-1) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass per member M(t-1)/N(t) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Actual growth c = UD(t)/[M(t-1)/N(t)]</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Last UD date and time (t)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>UD(t+1) = MAX { UD(t) ; c &#215; M(t) / N(t+1) }</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>MainWindow</name> <message> - <location filename="../../ui/mainwindow.ui" line="41"></location> + <location filename="../../ui/mainwindow.ui" line="41"/> <source>Fi&le</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="142"></location> + <location filename="../../ui/mainwindow.ui" line="142"/> <source>Account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="55"></location> + <location filename="../../ui/mainwindow.ui" line="55"/> <source>&Contacts</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="61"></location> + <location filename="../../ui/mainwindow.ui" line="61"/> <source>&Open</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="76"></location> + <location filename="../../ui/mainwindow.ui" line="76"/> <source>&Help</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="87"></location> + <location filename="../../ui/mainwindow.ui" line="87"/> <source>Manage accounts</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="92"></location> + <location filename="../../ui/mainwindow.ui" line="92"/> <source>Configure trustable nodes</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="97"></location> + <location filename="../../ui/mainwindow.ui" line="97"/> <source>&Add a contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="117"></location> + <location filename="../../ui/mainwindow.ui" line="117"/> <source>Send a message</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="122"></location> + <location filename="../../ui/mainwindow.ui" line="122"/> <source>Send money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="127"></location> + <location filename="../../ui/mainwindow.ui" line="127"/> <source>Remove contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="132"></location> + <location filename="../../ui/mainwindow.ui" line="132"/> <source>Save</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="137"></location> + <location filename="../../ui/mainwindow.ui" line="137"/> <source>&Quit</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="147"></location> + <location filename="../../ui/mainwindow.ui" line="147"/> <source>&Transfer money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="152"></location> + <location filename="../../ui/mainwindow.ui" line="152"/> <source>&Configure</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="157"></location> + <location filename="../../ui/mainwindow.ui" line="157"/> <source>&Import</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="162"></location> + <location filename="../../ui/mainwindow.ui" line="162"/> <source>&Export</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="167"></location> + <location filename="../../ui/mainwindow.ui" line="167"/> <source>&Certification</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="172"></location> + <location filename="../../ui/mainwindow.ui" line="172"/> <source>&Set as default</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="177"></location> + <location filename="../../ui/mainwindow.ui" line="177"/> <source>A&bout</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="182"></location> + <location filename="../../ui/mainwindow.ui" line="182"/> <source>&Preferences</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="187"></location> + <location filename="../../ui/mainwindow.ui" line="187"/> <source>&Add account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="176"></location> - <source>Loading account {0}</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="242"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="190"/> <source>Latest release : {version}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="249"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="197"/> <source>Download link</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="256"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="204"/> <source> <h1>Cutecoin</h1> @@ -1118,42 +1082,42 @@ Revoking your UID can only success if it is not already validated by the network <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="281"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="229"/> <source>Please get the latest release {version}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="324"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="272"/> <source>Edit</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="327"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="275"/> <source>Delete</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="343"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="291"/> <source>CuteCoin {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="368"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="316"/> <source>CuteCoin {0} - Account : {1}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="388"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="336"/> <source>Export an account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="389"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="337"/> <source>All account files (*.acc)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="390"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="338"/> <source>Export</source> <translation type="unfinished"></translation> </message> @@ -1161,41 +1125,41 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>MemberDialog</name> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="31"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="31"/> <source>not a member</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="43"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="43"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="47"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> <source>Public key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="47"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> <source>Join date</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="65"></location> - <source><tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr></source> + <location filename="../../../src/cutecoin/gui/member.py" line="65"/> + <source><tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="56"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="56"/> <source>Distance</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="61"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="61"/> <source>Path</source> <translation type="unfinished"></translation> </message> @@ -1203,57 +1167,57 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>NetworkFilterProxyModel</name> <message> - <location filename="../../../src/cutecoin/models/network.py" line="39"></location> + <location filename="../../../src/cutecoin/models/network.py" line="41"/> <source>Address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="40"></location> + <location filename="../../../src/cutecoin/models/network.py" line="42"/> <source>Port</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="41"></location> + <location filename="../../../src/cutecoin/models/network.py" line="43"/> <source>Block</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="42"></location> + <location filename="../../../src/cutecoin/models/network.py" line="44"/> <source>UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="43"></location> + <location filename="../../../src/cutecoin/models/network.py" line="45"/> <source>Member</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="44"></location> + <location filename="../../../src/cutecoin/models/network.py" line="46"/> <source>Pubkey</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="45"></location> + <location filename="../../../src/cutecoin/models/network.py" line="47"/> <source>Software</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="46"></location> + <location filename="../../../src/cutecoin/models/network.py" line="48"/> <source>Version</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="59"></location> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> <source>yes</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="59"></location> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> <source>no</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="59"></location> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> <source>offline</source> <translation type="unfinished"></translation> </message> @@ -1261,40 +1225,45 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>NetworkTabWidget</name> <message> - <location filename="../../ui/network_tab.ui" line="14"></location> + <location filename="../../ui/network_tab.ui" line="14"/> <source>Form</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/network_tab.py" line="51"></location> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="61"/> <source>Unset root node</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/network_tab.py" line="57"></location> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="67"/> <source>Set as root node</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="73"/> + <source>Open in browser</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>NetworkTableModel</name> <message> - <location filename="../../../src/cutecoin/models/network.py" line="112"></location> + <location filename="../../../src/cutecoin/models/network.py" line="114"/> <source>Online</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="113"></location> + <location filename="../../../src/cutecoin/models/network.py" line="115"/> <source>Offline</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="114"></location> + <location filename="../../../src/cutecoin/models/network.py" line="116"/> <source>Unsynchronized</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="115"></location> + <location filename="../../../src/cutecoin/models/network.py" line="117"/> <source>Corrupted</source> <translation type="unfinished"></translation> </message> @@ -1302,37 +1271,37 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>PasswordAskerDialog</name> <message> - <location filename="../../ui/password_asker.ui" line="14"></location> + <location filename="../../ui/password_asker.ui" line="14"/> <source>Password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/password_asker.ui" line="23"></location> + <location filename="../../ui/password_asker.ui" line="23"/> <source>Please enter your account password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/password_asker.ui" line="32"></location> + <location filename="../../ui/password_asker.ui" line="32"/> <source>Remember my password during this session</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"></location> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> <source>Bad password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"></location> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> <source>Non printable characters in password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"></location> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> <source>Failed to get private key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"></location> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> <source>Wrong password typed. Cannot open the private key</source> <translation type="unfinished"></translation> </message> @@ -1340,73 +1309,93 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>PreferencesDialog</name> <message> - <location filename="../../../src/cutecoin/gui/preferences.py" line="45"></location> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> <source>Preferences</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/preferences.ui" line="22"></location> + <location filename="../../ui/preferences.ui" line="108"/> <source>Default account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/preferences.ui" line="36"></location> - <source>Default referential</source> + <location filename="../../ui/preferences.ui" line="205"/> + <source>Language</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/preferences.ui" line="50"></location> - <source>Language</source> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> + <source>A restart is needed to apply your new preferences.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/preferences.py" line="45"></location> - <source>A restart is needed to apply your new preferences.</source> + <location filename="../../ui/preferences.ui" line="122"/> + <source>Default &referential</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="159"/> + <source>Enable expert mode</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="191"/> + <source>Digits after commas </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="239"/> + <source>Maximize Window at Startup</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="266"/> + <source>Enable notifications</source> <translation type="unfinished"></translation> </message> </context> <context> <name>ProcessConfigureAccount</name> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="165"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="165"/> <source>New account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="172"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="172"/> <source>Configure {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="186"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="187"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="207"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> <source>Public key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="207"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> <source>These parameters pubkeys are : {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="256"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="248"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="233"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="233"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> <source>This action will delete your account locally. Please note your key parameters (salt and password) if you wish to recover it later. -Your account won't be removed from the networks it joined. +Your account won't be removed from the networks it joined. Are you sure ?</source> <translation type="unfinished"></translation> </message> @@ -1414,33 +1403,33 @@ Are you sure ?</source> <context> <name>ProcessConfigureCommunity</name> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="117"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="126"/> <source>Configure community {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="120"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="129"/> <source>Add a community</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> <source>Error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="187"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="192"/> <source>Delete</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="198"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> <source>Pubkey not found</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="198"></location> - <source>The public key of your account wasn't found in the community. : + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> + <source>The public key of your account wasn't found in the community. : {0} @@ -1448,25 +1437,25 @@ Would you like to publish the key ?</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="209"></location> - <source>Pubkey publishing error</source> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>UID Publishing</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="212"></location> - <source>Network error</source> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>Success publishing your UID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="212"></location> - <source>Couldn't connect to network : {0}</source> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> + <source>{0} : {1}</source> <translation type="unfinished"></translation> </message> </context> <context> <name>Toast</name> <message> - <location filename="../../ui/toast.ui" line="14"></location> + <location filename="../../ui/toast.ui" line="14"/> <source>MainWindow</source> <translation type="unfinished"></translation> </message> @@ -1474,190 +1463,194 @@ Would you like to publish the key ?</source> <context> <name>TransactionsTabWidget</name> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="104"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="123"/> <source><b>Deposits</b> {:} {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="108"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="127"/> <source><b>Payments</b> {:} {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="112"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="131"/> <source><b>Balance</b> {:} {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="121"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="140"/> <source>Actions</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="134"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="153"/> <source>Send again</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="139"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="158"/> <source>Cancel</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="145"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="164"/> <source>Informations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="150"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="169"/> <source>Add as contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="155"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="174"/> <source>Send money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="161"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="180"/> <source>View in Web of Trust</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="166"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="185"/> <source>Copy pubkey to clipboard</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> <source>Warning</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> <source>Are you sure ? This money transfer will be removed and not sent.</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="93"/> + <source>Received {0} {1} from {2} transfers</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="97"/> + <source>New transactions received</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>TransferMoneyDialog</name> <message> - <location filename="../../ui/transfer.ui" line="14"></location> + <location filename="../../ui/transfer.ui" line="14"/> <source>Transfer money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="20"></location> + <location filename="../../ui/transfer.ui" line="20"/> <source>Community</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="32"></location> + <location filename="../../ui/transfer.ui" line="32"/> <source>Transfer money to</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="40"></location> + <location filename="../../ui/transfer.ui" line="40"/> <source>Contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="61"></location> + <location filename="../../ui/transfer.ui" line="61"/> <source>Recipient public key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="80"></location> + <location filename="../../ui/transfer.ui" line="80"/> <source>Key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="106"></location> + <location filename="../../ui/transfer.ui" line="106"/> <source>Wallet :</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="125"></location> + <location filename="../../ui/transfer.ui" line="125"/> <source>Availalble currency : </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="134"></location> + <location filename="../../ui/transfer.ui" line="134"/> <source>Amount :</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="144"></location> + <location filename="../../ui/transfer.ui" line="144"/> <source> UD</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transfer.ui" line="162"></location> + <location filename="../../ui/transfer.ui" line="162"/> <source>Transaction message</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="92"></location> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> <source>Money transfer</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="64"></location> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> <source>No amount. Please give the transfert amount</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="78"></location> - <source>Success transfering {0} {1} to {2}</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="83"></location> - <source>Something wrong happened : {0}</source> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>Error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="88"></location> - <source>This transaction could not be sent on this block -Please try again later</source> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Transfer</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="92"></location> - <source>Couldn't connect to network : {0}</source> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Success sending money to {0}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="97"></location> - <source>Error</source> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>{0} : {1}</source> <translation type="unfinished"></translation> </message> </context> <context> <name>WalletsTab</name> <message> - <location filename="../../ui/wallets_tab.ui" line="14"></location> + <location filename="../../ui/wallets_tab.ui" line="14"/> <source>Form</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/wallets_tab.ui" line="43"></location> + <location filename="../../ui/wallets_tab.ui" line="43"/> <source>Account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/wallets_tab.ui" line="52"></location> + <location filename="../../ui/wallets_tab.ui" line="52"/> <source>label_general</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/wallets_tab.ui" line="68"></location> + <location filename="../../ui/wallets_tab.ui" line="68"/> <source>Balance</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/wallets_tab.ui" line="74"></location> + <location filename="../../ui/wallets_tab.ui" line="74"/> <source>label_balance</source> <translation type="unfinished"></translation> </message> @@ -1665,99 +1658,99 @@ Please try again later</source> <context> <name>WalletsTabWidget</name> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="70"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="70"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> <source>Membership</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="70"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> <source>Last renewal on {:}, expiration on {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source>Your web of trust</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source>Certified by {:} members; Certifier of {:} members</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source>Not a member</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>Your money share </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>{:.2f}%</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>Your part </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>{:} {:} in [{:} ; {:}] {:}</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="173"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="181"/> <source>New Wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="176"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="184"/> <source>Rename</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="180"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="188"/> <source>Copy pubkey to clipboard</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="185"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="193"/> <source>Transfer to...</source> <translation type="unfinished"></translation> </message> @@ -1765,17 +1758,17 @@ Please try again later</source> <context> <name>WalletsTableModel</name> <message> - <location filename="../../../src/cutecoin/models/wallets.py" line="70"></location> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> <source>Name</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/wallets.py" line="70"></location> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> <source>Amount</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/models/wallets.py" line="70"></location> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> <source>Pubkey</source> <translation type="unfinished"></translation> </message> @@ -1783,22 +1776,22 @@ Please try again later</source> <context> <name>WoT.Node</name> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="288"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="288"/> <source>Informations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"/> <source>Add as contact</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="298"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="298"/> <source>Send money</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="303"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="303"/> <source>Certify identity</source> <translation type="unfinished"></translation> </message> @@ -1806,12 +1799,12 @@ Please try again later</source> <context> <name>WotTabWidget</name> <message> - <location filename="../../ui/wot_tab.ui" line="14"></location> + <location filename="../../ui/wot_tab.ui" line="14"/> <source>Form</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/wot_tab.ui" line="33"></location> + <location filename="../../ui/wot_tab.ui" line="33"/> <source>Me</source> <translation type="unfinished"></translation> </message> @@ -1819,32 +1812,32 @@ Please try again later</source> <context> <name>self.config_dialog</name> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="89"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="92"/> <source>Ok</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="71"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="71"/> <source>Forbidden : salt is too short</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="75"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="75"/> <source>Forbidden : password is too short</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="79"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="79"/> <source>Forbidden : Invalid characters in salt field</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="83"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="83"/> <source>Forbidden : Invalid characters in password field</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="89"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="89"/> <source>Error : passwords are different</source> <translation type="unfinished"></translation> </message> @@ -1852,27 +1845,27 @@ Please try again later</source> <context> <name>transactionsTabWidget</name> <message> - <location filename="../../ui/transactions_tab.ui" line="14"></location> + <location filename="../../ui/transactions_tab.ui" line="14"/> <source>Form</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transactions_tab.ui" line="37"></location> + <location filename="../../ui/transactions_tab.ui" line="37"/> <source>dd/MM/yyyy</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transactions_tab.ui" line="83"></location> + <location filename="../../ui/transactions_tab.ui" line="83"/> <source>Payment:</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transactions_tab.ui" line="90"></location> + <location filename="../../ui/transactions_tab.ui" line="90"/> <source>Deposit:</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../../ui/transactions_tab.ui" line="100"></location> + <location filename="../../ui/transactions_tab.ui" line="100"/> <source>Balance:</source> <translation type="unfinished"></translation> </message> diff --git a/res/i18n/ts/fr_FR.ts b/res/i18n/ts/fr_FR.ts index ba5dc2a05568742ab065e5b95d203fb03826724d..fdc686921cfbd7d5e9d917cd3373f7d8560a23aa 100644 --- a/res/i18n/ts/fr_FR.ts +++ b/res/i18n/ts/fr_FR.ts @@ -1,35 +1,34 @@ -<?xml version='1.0' encoding='utf-8'?> -<!DOCTYPE TS> -<TS version="2.0" language="fr_FR" sourcelanguage="en"> +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="2.0" language="fr_FR" sourcelanguage="en"> <context> <name>@default</name> <message> - <location filename="../../../src/cutecoin/core/account.py" line="61"></location> + <location filename="../../../src/cutecoin/core/account.py" line="61"/> <source>ud {0}</source> <translation type="obsolete">du {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="285"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="285"/> <source>Informations</source> <translation type="obsolete">Informations</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="289"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="289"/> <source>Add as contact</source> <translation type="obsolete">Ajouter comme contact</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"/> <source>Send money</source> - <translation type="obsolete">Envoyer de l'argent</translation> + <translation type="obsolete">Envoyer de l'argent</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="310"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="310"/> <source>Renew membership</source> <translation type="obsolete">Renouveller le statut de membre</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="297"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="297"/> <source>Certify identity</source> <translation type="obsolete">Certifier cette identité</translation> </message> @@ -37,12 +36,12 @@ <context> <name>AboutPopup</name> <message> - <location filename="../../ui/about.ui" line="14"></location> + <location filename="../../ui/about.ui" line="14"/> <source>About</source> - <translation>A proposh</translation> + <translation>A propos</translation> </message> <message> - <location filename="../../ui/about.ui" line="22"></location> + <location filename="../../ui/about.ui" line="22"/> <source>label</source> <translation></translation> </message> @@ -50,42 +49,42 @@ <context> <name>Account</name> <message> - <location filename="../../../src/cutecoin/core/account.py" line="61"></location> + <location filename="../../../src/cutecoin/core/account.py" line="61"/> <source>ud {0}</source> <translation type="obsolete">du {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Units</source> <translation>Unités</translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>UD</source> <translation>DU</translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Quant Z-sum</source> <translation>Quant. som. 0</translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Relat Z-sum</source> <translation>Rel. som. 0</translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>UD {0}</source> <translation>DU {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>Q0 {0}</source> <translation>Q0 {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/core/account.py" line="103"></location> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> <source>R0 {0}</source> <translation>R0 {0}</translation> </message> @@ -93,77 +92,77 @@ <context> <name>AccountConfigurationDialog</name> <message> - <location filename="../../ui/account_cfg.ui" line="14"></location> + <location filename="../../ui/account_cfg.ui" line="14"/> <source>Add an account</source> <translation>Ajouter un compte</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="30"></location> + <location filename="../../ui/account_cfg.ui" line="30"/> <source>Account parameters</source> <translation>Paramètres du compte</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="51"></location> + <location filename="../../ui/account_cfg.ui" line="51"/> <source>Account name (uid)</source> <translation>Nom de compte</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="68"></location> + <location filename="../../ui/account_cfg.ui" line="68"/> <source>Wallets</source> <translation>Nombre de portefeuilles</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="111"></location> + <location filename="../../ui/account_cfg.ui" line="111"/> <source>Delete account</source> <translation>Supprimer ce compte</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="140"></location> + <location filename="../../ui/account_cfg.ui" line="140"/> <source>Key parameters</source> <translation>Paramètres de la clé</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="180"></location> + <location filename="../../ui/account_cfg.ui" line="180"/> <source>Your password</source> <translation>Votre mot de passe</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="193"></location> + <location filename="../../ui/account_cfg.ui" line="193"/> <source>Please repeat your password</source> <translation>Veuillez répéter votre mot de passe</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="212"></location> + <location filename="../../ui/account_cfg.ui" line="212"/> <source>Show public key</source> <translation>Afficher la clé publique correspondante</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="242"></location> + <location filename="../../ui/account_cfg.ui" line="242"/> <source>Communities membership</source> <translation>Communautés</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="257"></location> + <location filename="../../ui/account_cfg.ui" line="257"/> <source>Add a community</source> <translation>Ajouter une communauté</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="264"></location> + <location filename="../../ui/account_cfg.ui" line="264"/> <source>Remove selected community</source> <translation>Supprimer la communauté sélectionnée</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="288"></location> + <location filename="../../ui/account_cfg.ui" line="288"/> <source>Previous</source> <translation>Précédent</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="308"></location> + <location filename="../../ui/account_cfg.ui" line="308"/> <source>Next</source> <translation>Suivant</translation> </message> <message> - <location filename="../../ui/account_cfg.ui" line="170"></location> + <location filename="../../ui/account_cfg.ui" line="170"/> <source>CryptoID</source> <translation>CryptoID</translation> </message> @@ -171,237 +170,247 @@ <context> <name>CertificationDialog</name> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="58"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> <source>Certification</source> <translation>Certification</translation> </message> <message> - <location filename="../../ui/certification.ui" line="20"></location> + <location filename="../../ui/certification.ui" line="20"/> <source>Community</source> <translation>Communauté</translation> </message> <message> - <location filename="../../ui/certification.ui" line="32"></location> + <location filename="../../ui/certification.ui" line="32"/> <source>Certify user</source> <translation>Utilisateur certifié</translation> </message> <message> - <location filename="../../ui/certification.ui" line="40"></location> + <location filename="../../ui/certification.ui" line="40"/> <source>Contact</source> <translation>Contact</translation> </message> <message> - <location filename="../../ui/certification.ui" line="61"></location> + <location filename="../../ui/certification.ui" line="61"/> <source>User public key</source> <translation>Clé publique</translation> </message> <message> - <location filename="../../ui/certification.ui" line="80"></location> + <location filename="../../ui/certification.ui" line="80"/> <source>Key</source> <translation>Clé</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="49"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> <source>Success certifying {0} from {1}</source> <translation>Succès lors de la certification de {0}, dans la communauté {1}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="53"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="53"/> <source>Something wrong happened : {0}</source> - <translation>Une erreur a été rencontrée : {0}</translation> + <translation type="obsolete">Une erreur a été rencontrée : {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="58"></location> - <source>Couldn't connect to network : {0}</source> - <translation>Impossible de se connecter au réseau : {0}</translation> + <location filename="../../../src/cutecoin/gui/certification.py" line="58"/> + <source>Couldn't connect to network : {0}</source> + <translation type="obsolete">Impossible de se connecter au réseau : {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="63"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> <source>Error</source> <translation>Erreur</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="76"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="77"/> <source>Ok</source> <translation>Ok</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/certification.py" line="79"></location> + <location filename="../../../src/cutecoin/gui/certification.py" line="80"/> <source>Not a member</source> <translation>Non-membre</translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>CommunityConfigurationDialog</name> <message> - <location filename="../../ui/community_cfg.ui" line="17"></location> + <location filename="../../ui/community_cfg.ui" line="17"/> <source>Add a community</source> <translation>Ajouter une communauté</translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="46"></location> + <location filename="../../ui/community_cfg.ui" line="46"/> <source>Please enter the address of a node :</source> - <translation>Veuillez entrer l'adresse d'un noeud :</translation> + <translation>Veuillez entrer l'adresse d'un noeud :</translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="61"></location> + <location filename="../../ui/community_cfg.ui" line="61"/> <source>:</source> <translation>:</translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="100"></location> + <location filename="../../ui/community_cfg.ui" line="124"/> <source>Communities nodes</source> <translation>Noeuds de la communauté</translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="118"></location> + <location filename="../../ui/community_cfg.ui" line="142"/> <source>Server</source> <translation>Serveur</translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="138"></location> + <location filename="../../ui/community_cfg.ui" line="162"/> <source>Add</source> <translation>Ajouter</translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="159"></location> + <location filename="../../ui/community_cfg.ui" line="183"/> <source>Previous</source> <translation>Précédent</translation> </message> <message> - <location filename="../../ui/community_cfg.ui" line="182"></location> + <location filename="../../ui/community_cfg.ui" line="206"/> <source>Next</source> <translation>Suivant</translation> </message> + <message> + <location filename="../../ui/community_cfg.ui" line="98"/> + <source>Check node connectivity</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>CommunityTabWidget</name> <message> - <location filename="../../ui/community_tab.ui" line="17"></location> + <location filename="../../ui/community_tab.ui" line="17"/> <source>communityTabWidget</source> <translation></translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="40"></location> + <location filename="../../ui/community_tab.ui" line="40"/> <source>Identities</source> <translation>Identités</translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="53"></location> + <location filename="../../ui/community_tab.ui" line="53"/> <source>Research a pubkey, an uid...</source> <translation>Rechercher une clé publique, un uid...</translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="118"></location> + <location filename="../../ui/community_tab.ui" line="118"/> <source>Quality : </source> <translation>Qualification : </translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="334"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="351"/> <source>Renew membership</source> <translation>Renouveller le statut de membre</translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="146"></location> + <location filename="../../ui/community_tab.ui" line="146"/> <source>Send leaving demand</source> <translation>Quitter la communauté</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="202"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> <source>Membership</source> <translation>Statut de membre</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="172"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="172"/> <source>Success sending membership demand</source> - <translation>Succès lors de l'envoi d'une demande de membre</translation> + <translation type="obsolete">Succès lors de l'envoi d'une demande de membre</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="174"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="174"/> <source>Join demand error</source> - <translation>Erreur lors de l'envoi d'une demande de membre</translation> + <translation type="obsolete">Erreur lors de l'envoi d'une demande de membre</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="177"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="177"/> <source>Key not sent to community</source> - <translation>La clé n'a pas pu être envoyée à la communauté</translation> + <translation type="obsolete">La clé n'a pas pu être envoyée à la communauté</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="259"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> <source>Network error</source> <translation>Erreur réseau</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="259"></location> - <source>Couldn't connect to network : {0}</source> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> + <source>Couldn't connect to network : {0}</source> <translation>Impossible de se connecter au réseau : {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="242"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> <source>Warning</source> <translation>Attention</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="202"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="202"/> <source>Success sending leaving demand</source> - <translation>Succès lors de l'envoi de la demande pour quitter la communauté</translation> + <translation type="obsolete">Succès lors de l'envoi de la demande pour quitter la communauté</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="204"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="204"/> <source>Leaving demand error</source> - <translation>Erreur lors de l'envoi de la demande pour quitter la communauté</translation> + <translation type="obsolete">Erreur lors de l'envoi de la demande pour quitter la communauté</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="263"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="263"/> <source>Error</source> - <translation>Erreur</translation> + <translation type="obsolete">Erreur</translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="60"></location> + <location filename="../../ui/community_tab.ui" line="60"/> <source>Search</source> <translation>Rechercher</translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="125"></location> + <location filename="../../ui/community_tab.ui" line="125"/> <source>Publish UID</source> <translation>Publier votre UID</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="58"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="60"/> <source>Members</source> <translation>Membres</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="61"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="63"/> <source>Direct connections</source> <translation>Connections directes</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="218"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="218"/> <source>Are you sure ? Publishing your UID cannot be canceled.</source> <translation type="obsolete">Êtes vous certain ? Publier votre UID ne peut être annulé.</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="227"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> <source>UID Publishing</source> - <translation>Publication de l'UID</translation> + <translation>Publication de l'UID</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="227"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> <source>Success publishing your UID</source> <translation>Succès lors de la publication de votre UID</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="177"></location> - <source>"Your key wasn't sent in the community. -You can't request a membership.</source> - <translation>Votre clé publique n'a pas été envoyée à la communauté. + <location filename="../../../src/cutecoin/gui/community_tab.py" line="177"/> + <source>"Your key wasn't sent in the community. +You can't request a membership.</source> + <translation type="obsolete">Votre clé publique n'a pas été envoyée à la communauté. Vous ne pouvez pas envoyer de requête de membre.</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="190"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="196"/> <source>Are you sure ? Sending a leaving demand cannot be canceled. The process to join back the community later will have to be done again.</source> @@ -410,97 +419,122 @@ Envoyer une demande pour quitter la communauté ne peut être annulée. Le processus pour rejoindre la communauté devrait être refait à zéro.</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="57"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="59"/> <source>Web of Trust</source> <translation>Toile de Confiance</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="79"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="104"/> <source>Informations</source> <translation>Informations</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="83"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="107"/> <source>Add as contact</source> <translation>Ajouter comme contact</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="87"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="111"/> <source>Send money</source> - <translation>Envoyer de l'argent</translation> + <translation>Envoyer de l'argent</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="91"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="115"/> <source>Certify identity</source> <translation>Certifier cette identité</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="95"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="119"/> <source>View in Web of Trust</source> <translation>Voir dans la Toile de Confiance</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="341"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="358"/> <source>Send membership demand</source> <translation>Envoyer une demande de membre</translation> </message> <message> - <location filename="../../ui/community_tab.ui" line="132"></location> + <location filename="../../ui/community_tab.ui" line="132"/> <source>Revoke UID</source> <translation>Révoquer votre UID</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="216"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="209"/> <source>Are you sure ? Publishing your UID can be canceled by Revoke UID.</source> <translation>Etes-vous sûr(e) ? Publier votre UID peut être annulé par le bouton Révoquer votre UID.</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="230"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="223"/> <source>Publish UID error</source> <translation>Publier votre UID</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="242"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> <source>Are you sure ? Revoking your UID can only success if it is not already validated by the network.</source> - <translation>Etes-vous sûr(e) ? Révoquer votre UID ne peut réussir que s'il n'a pas été déjà validé par le réseau.</translation> + <translation>Etes-vous sûr(e) ? Révoquer votre UID ne peut réussir que s'il n'a pas été déjà validé par le réseau.</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="253"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="253"/> <source>UID Revoking</source> - <translation>Révocation de votre UID</translation> + <translation type="obsolete">Révocation de votre UID</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="253"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="253"/> <source>Success revoking your UID</source> - <translation>Révocation de votre UID réussie</translation> + <translation type="obsolete">Révocation de votre UID réussie</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/community_tab.py" line="256"></location> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="256"/> <source>Revoke UID error</source> - <translation>Erreur lors de la révocation de votre UID</translation> + <translation type="obsolete">Erreur lors de la révocation de votre UID</translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> + <source>Success sending Membership demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Revoke</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Success sending Revoke demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Self Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Success sending Self Certification document</source> + <translation type="unfinished"></translation> </message> </context> <context> <name>ConfigureContactDialog</name> <message> - <location filename="../../ui/contact.ui" line="14"></location> + <location filename="../../ui/contact.ui" line="14"/> <source>Add a contact</source> <translation>Ajouter un contact</translation> </message> <message> - <location filename="../../ui/contact.ui" line="36"></location> + <location filename="../../ui/contact.ui" line="36"/> <source>Pubkey</source> <translation>Clé publique</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/contact.py" line="58"></location> + <location filename="../../../src/cutecoin/gui/contact.py" line="58"/> <source>Contact already exists</source> <translation>Le contact existe déja</translation> </message> <message> - <location filename="../../ui/contact.ui" line="22"></location> + <location filename="../../ui/contact.ui" line="22"/> <source>Name</source> <translation>Nom</translation> </message> @@ -508,22 +542,22 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>CreateWalletDialog</name> <message> - <location filename="../../ui/create_wallet.ui" line="14"></location> + <location filename="../../ui/create_wallet.ui" line="14"/> <source>Create a new wallet</source> <translation>Créer un portefeuille</translation> </message> <message> - <location filename="../../ui/create_wallet.ui" line="45"></location> + <location filename="../../ui/create_wallet.ui" line="45"/> <source>Wallet name :</source> <translation>Nom du portefeuille :</translation> </message> <message> - <location filename="../../ui/create_wallet.ui" line="83"></location> + <location filename="../../ui/create_wallet.ui" line="83"/> <source>Previous</source> <translation>Précédent</translation> </message> <message> - <location filename="../../ui/create_wallet.ui" line="103"></location> + <location filename="../../ui/create_wallet.ui" line="103"/> <source>Next</source> <translation>Suivant</translation> </message> @@ -531,82 +565,82 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>CurrencyTabWidget</name> <message> - <location filename="../../ui/currency_tab.ui" line="14"></location> + <location filename="../../ui/currency_tab.ui" line="14"/> <source>Form</source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="77"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="72"/> <source>Wallets</source> <translation>Portefeuilles</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="85"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="76"/> <source>Transactions</source> <translation>Transferts</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="94"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="84"/> <source>Community</source> <translation>Communauté</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="100"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="80"/> <source>Informations</source> <translation>Informations</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="112"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="88"/> <source>Network</source> <translation>Réseau</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="183"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="161"/> <source> Block {0}</source> - <translation> Bloc {0}</translation> + <translation>Bloc {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="75"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="75"/> <source>Membership expiration<b>Warning : Membership expiration in {0} days</b></source> <translation type="obsolete">Expiration du statut de membre</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="206"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="206"/> <source>Received {0} {1} from {2} transfers</source> - <translation>Reception de {0} {1} dans {2} transfers</translation> + <translation type="obsolete">Reception de {0} {1} dans {2} transfers</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="210"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="210"/> <source>New transactions received</source> - <translation>Nouveaux transferts reçus</translation> + <translation type="obsolete">Nouveaux transferts reçus</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="145"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> <source>Membership expiration</source> <translation>Expiration du statut de membre</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="145"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> <source><b>Warning : Membership expiration in {0} days</b></source> <translation><b>Attention : Expiration du statut de membre dans {0} jours</b></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="43"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="43"/> <source>Warning : Your membership is expiring soon.</source> <translation>Attention : Votre statut de membre expire bientôt.</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="45"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="45"/> <source>Warning : Your could miss certifications soon.</source> <translation>Attention : Vous pourriez manquer de certifications prochainement.</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="150"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> <source>Certifications number</source> <translation>Nombre de certifications</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/currency_tab.py" line="150"></location> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> <source><b>Warning : You are certified by only {0} persons, need {1}</b></source> <translation><b>Attention : Vous êtes certifiés par seulement {0} personnes, besoin de {1}</b></translation> </message> @@ -614,22 +648,22 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>DialogMember</name> <message> - <location filename="../../ui/member.ui" line="14"></location> + <location filename="../../ui/member.ui" line="14"/> <source>Informations</source> <translation>Informations</translation> </message> <message> - <location filename="../../ui/member.ui" line="34"></location> + <location filename="../../ui/member.ui" line="34"/> <source>Member</source> <translation>Membre</translation> </message> <message> - <location filename="../../ui/member.ui" line="65"></location> + <location filename="../../ui/member.ui" line="65"/> <source>uid</source> <translation></translation> </message> <message> - <location filename="../../ui/member.ui" line="72"></location> + <location filename="../../ui/member.ui" line="72"/> <source>properties</source> <translation></translation> </message> @@ -637,32 +671,32 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>HistoryTableModel</name> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Date</source> <translation>Date</translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>UID/Public key</source> <translation>UID/Clé publique</translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Payment</source> <translation>Débit</translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Deposit</source> <translation>Crédit</translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="179"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> <source>Comment</source> <translation>Commentaire</translation> </message> <message> - <location filename="../../../src/cutecoin/models/txhistory.py" line="166"></location> + <location filename="../../../src/cutecoin/models/txhistory.py" line="166"/> <source>State</source> <translation type="obsolete">Statut</translation> </message> @@ -670,37 +704,37 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>HomeScreenWidget</name> <message> - <location filename="../../ui/homescreen.ui" line="20"></location> + <location filename="../../ui/homescreen.ui" line="20"/> <source>Form</source> - <translation>Formulaire</translation> + <translation></translation> </message> <message> - <location filename="../../ui/homescreen.ui" line="49"></location> + <location filename="../../ui/homescreen.ui" line="49"/> <source><html><head/><body><p><br/></p></body></html></source> <translation></translation> </message> <message> - <location filename="../../ui/homescreen.ui" line="67"></location> + <location filename="../../ui/homescreen.ui" line="67"/> <source>Create a new account</source> <translation>Créer un nouveau compte</translation> </message> <message> - <location filename="../../ui/homescreen.ui" line="100"></location> + <location filename="../../ui/homescreen.ui" line="100"/> <source>Import an existing account</source> <translation>Importer un compte</translation> </message> <message> - <location filename="../../ui/homescreen.ui" line="127"></location> + <location filename="../../ui/homescreen.ui" line="127"/> <source>Get to know more about ucoin</source> <translation>En savoir plus sur ucoin</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/homescreen.py" line="35"></location> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="35"/> <source>Please get the latest release {version}</source> <translation>Veuillez télécharger la dernière version {version}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/homescreen.py" line="39"></location> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="39"/> <source> <h1>Welcome to Cutecoin {version}</h1> <h2>{version_info}</h2> @@ -716,22 +750,22 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>IdentitiesTableModel</name> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="85"></location> + <location filename="../../../src/cutecoin/models/identities.py" line="84"/> <source>UID</source> <translation>UID</translation> </message> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="86"></location> + <location filename="../../../src/cutecoin/models/identities.py" line="85"/> <source>Pubkey</source> <translation>Clé publique</translation> </message> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="87"></location> + <location filename="../../../src/cutecoin/models/identities.py" line="86"/> <source>Renewed</source> <translation>Dernier renouvellement</translation> </message> <message> - <location filename="../../../src/cutecoin/models/identities.py" line="88"></location> + <location filename="../../../src/cutecoin/models/identities.py" line="87"/> <source>Expiration</source> <translation>Expiration</translation> </message> @@ -739,57 +773,57 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>ImportAccountDialog</name> <message> - <location filename="../../ui/import_account.ui" line="25"></location> + <location filename="../../ui/import_account.ui" line="25"/> <source>Import a file</source> <translation>Importer un fichier</translation> </message> <message> - <location filename="../../ui/import_account.ui" line="36"></location> + <location filename="../../ui/import_account.ui" line="36"/> <source>Name of the account :</source> <translation>Nom du compte :</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="34"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="34"/> <source>Error</source> <translation>Erreur</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="38"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> <source>Account import</source> <translation>Import de compte</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="38"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> <source>Account imported succefully !</source> <translation>Compte importé avec succès !</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="43"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> <source>Import an account file</source> <translation>Importer un fichier de compte</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="43"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> <source>All account files (*.acc)</source> <translation>Tout fichier de compte (*.acc)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="58"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="58"/> <source>Please enter a name</source> <translation>Veuillez entrer un nom</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="63"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="63"/> <source>Name already exists</source> <translation>Ce nom existe déja</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/import_account.py" line="67"></location> + <location filename="../../../src/cutecoin/gui/import_account.py" line="67"/> <source>File is not an account format</source> - <translation>Le fichier n'est pas au format de compte</translation> + <translation>Le fichier n'est pas au format de compte</translation> </message> <message> - <location filename="../../ui/import_account.ui" line="14"></location> + <location filename="../../ui/import_account.ui" line="14"/> <source>Import an account</source> <translation>Importer un compte</translation> </message> @@ -797,365 +831,400 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>InformationsTabWidget</name> <message> - <location filename="../../ui/informations_tab.ui" line="14"></location> + <location filename="../../ui/informations_tab.ui" line="14"/> <source>Form</source> <translation></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="52"></location> + <location filename="../../ui/informations_tab.ui" line="52"/> <source>General</source> <translation>Général</translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="61"></location> + <location filename="../../ui/informations_tab.ui" line="61"/> <source>label_general</source> <translation></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="77"></location> + <location filename="../../ui/informations_tab.ui" line="77"/> <source>Rules</source> <translation>Règles</translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="83"></location> + <location filename="../../ui/informations_tab.ui" line="83"/> <source>label_rules</source> <translation></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="96"></location> + <location filename="../../ui/informations_tab.ui" line="96"/> <source>Money</source> <translation>Monnaie</translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="102"></location> + <location filename="../../ui/informations_tab.ui" line="102"/> <source>label_money</source> <translation></translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="115"></location> + <location filename="../../ui/informations_tab.ui" line="115"/> <source>WoT</source> <translation>Toile de Confiance</translation> </message> <message> - <location filename="../../ui/informations_tab.ui" line="121"></location> + <location filename="../../ui/informations_tab.ui" line="121"/> <source>label_wot</source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="121"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> - <translation> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:2.2%} / {:} jours</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <translation type="obsolete"> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:2.2%} / {:} jours</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Universal Dividend UD(t) in</source> <translation>Dividende Universel DU(t) en</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> <source>Monetary Mass M(t) in</source> - <translation>Masse Monétaire M(t) en</translation> + <translation type="obsolete">Masse Monétaire M(t) en</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Members N(t)</source> <translation>Membres N(t)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> <source>Monetary Mass per member M(t)/N(t) in</source> - <translation>Masse Monétaire par membre M(t)/N(t) en</translation> + <translation type="obsolete">Masse Monétaire par membre M(t)/N(t) en</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="72"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Actual growth c = UD(t)/[M(t-1)/N(t)]</source> - <translation type="obsolete">Croissance actuelle c = DU(t)/[M(t -1)/N(t)]</translation> + <translation>Croissance actuelle c = DU(t)/[M(t -1)/N(t)]</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Next UD date and time (t+1)</source> - <translation>Prochain DU, date et heure (t+1)</translation> + <translation type="unfinished">Date et heure du prochain DU (t+1)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="165"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="202"/> <source>No Universal Dividend created yet.</source> <translation>Pas de dividende universel créé pour le moment.</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>{:2.0%} / {:} days</source> <translation>{:2.0%} / {:} jours</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>Fundamental growth (c) / Delta time (dt)</source> <translation>Croissance fondamentale (c) / Delta de temps (dt)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="135"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="135"/> <source>UD(t+1) = MAX { UD(t) ; c * M(t) / N(t) }</source> <translation type="obsolete">DU(t+1) = MAX { DU(t) ; c * M(t) / N(t) }</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>Universal Dividend (formula)</source> <translation>Dividende Universel (formule)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>Universal Dividend (computed)</source> <translation>Dividende Universel (calculé)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:2.0%} / {:} days</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:2.0%}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:2.0%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:2.0%}</b></td><td>{:}</td></tr> </table> </source> <translation> - <table cellpadding="5"> - <tr><td align="right"><b>{:2.0%} / {:} jours</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:2.0%}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:2.0%} / {:} jours</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:2.0%}</b></td><td>{:}</td></tr> </table> </translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Fundamental growth (c)</source> <translation>Croissance fondamentale (c)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Initial Universal Dividend UD(0) in</source> <translation>Dividende Universel Initial DU(0) en</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Time period (dt) in days (86400 seconds) between two UD</source> <translation>Période de temps (dt) en jours (86400 secondes) entre deux DU</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>Number of blocks used for calculating median time</source> <translation>Nombre de blocs utilisés pour calculer le temps median</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The average time in seconds for writing 1 block (wished time)</source> <translation>Le temps moyen en secondes pour écrire un bloc (temps espéré)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The number of blocks required to evaluate again PoWMin value</source> <translation>Le nombre de blocs requis pour évaluer une nouvelle valeur de PoWMin</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The number of previous blocks to check for personalized difficulty</source> <translation>Le nombre de blocs précédents pour vérifier la difficulté personnalisée</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="168"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> <source>The percent of previous issuers to reach for personalized difficulty</source> - <translation>Le pourcentage d'utilisateurs précédents atteignant la difficulté personnalisée</translation> + <translation>Le pourcentage d'utilisateurs précédents atteignant la difficulté personnalisée</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Minimum delay between 2 identical certifications (in days)</source> <translation>Le délai minimum entre 2 certifications identiques (en jours)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Maximum age of a valid signature (in days)</source> - <translation>Age maximum d'une signature valide (en jours)</translation> + <translation>Age maximum d'une signature valide (en jours)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Minimum quantity of signatures to be part of the WoT</source> <translation>Nombre de signatures minimum pour faire partie de la TdC</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Minimum quantity of valid made certifications to be part of the WoT for distance rule</source> <translation>Quantité minimum de certifications valides pour faire partie de la TdC suivant la règle de distance</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Maximum age of a valid membership (in days)</source> - <translation>Age maximum d'un statut de membre valide (en jours)</translation> + <translation>Age maximum d'un statut de membre valide (en jours)</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> <source>Maximum distance between each WoT member and a newcomer</source> <translation>Distance maximum entre chaque membre de la TdC et un nouveau venu</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="78"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Monetary Mass M(t-1) in</source> - <translation type="obsolete">Masse Monétaire M(t-1) en</translation> + <translation>Masse Monétaire M(t-1) en</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="78"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> <source>Monetary Mass per member M(t-1)/N(t) in</source> - <translation type="obsolete">Masse Monétaire par membre M(t-1)/N(t) en</translation> + <translation>Masse Monétaire par membre M(t-1)/N(t) en</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="127"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="127"/> <source>UD(t+1) = MAX { UD(t) ; c * M(t-1) / N(t) }</source> <translation type="obsolete">DU(t+1) = MAX { DU(t) ; c * M(t-1) / N(t) }</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="139"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="139"/> <source>UD(t+1) = MAX { UD(t) ; c * M(t) / N(t+1) }</source> <translation type="obsolete">DU(t+1) = MAX { DU(t) ; c * M(t) / N(t+1) }</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="74"/> <source>Actual growth c = UD(t)/[M(t-1)/N(t-1)]</source> - <translation>Croissance actuelle c = DU(t)/[M(t -1)/N(t-1)]</translation> + <translation type="obsolete">Croissance actuelle c = DU(t)/[M(t -1)/N(t-1)]</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> - <source>UD(t+1) = MAX { UD(t) ; c × M(t) / N(t) }</source> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> + <source>UD(t+1) = MAX { UD(t) ; c × M(t) / N(t) }</source> <translation type="obsolete">DU(t+1) = MAX { DU(t) ; c × M(t) / N(t) }</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> <source>UD(t+1) = MAX { UD(t) ; c u00D7 M(t) / N(t) }</source> <translation type="obsolete">DU(t+1) = MAX { DU(t) ; c u00D7 M(t) / N(t) }</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"/> <source>UD(t+1) = MAX { UD(t) ; c &#215; M(t) / N(t) }</source> - <translation>DU(t+1) = MAX { DU(t) ; c &#215; M(t) / N(t) }</translation> + <translation type="obsolete">DU(t+1) = MAX { DU(t) ; c &#215; M(t) / N(t) }</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/informations_tab.py" line="140"></location> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> <source>{:} = MAX {{ {:} {:} ; {:2.0%} &#215; {:} {:} / {:} }}</source> - <translation>{:} = MAX {{ {:} {:} ; {:2.0%} &#215; {:} {:} / {:} }}</translation> + <translation></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>UD(t+1) = MAX { UD(t) ; c &#215; M(t) / N(t+1) }</source> + <translation></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:2.2%} / {:} jours</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr + </table> + </translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Last UD date and time (t)</source> + <translation>Date et heure du dernier DU (t)</translation> </message> </context> <context> <name>MainWindow</name> <message> - <location filename="../../ui/mainwindow.ui" line="142"></location> + <location filename="../../ui/mainwindow.ui" line="142"/> <source>Account</source> <translation>Compte</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="61"></location> + <location filename="../../ui/mainwindow.ui" line="61"/> <source>Contacts</source> <translation type="obsolete">Contacts</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="75"></location> + <location filename="../../ui/mainwindow.ui" line="75"/> <source>Actions</source> <translation type="obsolete">Actions</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="87"></location> + <location filename="../../ui/mainwindow.ui" line="87"/> <source>Manage accounts</source> <translation>Gérer les comptes</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="92"></location> + <location filename="../../ui/mainwindow.ui" line="92"/> <source>Configure trustable nodes</source> <translation>Configurer les noeuds de confiance</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="117"></location> + <location filename="../../ui/mainwindow.ui" line="117"/> <source>Send a message</source> <translation>Envoyer un message</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="122"></location> + <location filename="../../ui/mainwindow.ui" line="122"/> <source>Send money</source> - <translation>Envoyer de l'argent</translation> + <translation>Envoyer de l'argent</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="127"></location> + <location filename="../../ui/mainwindow.ui" line="127"/> <source>Remove contact</source> <translation>Supprimer un contact</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="132"></location> + <location filename="../../ui/mainwindow.ui" line="132"/> <source>Save</source> <translation>Sauvegarder</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="390"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="338"/> <source>Export</source> <translation>Exporter</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="176"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="176"/> <source>Loading account {0}</source> - <translation>Chargement du compte {0}</translation> + <translation type="obsolete">Chargement du compte {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="242"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="190"/> <source>Latest release : {version}</source> <translation>Dernière version : {version}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="246"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="246"/> <source> <p><b>{version_info}</b></p> <p><a href={version_url}>Download link</a></p> @@ -1166,7 +1235,7 @@ Revoking your UID can only success if it is not already validated by the network </translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="256"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="204"/> <source> <h1>Cutecoin</h1> @@ -1201,133 +1270,133 @@ Revoking your UID can only success if it is not already validated by the network </translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="324"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="272"/> <source>Edit</source> <translation>Editer</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="327"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="275"/> <source>Delete</source> <translation>Supprimer</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="343"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="291"/> <source>CuteCoin {0}</source> <translation>CuteCoin {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="368"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="316"/> <source>CuteCoin {0} - Account : {1}</source> <translation>CuteCoin {0} - Compte : {1}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="388"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="336"/> <source>Export an account</source> <translation>Exporter un compte</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="389"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="337"/> <source>All account files (*.acc)</source> <translation>Tout fichier de compte (*.acc)</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="61"></location> + <location filename="../../ui/mainwindow.ui" line="61"/> <source>&Open</source> <translation>&Ouvrir</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="55"></location> + <location filename="../../ui/mainwindow.ui" line="55"/> <source>&Contacts</source> <translation>&Contacts</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="97"></location> + <location filename="../../ui/mainwindow.ui" line="97"/> <source>&Add a contact</source> <translation>&Ajouter un contact</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="132"></location> + <location filename="../../ui/mainwindow.ui" line="132"/> <source>&Add</source> <translation type="obsolete">&Ajouter</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="137"></location> + <location filename="../../ui/mainwindow.ui" line="137"/> <source>&Quit</source> <translation>&Quitter</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="147"></location> + <location filename="../../ui/mainwindow.ui" line="147"/> <source>&Transfer money</source> <translation>&Transférer de la monnaie</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="152"></location> + <location filename="../../ui/mainwindow.ui" line="152"/> <source>&Configure</source> <translation>&Configurer</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="157"></location> + <location filename="../../ui/mainwindow.ui" line="157"/> <source>&Import</source> <translation>&Importer</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="162"></location> + <location filename="../../ui/mainwindow.ui" line="162"/> <source>&Export</source> <translation>&Exporter</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="167"></location> + <location filename="../../ui/mainwindow.ui" line="167"/> <source>&Certification</source> <translation>&Certification</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="172"></location> + <location filename="../../ui/mainwindow.ui" line="172"/> <source>&Set as default</source> <translation>&Par défaut</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="177"></location> + <location filename="../../ui/mainwindow.ui" line="177"/> <source>A&bout</source> <translation>A &propos</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="182"></location> + <location filename="../../ui/mainwindow.ui" line="182"/> <source>&Preferences</source> <translation>&Préférences</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="281"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="229"/> <source>Please get the latest release {version}</source> <translation>Veuillez télécharger la dernière version {version}</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="41"></location> + <location filename="../../ui/mainwindow.ui" line="41"/> <source>Fi&le</source> <translation>&Fichier</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="76"></location> + <location filename="../../ui/mainwindow.ui" line="76"/> <source>&Help</source> <translation>&Aide</translation> </message> <message> - <location filename="../../ui/mainwindow.ui" line="187"></location> + <location filename="../../ui/mainwindow.ui" line="187"/> <source>&Add account</source> <translation>&Ajouter un compte</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="246"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="246"/> <source> <p><b>{version_info}</b></p> - <p><a href="{version_url}">Download link</a></p> + <p><a href="{version_url}">Download link</a></p> </source> <translation type="obsolete"> <p><b>{version_info}</b></p> - <p><a href="{version_url}">Lien de téléchargement</a></p> + <p><a href="{version_url}">Lien de téléchargement</a></p> </translation> </message> <message> - <location filename="../../../src/cutecoin/gui/mainwindow.py" line="249"></location> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="197"/> <source>Download link</source> <translation>Lien de téléchargement</translation> </message> @@ -1335,41 +1404,41 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>MemberDialog</name> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="31"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="31"/> <source>not a member</source> <translation>Non membre</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="43"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="43"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> </source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="47"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> <source>Public key</source> <translation>Clé publique</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="47"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> <source>Join date</source> - <translation>Date d'inscription</translation> + <translation>Date d'inscription</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="65"></location> - <source><tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr></source> + <location filename="../../../src/cutecoin/gui/member.py" line="65"/> + <source><tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr></source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="56"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="56"/> <source>Distance</source> <translation>Distance</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/member.py" line="61"></location> + <location filename="../../../src/cutecoin/gui/member.py" line="61"/> <source>Path</source> <translation>Chemin</translation> </message> @@ -1377,57 +1446,57 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>NetworkFilterProxyModel</name> <message> - <location filename="../../../src/cutecoin/models/network.py" line="39"></location> + <location filename="../../../src/cutecoin/models/network.py" line="41"/> <source>Address</source> <translation>Adresse</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="40"></location> + <location filename="../../../src/cutecoin/models/network.py" line="42"/> <source>Port</source> <translation>Port</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="41"></location> + <location filename="../../../src/cutecoin/models/network.py" line="43"/> <source>Block</source> <translation>Bloc</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="42"></location> + <location filename="../../../src/cutecoin/models/network.py" line="44"/> <source>UID</source> <translation>UID</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="43"></location> + <location filename="../../../src/cutecoin/models/network.py" line="45"/> <source>Member</source> <translation>Membre</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="44"></location> + <location filename="../../../src/cutecoin/models/network.py" line="46"/> <source>Pubkey</source> <translation>Clé publique</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="45"></location> + <location filename="../../../src/cutecoin/models/network.py" line="47"/> <source>Software</source> <translation>Logiciel</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="46"></location> + <location filename="../../../src/cutecoin/models/network.py" line="48"/> <source>Version</source> <translation>Version</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="59"></location> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> <source>yes</source> <translation>oui</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="59"></location> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> <source>no</source> <translation>non</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="59"></location> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> <source>offline</source> <translation>déconnecté</translation> </message> @@ -1435,40 +1504,45 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>NetworkTabWidget</name> <message> - <location filename="../../ui/network_tab.ui" line="14"></location> + <location filename="../../ui/network_tab.ui" line="14"/> <source>Form</source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/network_tab.py" line="51"></location> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="61"/> <source>Unset root node</source> <translation>Supprimer des noeuds racines</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/network_tab.py" line="57"></location> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="67"/> <source>Set as root node</source> <translation>Définir comme noeud racine</translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="73"/> + <source>Open in browser</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>NetworkTableModel</name> <message> - <location filename="../../../src/cutecoin/models/network.py" line="112"></location> + <location filename="../../../src/cutecoin/models/network.py" line="114"/> <source>Online</source> <translation>Connecté</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="113"></location> + <location filename="../../../src/cutecoin/models/network.py" line="115"/> <source>Offline</source> <translation>Déconnecté</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="114"></location> + <location filename="../../../src/cutecoin/models/network.py" line="116"/> <source>Unsynchronized</source> <translation>Désynchronisé</translation> </message> <message> - <location filename="../../../src/cutecoin/models/network.py" line="115"></location> + <location filename="../../../src/cutecoin/models/network.py" line="117"/> <source>Corrupted</source> <translation>Corrompu</translation> </message> @@ -1476,22 +1550,22 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>Node</name> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="285"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="285"/> <source>Informations</source> <translation type="obsolete">Informations</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="289"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="289"/> <source>Add as contact</source> <translation type="obsolete">Ajouter comme contact</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"/> <source>Send money</source> - <translation type="obsolete">Envoyer de l'argent</translation> + <translation type="obsolete">Envoyer de l'argent</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="297"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="297"/> <source>Certify identity</source> <translation type="obsolete">Certifier cette identité</translation> </message> @@ -1499,111 +1573,136 @@ Revoking your UID can only success if it is not already validated by the network <context> <name>PasswordAskerDialog</name> <message> - <location filename="../../ui/password_asker.ui" line="14"></location> + <location filename="../../ui/password_asker.ui" line="14"/> <source>Password</source> <translation>Mot de passe</translation> </message> <message> - <location filename="../../ui/password_asker.ui" line="23"></location> + <location filename="../../ui/password_asker.ui" line="23"/> <source>Please enter your account password</source> <translation>Veuillez entrer le mot de passe de votre compte</translation> </message> <message> - <location filename="../../ui/password_asker.ui" line="32"></location> + <location filename="../../ui/password_asker.ui" line="32"/> <source>Remember my password during this session</source> <translation>Sauvegarder le mot de passe durant cette session</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"></location> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> <source>Bad password</source> <translation>Mauvais mot de passe</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"></location> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> <source>Non printable characters in password</source> <translation>Caractères invisibles présents dans le mot de passe</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"></location> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> <source>Failed to get private key</source> - <translation>Echec d'ouverture de la clé privée</translation> + <translation>Echec d'ouverture de la clé privée</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"></location> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> <source>Wrong password typed. Cannot open the private key</source> - <translation>Mauvais mot de passe. Impossible d'ouvrir votre clé privée</translation> + <translation>Mauvais mot de passe. Impossible d'ouvrir votre clé privée</translation> </message> </context> <context> <name>PreferencesDialog</name> <message> - <location filename="../../ui/preferences.ui" line="22"></location> + <location filename="../../ui/preferences.ui" line="108"/> <source>Default account</source> <translation>Compte par défaut</translation> </message> <message> - <location filename="../../ui/preferences.ui" line="36"></location> + <location filename="../../ui/preferences.ui" line="36"/> <source>Default referential</source> - <translation>Référentiel par défaut</translation> + <translation type="obsolete">Référentiel par défaut</translation> </message> <message> - <location filename="../../ui/preferences.ui" line="50"></location> + <location filename="../../ui/preferences.ui" line="205"/> <source>Language</source> <translation>Langue</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/preferences.py" line="45"></location> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> <source>Preferences</source> <translation>Préférences</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/preferences.py" line="45"></location> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> <source>A restart is needed to apply your new preferences.</source> <translation>Vous devez redémarrer Cutecoin pour appliquer vos nouvelles préférences.</translation> </message> + <message> + <location filename="../../ui/preferences.ui" line="122"/> + <source>Default &referential</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="159"/> + <source>Enable expert mode</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="191"/> + <source>Digits after commas </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="239"/> + <source>Maximize Window at Startup</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="266"/> + <source>Enable notifications</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>ProcessConfigureAccount</name> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="165"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="165"/> <source>New account</source> <translation>Nouveau compte</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="172"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="172"/> <source>Configure {0}</source> <translation>Configurer {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="186"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="187"/> <source>Ok</source> <translation>Ok</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="207"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> <source>Public key</source> <translation>Clé publique</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="207"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> <source>These parameters pubkeys are : {0}</source> <translation>Les paramètres de cette clé publique sont : {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="256"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="248"/> <source>Error</source> <translation>Erreur</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="233"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> <source>Warning</source> <translation>Attention</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="233"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> <source>This action will delete your account locally. Please note your key parameters (salt and password) if you wish to recover it later. -Your account won't be removed from the networks it joined. +Your account won't be removed from the networks it joined. Are you sure ?</source> <translation>Cette action supprimera votre compte localement. Veuillez noter les paramètres de votre clé (salage et mot de passe) si vous souhaitez le récupérer plus tard. @@ -1614,63 +1713,78 @@ Votre compte ne sera pas supprimer des réseaux rejoins. <context> <name>ProcessConfigureCommunity</name> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="117"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="126"/> <source>Configure community {0}</source> <translation>Configurer la communauté {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="120"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="129"/> <source>Add a community</source> <translation>Ajouter une communauté</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> <source>Error</source> <translation>Erreur</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="187"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="192"/> <source>Delete</source> <translation>Supprimer</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="198"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> <source>Pubkey not found</source> <translation>Clé publique introuvable</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="198"></location> - <source>The public key of your account wasn't found in the community. : + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> + <source>The public key of your account wasn't found in the community. : {0} Would you like to publish the key ?</source> - <translation>La clé publique de votre compte n'a pas été trouvée dans la communauté : + <translation>La clé publique de votre compte n'a pas été trouvée dans la communauté : {0} Souhaitez-vous publier votre clé publique ?</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="209"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="209"/> <source>Pubkey publishing error</source> - <translation>Erreur de publication</translation> + <translation type="obsolete">Erreur de publication</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="212"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="212"/> <source>Network error</source> - <translation>Erreur réseau</translation> + <translation type="obsolete">Erreur réseau</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="212"></location> - <source>Couldn't connect to network : {0}</source> - <translation>Impossible de se connecter au réseau : {0}</translation> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="212"/> + <source>Couldn't connect to network : {0}</source> + <translation type="obsolete">Impossible de se connecter au réseau : {0}</translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>UID Publishing</source> + <translation type="unfinished">Publication de l'UID</translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>Success publishing your UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> </message> </context> <context> <name>Toast</name> <message> - <location filename="../../ui/toast.ui" line="14"></location> + <location filename="../../ui/toast.ui" line="14"/> <source>MainWindow</source> <translation></translation> </message> @@ -1678,202 +1792,227 @@ Souhaitez-vous publier votre clé publique ?</translation> <context> <name>TransactionsTabWidget</name> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="121"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="140"/> <source>Actions</source> <translation>Actions</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="134"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="153"/> <source>Send again</source> <translation>Renvoyer</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="139"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="158"/> <source>Cancel</source> <translation>Annuler</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="145"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="164"/> <source>Informations</source> <translation>Informations</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="150"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="169"/> <source>Add as contact</source> <translation>Ajouter comme contact</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="153"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="153"/> <source>Send money to</source> <translation type="obsolete">Envoyer de la monnaie à </translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="159"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="159"/> <source>View in WoT</source> <translation type="obsolete">Voir dans la WoT</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="166"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="185"/> <source>Copy pubkey to clipboard</source> <translation>Copier la clé publique</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> <source>Warning</source> <translation>Attention</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="203"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> <source>Are you sure ? This money transfer will be removed and not sent.</source> <translation>Êtes vous certain ? Le transfer de monnaie sera annulé et non envoyé.</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="104"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="123"/> <source><b>Deposits</b> {:} {:}</source> <translation><b>Crédit</b> {:} {:}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="108"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="127"/> <source><b>Payments</b> {:} {:}</source> <translation><b>Débit</b> {:} {:}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="112"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="131"/> <source><b>Balance</b> {:} {:}</source> <translation><b>Balance</b> {:} {:}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="155"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="174"/> <source>Send money</source> - <translation>Envoyer de l'argent</translation> + <translation>Envoyer de l'argent</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="161"></location> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="180"/> <source>View in Web of Trust</source> <translation>Voir dans la Toile de Confiance</translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="93"/> + <source>Received {0} {1} from {2} transfers</source> + <translation type="unfinished">Reception de {0} {1} dans {2} transfers</translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="97"/> + <source>New transactions received</source> + <translation type="unfinished">Nouveaux transferts reçus</translation> + </message> </context> <context> <name>TransferMoneyDialog</name> <message> - <location filename="../../ui/transfer.ui" line="14"></location> + <location filename="../../ui/transfer.ui" line="14"/> <source>Transfer money</source> <translation>Transfert de monnaie</translation> </message> <message> - <location filename="../../ui/transfer.ui" line="20"></location> + <location filename="../../ui/transfer.ui" line="20"/> <source>Community</source> <translation>Communauté</translation> </message> <message> - <location filename="../../ui/transfer.ui" line="32"></location> + <location filename="../../ui/transfer.ui" line="32"/> <source>Transfer money to</source> <translation>Transférer de la monnaie à </translation> </message> <message> - <location filename="../../ui/transfer.ui" line="40"></location> + <location filename="../../ui/transfer.ui" line="40"/> <source>Contact</source> <translation>Contact</translation> </message> <message> - <location filename="../../ui/transfer.ui" line="61"></location> + <location filename="../../ui/transfer.ui" line="61"/> <source>Recipient public key</source> <translation>Clé publique du receveur</translation> </message> <message> - <location filename="../../ui/transfer.ui" line="80"></location> + <location filename="../../ui/transfer.ui" line="80"/> <source>Key</source> <translation>Clé</translation> </message> <message> - <location filename="../../ui/transfer.ui" line="106"></location> + <location filename="../../ui/transfer.ui" line="106"/> <source>Wallet :</source> <translation>Portefeuille :</translation> </message> <message> - <location filename="../../ui/transfer.ui" line="125"></location> + <location filename="../../ui/transfer.ui" line="125"/> <source>Availalble currency : </source> - <translation>Monnaie disponible : </translation> + <translation>Monnaie disponible :</translation> </message> <message> - <location filename="../../ui/transfer.ui" line="134"></location> + <location filename="../../ui/transfer.ui" line="134"/> <source>Amount :</source> <translation>Montant :</translation> </message> <message> - <location filename="../../ui/transfer.ui" line="144"></location> + <location filename="../../ui/transfer.ui" line="144"/> <source> UD</source> <translation>DU</translation> </message> <message> - <location filename="../../ui/transfer.ui" line="162"></location> + <location filename="../../ui/transfer.ui" line="162"/> <source>Transaction message</source> <translation>Message</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="92"></location> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> <source>Money transfer</source> <translation>Transfert de monnaie</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="64"></location> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> <source>No amount. Please give the transfert amount</source> <translation>Pas de montant. Veuillez entrer un montant</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="78"></location> + <location filename="../../../src/cutecoin/gui/transfer.py" line="78"/> <source>Success transfering {0} {1} to {2}</source> - <translation>Succès lors de l'envoi de {0} {1} pour {2}</translation> + <translation type="obsolete">Succès lors de l'envoi de {0} {1} pour {2}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="83"></location> + <location filename="../../../src/cutecoin/gui/transfer.py" line="83"/> <source>Something wrong happened : {0}</source> - <translation>Une erreur a été rencontrée : {0}</translation> + <translation type="obsolete">Une erreur a été rencontrée : {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="88"></location> + <location filename="../../../src/cutecoin/gui/transfer.py" line="88"/> <source>This transaction could not be sent on this block Please try again later</source> - <translation>Ce transfert ne peut être envoyer sur ce bloc. + <translation type="obsolete">Ce transfert ne peut être envoyer sur ce bloc. Veuillez rééssayer plus tard</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="92"></location> - <source>Couldn't connect to network : {0}</source> - <translation>Impossible de se connecter au réseau : {0}</translation> + <location filename="../../../src/cutecoin/gui/transfer.py" line="92"/> + <source>Couldn't connect to network : {0}</source> + <translation type="obsolete">Impossible de se connecter au réseau : {0}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/transfer.py" line="97"></location> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> <source>Error</source> <translation>Erreur</translation> </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Transfer</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Success sending money to {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>WalletsTab</name> <message> - <location filename="../../ui/wallets_tab.ui" line="14"></location> + <location filename="../../ui/wallets_tab.ui" line="14"/> <source>Form</source> <translation></translation> </message> <message> - <location filename="../../ui/wallets_tab.ui" line="43"></location> + <location filename="../../ui/wallets_tab.ui" line="43"/> <source>Account</source> <translation>Compte</translation> </message> <message> - <location filename="../../ui/wallets_tab.ui" line="52"></location> + <location filename="../../ui/wallets_tab.ui" line="52"/> <source>label_general</source> <translation></translation> </message> <message> - <location filename="../../ui/wallets_tab.ui" line="68"></location> + <location filename="../../ui/wallets_tab.ui" line="68"/> <source>Balance</source> <translation>Solde</translation> </message> <message> - <location filename="../../ui/wallets_tab.ui" line="74"></location> + <location filename="../../ui/wallets_tab.ui" line="74"/> <source>label_balance</source> <translation></translation> </message> @@ -1881,109 +2020,109 @@ Veuillez rééssayer plus tard</translation> <context> <name>WalletsTabWidget</name> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="70"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="70"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> <source>Membership</source> <translation>Statut de membre</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="70"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> <source>Last renewal on {:}, expiration on {:}</source> <translation>Dernier renouvellement le {:}, expire le {:}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source>Not a member</source> <translation>Non-membre</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source> - <table cellpadding="5"> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> - <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> </source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="118"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="118"/> <source>{:} {:} in [{:.2f} - {:}] {:}</source> <translation type="obsolete">{:} {:} compris dans [{:.2f} - {:}] {:}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="176"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="184"/> <source>Rename</source> <translation>Renommer</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="180"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="188"/> <source>Copy pubkey to clipboard</source> <translation>Copier la clé publique</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="185"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="193"/> <source>Transfer to...</source> <translation>Transférer à ...</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source>Your web of trust</source> <translation>Votre toile de confiance</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>Your money share </source> <translation>Votre part de monnaie</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>{:.2f}%</source> <translation></translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>Your part </source> <translation>Votre part</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="173"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="181"/> <source>New Wallet</source> <translation>Nouveau portefeuille</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="88"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> <source>Certified by {:} members; Certifier of {:} members</source> <translation>Certifié par {:} membres; Certifieur de {:} membres</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="118"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="118"/> <source>{:} {:} in [{:.2f} ; {:}] {:}</source> <translation type="obsolete">{:} {:} compris entre [{:.2f} ; {:}] {:}</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="119"></location> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> <source>{:} {:} in [{:} ; {:}] {:}</source> <translation>{:} {:} compris entre [{:} ; {:}] {:}</translation> </message> @@ -1991,17 +2130,17 @@ Veuillez rééssayer plus tard</translation> <context> <name>WalletsTableModel</name> <message> - <location filename="../../../src/cutecoin/models/wallets.py" line="70"></location> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> <source>Name</source> <translation>Nom</translation> </message> <message> - <location filename="../../../src/cutecoin/models/wallets.py" line="70"></location> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> <source>Amount</source> <translation>Montant</translation> </message> <message> - <location filename="../../../src/cutecoin/models/wallets.py" line="70"></location> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> <source>Pubkey</source> <translation>Clé publique</translation> </message> @@ -2009,22 +2148,22 @@ Veuillez rééssayer plus tard</translation> <context> <name>WoT.Node</name> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="288"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="288"/> <source>Informations</source> <translation>Informations</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"/> <source>Add as contact</source> <translation>Ajouter comme contact</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="298"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="298"/> <source>Send money</source> - <translation>Envoyer de l'argent</translation> + <translation>Envoyer de l'argent</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/views/wot.py" line="303"></location> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="303"/> <source>Certify identity</source> <translation>Certifier cette identité</translation> </message> @@ -2032,12 +2171,12 @@ Veuillez rééssayer plus tard</translation> <context> <name>WotTabWidget</name> <message> - <location filename="../../ui/wot_tab.ui" line="14"></location> + <location filename="../../ui/wot_tab.ui" line="14"/> <source>Form</source> <translation></translation> </message> <message> - <location filename="../../ui/wot_tab.ui" line="33"></location> + <location filename="../../ui/wot_tab.ui" line="33"/> <source>Me</source> <translation>Moi</translation> </message> @@ -2045,32 +2184,32 @@ Veuillez rééssayer plus tard</translation> <context> <name>self.config_dialog</name> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="89"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="92"/> <source>Ok</source> <translation>Ok</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="71"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="71"/> <source>Forbidden : salt is too short</source> <translation>Interdit : le sel est trop court</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="75"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="75"/> <source>Forbidden : password is too short</source> <translation>Interdit : Le mot de passe est trop court</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="79"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="79"/> <source>Forbidden : Invalid characters in salt field</source> <translation>Interdit : Caractères invalides dans le sel</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="83"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="83"/> <source>Forbidden : Invalid characters in password field</source> <translation>Interdit : Caractères invalides dans le mot de passe</translation> </message> <message> - <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="89"></location> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="89"/> <source>Error : passwords are different</source> <translation>Erreur : les mots de passes sont différents</translation> </message> @@ -2078,27 +2217,27 @@ Veuillez rééssayer plus tard</translation> <context> <name>transactionsTabWidget</name> <message> - <location filename="../../ui/transactions_tab.ui" line="14"></location> + <location filename="../../ui/transactions_tab.ui" line="14"/> <source>Form</source> <translation></translation> </message> <message> - <location filename="../../ui/transactions_tab.ui" line="37"></location> + <location filename="../../ui/transactions_tab.ui" line="37"/> <source>dd/MM/yyyy</source> <translation></translation> </message> <message> - <location filename="../../ui/transactions_tab.ui" line="100"></location> + <location filename="../../ui/transactions_tab.ui" line="100"/> <source>Balance:</source> <translation>Solde:</translation> </message> <message> - <location filename="../../ui/transactions_tab.ui" line="83"></location> + <location filename="../../ui/transactions_tab.ui" line="83"/> <source>Payment:</source> <translation>Paiements:</translation> </message> <message> - <location filename="../../ui/transactions_tab.ui" line="90"></location> + <location filename="../../ui/transactions_tab.ui" line="90"/> <source>Deposit:</source> <translation>Dépôts:</translation> </message> diff --git a/res/i18n/ts/it_IT.ts b/res/i18n/ts/it_IT.ts new file mode 100644 index 0000000000000000000000000000000000000000..05142f9b6df42336d09a34f3e3309e57e36f964c --- /dev/null +++ b/res/i18n/ts/it_IT.ts @@ -0,0 +1,1873 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="2.0" language="it_IT" sourcelanguage=""> +<context> + <name>AboutPopup</name> + <message> + <location filename="../../ui/about.ui" line="14"/> + <source>About</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/about.ui" line="22"/> + <source>label</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Account</name> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Units</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>UD {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>UD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Q0 {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Quant Z-sum</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>R0 {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Relat Z-sum</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>AccountConfigurationDialog</name> + <message> + <location filename="../../ui/account_cfg.ui" line="14"/> + <source>Add an account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="30"/> + <source>Account parameters</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="51"/> + <source>Account name (uid)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="68"/> + <source>Wallets</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="111"/> + <source>Delete account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="140"/> + <source>Key parameters</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="170"/> + <source>CryptoID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="180"/> + <source>Your password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="193"/> + <source>Please repeat your password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="212"/> + <source>Show public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="242"/> + <source>Communities membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="257"/> + <source>Add a community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="264"/> + <source>Remove selected community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="288"/> + <source>Previous</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="308"/> + <source>Next</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CertificationDialog</name> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> + <source>Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="20"/> + <source>Community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="32"/> + <source>Certify user</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="40"/> + <source>Contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="61"/> + <source>User public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="80"/> + <source>Key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> + <source>Success certifying {0} from {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="77"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="80"/> + <source>Not a member</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CommunityConfigurationDialog</name> + <message> + <location filename="../../ui/community_cfg.ui" line="17"/> + <source>Add a community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="46"/> + <source>Please enter the address of a node :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="61"/> + <source>:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="98"/> + <source>Check node connectivity</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="124"/> + <source>Communities nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="142"/> + <source>Server</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="162"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="183"/> + <source>Previous</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="206"/> + <source>Next</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CommunityTabWidget</name> + <message> + <location filename="../../ui/community_tab.ui" line="17"/> + <source>communityTabWidget</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="40"/> + <source>Identities</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="53"/> + <source>Research a pubkey, an uid...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="60"/> + <source>Search</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="118"/> + <source>Quality : </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="125"/> + <source>Publish UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="132"/> + <source>Revoke UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="351"/> + <source>Renew membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="146"/> + <source>Send leaving demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="59"/> + <source>Web of Trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="60"/> + <source>Members</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="63"/> + <source>Direct connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> + <source>Membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> + <source>Success sending Membership demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Revoke</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Success sending Revoke demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Self Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Success sending Self Certification document</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="104"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="107"/> + <source>Add as contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="111"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="115"/> + <source>Certify identity</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="119"/> + <source>View in Web of Trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="196"/> + <source>Are you sure ? +Sending a leaving demand cannot be canceled. +The process to join back the community later will have to be done again.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="209"/> + <source>Are you sure ? +Publishing your UID can be canceled by Revoke UID.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> + <source>UID Publishing</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> + <source>Success publishing your UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="223"/> + <source>Publish UID error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> + <source>Network error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> + <source>Couldn't connect to network : {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> + <source>Are you sure ? +Revoking your UID can only success if it is not already validated by the network.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="358"/> + <source>Send membership demand</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ConfigureContactDialog</name> + <message> + <location filename="../../ui/contact.ui" line="14"/> + <source>Add a contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/contact.ui" line="22"/> + <source>Name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/contact.ui" line="36"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/contact.py" line="58"/> + <source>Contact already exists</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CreateWalletDialog</name> + <message> + <location filename="../../ui/create_wallet.ui" line="14"/> + <source>Create a new wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/create_wallet.ui" line="45"/> + <source>Wallet name :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/create_wallet.ui" line="83"/> + <source>Previous</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/create_wallet.ui" line="103"/> + <source>Next</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CurrencyTabWidget</name> + <message> + <location filename="../../ui/currency_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="43"/> + <source>Warning : Your membership is expiring soon.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="45"/> + <source>Warning : Your could miss certifications soon.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="72"/> + <source>Wallets</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="76"/> + <source>Transactions</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="80"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="84"/> + <source>Community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="88"/> + <source>Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> + <source>Membership expiration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> + <source><b>Warning : Membership expiration in {0} days</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> + <source>Certifications number</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> + <source><b>Warning : You are certified by only {0} persons, need {1}</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="161"/> + <source> Block {0}</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>DialogMember</name> + <message> + <location filename="../../ui/member.ui" line="14"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/member.ui" line="34"/> + <source>Member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/member.ui" line="65"/> + <source>uid</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/member.ui" line="72"/> + <source>properties</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>HistoryTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Date</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>UID/Public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Payment</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Deposit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Comment</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>HomeScreenWidget</name> + <message> + <location filename="../../ui/homescreen.ui" line="20"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="49"/> + <source><html><head/><body><p><br/></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="67"/> + <source>Create a new account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="100"/> + <source>Import an existing account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="127"/> + <source>Get to know more about ucoin</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="35"/> + <source>Please get the latest release {version}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="39"/> + <source> + <h1>Welcome to Cutecoin {version}</h1> + <h2>{version_info}</h2> + <h3><a href={version_url}>Download link</a></h3> + </source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>IdentitiesTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="84"/> + <source>UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="85"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="86"/> + <source>Renewed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="87"/> + <source>Expiration</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ImportAccountDialog</name> + <message> + <location filename="../../ui/import_account.ui" line="14"/> + <source>Import an account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/import_account.ui" line="25"/> + <source>Import a file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/import_account.ui" line="36"/> + <source>Name of the account :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="34"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> + <source>Account import</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> + <source>Account imported succefully !</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> + <source>Import an account file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> + <source>All account files (*.acc)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="58"/> + <source>Please enter a name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="63"/> + <source>Name already exists</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="67"/> + <source>File is not an account format</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>InformationsTabWidget</name> + <message> + <location filename="../../ui/informations_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="52"/> + <source>General</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="61"/> + <source>label_general</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="77"/> + <source>Rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="83"/> + <source>label_rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="96"/> + <source>Money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="102"/> + <source>label_money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="115"/> + <source>WoT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="121"/> + <source>label_wot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Universal Dividend UD(t) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass M(t-1) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Members N(t)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass per member M(t-1)/N(t) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Actual growth c = UD(t)/[M(t-1)/N(t)]</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Last UD date and time (t)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Next UD date and time (t+1)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="202"/> + <source>No Universal Dividend created yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>{:2.0%} / {:} days</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>Fundamental growth (c) / Delta time (dt)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>UD(t+1) = MAX { UD(t) ; c &#215; M(t) / N(t+1) }</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>Universal Dividend (formula)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>{:} = MAX {{ {:} {:} ; {:2.0%} &#215; {:} {:} / {:} }}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>Universal Dividend (computed)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:2.0%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:2.0%}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Fundamental growth (c)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Initial Universal Dividend UD(0) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Time period (dt) in days (86400 seconds) between two UD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Number of blocks used for calculating median time</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The average time in seconds for writing 1 block (wished time)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The number of blocks required to evaluate again PoWMin value</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The number of previous blocks to check for personalized difficulty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The percent of previous issuers to reach for personalized difficulty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Minimum delay between 2 identical certifications (in days)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Maximum age of a valid signature (in days)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Minimum quantity of signatures to be part of the WoT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Minimum quantity of valid made certifications to be part of the WoT for distance rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Maximum age of a valid membership (in days)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Maximum distance between each WoT member and a newcomer</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>MainWindow</name> + <message> + <location filename="../../ui/mainwindow.ui" line="41"/> + <source>Fi&le</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="142"/> + <source>Account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="55"/> + <source>&Contacts</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="61"/> + <source>&Open</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="76"/> + <source>&Help</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="87"/> + <source>Manage accounts</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="92"/> + <source>Configure trustable nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="97"/> + <source>&Add a contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="117"/> + <source>Send a message</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="122"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="127"/> + <source>Remove contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="132"/> + <source>Save</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="137"/> + <source>&Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="147"/> + <source>&Transfer money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="152"/> + <source>&Configure</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="157"/> + <source>&Import</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="162"/> + <source>&Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="167"/> + <source>&Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="172"/> + <source>&Set as default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="177"/> + <source>A&bout</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="182"/> + <source>&Preferences</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="187"/> + <source>&Add account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="190"/> + <source>Latest release : {version}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="197"/> + <source>Download link</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="204"/> + <source> + <h1>Cutecoin</h1> + + <p>Python/Qt uCoin client</p> + + <p>Version : {:}</p> + {new_version_text} + + <p>License : MIT</p> + + <p><b>Authors</b></p> + + <p>inso</p> + <p>vit</p> + <p>canercandan</p> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="229"/> + <source>Please get the latest release {version}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="272"/> + <source>Edit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="275"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="291"/> + <source>CuteCoin {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="316"/> + <source>CuteCoin {0} - Account : {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="336"/> + <source>Export an account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="337"/> + <source>All account files (*.acc)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="338"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>MemberDialog</name> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="31"/> + <source>not a member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="43"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> + <source>Public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> + <source>Join date</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="65"/> + <source><tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="56"/> + <source>Distance</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="61"/> + <source>Path</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NetworkFilterProxyModel</name> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="41"/> + <source>Address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="42"/> + <source>Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="43"/> + <source>Block</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="44"/> + <source>UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="45"/> + <source>Member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="46"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="47"/> + <source>Software</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="48"/> + <source>Version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> + <source>yes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> + <source>no</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> + <source>offline</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NetworkTabWidget</name> + <message> + <location filename="../../ui/network_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="61"/> + <source>Unset root node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="67"/> + <source>Set as root node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="73"/> + <source>Open in browser</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NetworkTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="114"/> + <source>Online</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="115"/> + <source>Offline</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="116"/> + <source>Unsynchronized</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="117"/> + <source>Corrupted</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PasswordAskerDialog</name> + <message> + <location filename="../../ui/password_asker.ui" line="14"/> + <source>Password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/password_asker.ui" line="23"/> + <source>Please enter your account password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/password_asker.ui" line="32"/> + <source>Remember my password during this session</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> + <source>Bad password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> + <source>Non printable characters in password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> + <source>Failed to get private key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> + <source>Wrong password typed. Cannot open the private key</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> + <source>Preferences</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="108"/> + <source>Default account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="122"/> + <source>Default &referential</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="159"/> + <source>Enable expert mode</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="191"/> + <source>Digits after commas </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="205"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="239"/> + <source>Maximize Window at Startup</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="266"/> + <source>Enable notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> + <source>A restart is needed to apply your new preferences.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessConfigureAccount</name> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="165"/> + <source>New account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="172"/> + <source>Configure {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="187"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> + <source>Public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> + <source>These parameters pubkeys are : {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> + <source>This action will delete your account locally. +Please note your key parameters (salt and password) if you wish to recover it later. +Your account won't be removed from the networks it joined. +Are you sure ?</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="248"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessConfigureCommunity</name> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="126"/> + <source>Configure community {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="129"/> + <source>Add a community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="192"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>UID Publishing</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>Success publishing your UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> + <source>Pubkey not found</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> + <source>The public key of your account wasn't found in the community. : + +{0} + +Would you like to publish the key ?</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Toast</name> + <message> + <location filename="../../ui/toast.ui" line="14"/> + <source>MainWindow</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>TransactionsTabWidget</name> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="93"/> + <source>Received {0} {1} from {2} transfers</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="97"/> + <source>New transactions received</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="123"/> + <source><b>Deposits</b> {:} {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="127"/> + <source><b>Payments</b> {:} {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="131"/> + <source><b>Balance</b> {:} {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="140"/> + <source>Actions</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="153"/> + <source>Send again</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="158"/> + <source>Cancel</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="164"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="169"/> + <source>Add as contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="174"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="180"/> + <source>View in Web of Trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="185"/> + <source>Copy pubkey to clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> + <source>Are you sure ? +This money transfer will be removed and not sent.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>TransferMoneyDialog</name> + <message> + <location filename="../../ui/transfer.ui" line="14"/> + <source>Transfer money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="20"/> + <source>Community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="32"/> + <source>Transfer money to</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="40"/> + <source>Contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="61"/> + <source>Recipient public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="80"/> + <source>Key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="106"/> + <source>Wallet :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="125"/> + <source>Availalble currency : </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="134"/> + <source>Amount :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="144"/> + <source> UD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="162"/> + <source>Transaction message</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> + <source>Money transfer</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> + <source>No amount. Please give the transfert amount</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Transfer</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Success sending money to {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WalletsTab</name> + <message> + <location filename="../../ui/wallets_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="43"/> + <source>Account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="52"/> + <source>label_general</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="68"/> + <source>Balance</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="74"/> + <source>label_balance</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WalletsTabWidget</name> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> + <source>Membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> + <source>Last renewal on {:}, expiration on {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source>Your web of trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source>Certified by {:} members; Certifier of {:} members</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source>Not a member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>Your money share </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>{:.2f}%</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>Your part </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>{:} {:} in [{:} ; {:}] {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="181"/> + <source>New Wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="184"/> + <source>Rename</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="188"/> + <source>Copy pubkey to clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="193"/> + <source>Transfer to...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WalletsTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> + <source>Name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> + <source>Amount</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WoT.Node</name> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="288"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"/> + <source>Add as contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="298"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="303"/> + <source>Certify identity</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WotTabWidget</name> + <message> + <location filename="../../ui/wot_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wot_tab.ui" line="33"/> + <source>Me</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>self.config_dialog</name> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="92"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="71"/> + <source>Forbidden : salt is too short</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="75"/> + <source>Forbidden : password is too short</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="79"/> + <source>Forbidden : Invalid characters in salt field</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="83"/> + <source>Forbidden : Invalid characters in password field</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="89"/> + <source>Error : passwords are different</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>transactionsTabWidget</name> + <message> + <location filename="../../ui/transactions_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="37"/> + <source>dd/MM/yyyy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="83"/> + <source>Payment:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="90"/> + <source>Deposit:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="100"/> + <source>Balance:</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/res/i18n/ts/pl_PL.ts b/res/i18n/ts/pl_PL.ts new file mode 100644 index 0000000000000000000000000000000000000000..d73d4aae3e179502508067a92e121d3e4296ec71 --- /dev/null +++ b/res/i18n/ts/pl_PL.ts @@ -0,0 +1,1873 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="2.0" language="pl_PL" sourcelanguage=""> +<context> + <name>AboutPopup</name> + <message> + <location filename="../../ui/about.ui" line="14"/> + <source>About</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/about.ui" line="22"/> + <source>label</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Account</name> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Units</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>UD {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>UD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Q0 {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Quant Z-sum</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>R0 {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Relat Z-sum</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>AccountConfigurationDialog</name> + <message> + <location filename="../../ui/account_cfg.ui" line="14"/> + <source>Add an account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="30"/> + <source>Account parameters</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="51"/> + <source>Account name (uid)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="68"/> + <source>Wallets</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="111"/> + <source>Delete account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="140"/> + <source>Key parameters</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="170"/> + <source>CryptoID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="180"/> + <source>Your password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="193"/> + <source>Please repeat your password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="212"/> + <source>Show public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="242"/> + <source>Communities membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="257"/> + <source>Add a community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="264"/> + <source>Remove selected community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="288"/> + <source>Previous</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="308"/> + <source>Next</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CertificationDialog</name> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> + <source>Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="20"/> + <source>Community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="32"/> + <source>Certify user</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="40"/> + <source>Contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="61"/> + <source>User public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="80"/> + <source>Key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> + <source>Success certifying {0} from {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="77"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="80"/> + <source>Not a member</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CommunityConfigurationDialog</name> + <message> + <location filename="../../ui/community_cfg.ui" line="17"/> + <source>Add a community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="46"/> + <source>Please enter the address of a node :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="61"/> + <source>:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="98"/> + <source>Check node connectivity</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="124"/> + <source>Communities nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="142"/> + <source>Server</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="162"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="183"/> + <source>Previous</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="206"/> + <source>Next</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CommunityTabWidget</name> + <message> + <location filename="../../ui/community_tab.ui" line="17"/> + <source>communityTabWidget</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="40"/> + <source>Identities</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="53"/> + <source>Research a pubkey, an uid...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="60"/> + <source>Search</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="118"/> + <source>Quality : </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="125"/> + <source>Publish UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="132"/> + <source>Revoke UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="351"/> + <source>Renew membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="146"/> + <source>Send leaving demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="59"/> + <source>Web of Trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="60"/> + <source>Members</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="63"/> + <source>Direct connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> + <source>Membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> + <source>Success sending Membership demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Revoke</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Success sending Revoke demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Self Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Success sending Self Certification document</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="104"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="107"/> + <source>Add as contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="111"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="115"/> + <source>Certify identity</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="119"/> + <source>View in Web of Trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="196"/> + <source>Are you sure ? +Sending a leaving demand cannot be canceled. +The process to join back the community later will have to be done again.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="209"/> + <source>Are you sure ? +Publishing your UID can be canceled by Revoke UID.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> + <source>UID Publishing</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> + <source>Success publishing your UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="223"/> + <source>Publish UID error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> + <source>Network error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> + <source>Couldn't connect to network : {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> + <source>Are you sure ? +Revoking your UID can only success if it is not already validated by the network.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="358"/> + <source>Send membership demand</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ConfigureContactDialog</name> + <message> + <location filename="../../ui/contact.ui" line="14"/> + <source>Add a contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/contact.ui" line="22"/> + <source>Name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/contact.ui" line="36"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/contact.py" line="58"/> + <source>Contact already exists</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CreateWalletDialog</name> + <message> + <location filename="../../ui/create_wallet.ui" line="14"/> + <source>Create a new wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/create_wallet.ui" line="45"/> + <source>Wallet name :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/create_wallet.ui" line="83"/> + <source>Previous</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/create_wallet.ui" line="103"/> + <source>Next</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CurrencyTabWidget</name> + <message> + <location filename="../../ui/currency_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="43"/> + <source>Warning : Your membership is expiring soon.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="45"/> + <source>Warning : Your could miss certifications soon.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="72"/> + <source>Wallets</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="76"/> + <source>Transactions</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="80"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="84"/> + <source>Community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="88"/> + <source>Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> + <source>Membership expiration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> + <source><b>Warning : Membership expiration in {0} days</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> + <source>Certifications number</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> + <source><b>Warning : You are certified by only {0} persons, need {1}</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="161"/> + <source> Block {0}</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>DialogMember</name> + <message> + <location filename="../../ui/member.ui" line="14"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/member.ui" line="34"/> + <source>Member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/member.ui" line="65"/> + <source>uid</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/member.ui" line="72"/> + <source>properties</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>HistoryTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Date</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>UID/Public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Payment</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Deposit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Comment</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>HomeScreenWidget</name> + <message> + <location filename="../../ui/homescreen.ui" line="20"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="49"/> + <source><html><head/><body><p><br/></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="67"/> + <source>Create a new account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="100"/> + <source>Import an existing account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="127"/> + <source>Get to know more about ucoin</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="35"/> + <source>Please get the latest release {version}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="39"/> + <source> + <h1>Welcome to Cutecoin {version}</h1> + <h2>{version_info}</h2> + <h3><a href={version_url}>Download link</a></h3> + </source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>IdentitiesTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="84"/> + <source>UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="85"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="86"/> + <source>Renewed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="87"/> + <source>Expiration</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ImportAccountDialog</name> + <message> + <location filename="../../ui/import_account.ui" line="14"/> + <source>Import an account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/import_account.ui" line="25"/> + <source>Import a file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/import_account.ui" line="36"/> + <source>Name of the account :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="34"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> + <source>Account import</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> + <source>Account imported succefully !</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> + <source>Import an account file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> + <source>All account files (*.acc)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="58"/> + <source>Please enter a name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="63"/> + <source>Name already exists</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="67"/> + <source>File is not an account format</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>InformationsTabWidget</name> + <message> + <location filename="../../ui/informations_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="52"/> + <source>General</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="61"/> + <source>label_general</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="77"/> + <source>Rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="83"/> + <source>label_rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="96"/> + <source>Money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="102"/> + <source>label_money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="115"/> + <source>WoT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="121"/> + <source>label_wot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Universal Dividend UD(t) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass M(t-1) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Members N(t)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass per member M(t-1)/N(t) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Actual growth c = UD(t)/[M(t-1)/N(t)]</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Last UD date and time (t)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Next UD date and time (t+1)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="202"/> + <source>No Universal Dividend created yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>{:2.0%} / {:} days</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>Fundamental growth (c) / Delta time (dt)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>UD(t+1) = MAX { UD(t) ; c &#215; M(t) / N(t+1) }</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>Universal Dividend (formula)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>{:} = MAX {{ {:} {:} ; {:2.0%} &#215; {:} {:} / {:} }}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>Universal Dividend (computed)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:2.0%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:2.0%}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Fundamental growth (c)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Initial Universal Dividend UD(0) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Time period (dt) in days (86400 seconds) between two UD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Number of blocks used for calculating median time</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The average time in seconds for writing 1 block (wished time)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The number of blocks required to evaluate again PoWMin value</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The number of previous blocks to check for personalized difficulty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The percent of previous issuers to reach for personalized difficulty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Minimum delay between 2 identical certifications (in days)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Maximum age of a valid signature (in days)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Minimum quantity of signatures to be part of the WoT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Minimum quantity of valid made certifications to be part of the WoT for distance rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Maximum age of a valid membership (in days)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Maximum distance between each WoT member and a newcomer</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>MainWindow</name> + <message> + <location filename="../../ui/mainwindow.ui" line="41"/> + <source>Fi&le</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="142"/> + <source>Account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="55"/> + <source>&Contacts</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="61"/> + <source>&Open</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="76"/> + <source>&Help</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="87"/> + <source>Manage accounts</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="92"/> + <source>Configure trustable nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="97"/> + <source>&Add a contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="117"/> + <source>Send a message</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="122"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="127"/> + <source>Remove contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="132"/> + <source>Save</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="137"/> + <source>&Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="147"/> + <source>&Transfer money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="152"/> + <source>&Configure</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="157"/> + <source>&Import</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="162"/> + <source>&Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="167"/> + <source>&Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="172"/> + <source>&Set as default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="177"/> + <source>A&bout</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="182"/> + <source>&Preferences</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="187"/> + <source>&Add account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="190"/> + <source>Latest release : {version}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="197"/> + <source>Download link</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="204"/> + <source> + <h1>Cutecoin</h1> + + <p>Python/Qt uCoin client</p> + + <p>Version : {:}</p> + {new_version_text} + + <p>License : MIT</p> + + <p><b>Authors</b></p> + + <p>inso</p> + <p>vit</p> + <p>canercandan</p> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="229"/> + <source>Please get the latest release {version}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="272"/> + <source>Edit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="275"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="291"/> + <source>CuteCoin {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="316"/> + <source>CuteCoin {0} - Account : {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="336"/> + <source>Export an account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="337"/> + <source>All account files (*.acc)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="338"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>MemberDialog</name> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="31"/> + <source>not a member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="43"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> + <source>Public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> + <source>Join date</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="65"/> + <source><tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="56"/> + <source>Distance</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="61"/> + <source>Path</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NetworkFilterProxyModel</name> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="41"/> + <source>Address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="42"/> + <source>Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="43"/> + <source>Block</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="44"/> + <source>UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="45"/> + <source>Member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="46"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="47"/> + <source>Software</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="48"/> + <source>Version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> + <source>yes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> + <source>no</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> + <source>offline</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NetworkTabWidget</name> + <message> + <location filename="../../ui/network_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="61"/> + <source>Unset root node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="67"/> + <source>Set as root node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="73"/> + <source>Open in browser</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NetworkTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="114"/> + <source>Online</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="115"/> + <source>Offline</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="116"/> + <source>Unsynchronized</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="117"/> + <source>Corrupted</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PasswordAskerDialog</name> + <message> + <location filename="../../ui/password_asker.ui" line="14"/> + <source>Password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/password_asker.ui" line="23"/> + <source>Please enter your account password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/password_asker.ui" line="32"/> + <source>Remember my password during this session</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> + <source>Bad password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> + <source>Non printable characters in password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> + <source>Failed to get private key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> + <source>Wrong password typed. Cannot open the private key</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> + <source>Preferences</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="108"/> + <source>Default account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="122"/> + <source>Default &referential</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="159"/> + <source>Enable expert mode</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="191"/> + <source>Digits after commas </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="205"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="239"/> + <source>Maximize Window at Startup</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="266"/> + <source>Enable notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> + <source>A restart is needed to apply your new preferences.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessConfigureAccount</name> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="165"/> + <source>New account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="172"/> + <source>Configure {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="187"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> + <source>Public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> + <source>These parameters pubkeys are : {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> + <source>This action will delete your account locally. +Please note your key parameters (salt and password) if you wish to recover it later. +Your account won't be removed from the networks it joined. +Are you sure ?</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="248"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessConfigureCommunity</name> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="126"/> + <source>Configure community {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="129"/> + <source>Add a community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="192"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>UID Publishing</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>Success publishing your UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> + <source>Pubkey not found</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> + <source>The public key of your account wasn't found in the community. : + +{0} + +Would you like to publish the key ?</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Toast</name> + <message> + <location filename="../../ui/toast.ui" line="14"/> + <source>MainWindow</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>TransactionsTabWidget</name> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="93"/> + <source>Received {0} {1} from {2} transfers</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="97"/> + <source>New transactions received</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="123"/> + <source><b>Deposits</b> {:} {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="127"/> + <source><b>Payments</b> {:} {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="131"/> + <source><b>Balance</b> {:} {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="140"/> + <source>Actions</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="153"/> + <source>Send again</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="158"/> + <source>Cancel</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="164"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="169"/> + <source>Add as contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="174"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="180"/> + <source>View in Web of Trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="185"/> + <source>Copy pubkey to clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> + <source>Are you sure ? +This money transfer will be removed and not sent.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>TransferMoneyDialog</name> + <message> + <location filename="../../ui/transfer.ui" line="14"/> + <source>Transfer money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="20"/> + <source>Community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="32"/> + <source>Transfer money to</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="40"/> + <source>Contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="61"/> + <source>Recipient public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="80"/> + <source>Key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="106"/> + <source>Wallet :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="125"/> + <source>Availalble currency : </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="134"/> + <source>Amount :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="144"/> + <source> UD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="162"/> + <source>Transaction message</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> + <source>Money transfer</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> + <source>No amount. Please give the transfert amount</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Transfer</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Success sending money to {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WalletsTab</name> + <message> + <location filename="../../ui/wallets_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="43"/> + <source>Account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="52"/> + <source>label_general</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="68"/> + <source>Balance</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="74"/> + <source>label_balance</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WalletsTabWidget</name> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> + <source>Membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> + <source>Last renewal on {:}, expiration on {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source>Your web of trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source>Certified by {:} members; Certifier of {:} members</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source>Not a member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>Your money share </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>{:.2f}%</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>Your part </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>{:} {:} in [{:} ; {:}] {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="181"/> + <source>New Wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="184"/> + <source>Rename</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="188"/> + <source>Copy pubkey to clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="193"/> + <source>Transfer to...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WalletsTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> + <source>Name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> + <source>Amount</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WoT.Node</name> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="288"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"/> + <source>Add as contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="298"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="303"/> + <source>Certify identity</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WotTabWidget</name> + <message> + <location filename="../../ui/wot_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wot_tab.ui" line="33"/> + <source>Me</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>self.config_dialog</name> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="92"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="71"/> + <source>Forbidden : salt is too short</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="75"/> + <source>Forbidden : password is too short</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="79"/> + <source>Forbidden : Invalid characters in salt field</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="83"/> + <source>Forbidden : Invalid characters in password field</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="89"/> + <source>Error : passwords are different</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>transactionsTabWidget</name> + <message> + <location filename="../../ui/transactions_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="37"/> + <source>dd/MM/yyyy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="83"/> + <source>Payment:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="90"/> + <source>Deposit:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="100"/> + <source>Balance:</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/res/i18n/ts/ru_RU.ts b/res/i18n/ts/ru_RU.ts new file mode 100644 index 0000000000000000000000000000000000000000..3580270d63763f4d78e84787d7b116d915e18954 --- /dev/null +++ b/res/i18n/ts/ru_RU.ts @@ -0,0 +1,1873 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="2.0" language="ru_RU" sourcelanguage=""> +<context> + <name>AboutPopup</name> + <message> + <location filename="../../ui/about.ui" line="14"/> + <source>About</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/about.ui" line="22"/> + <source>label</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Account</name> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Units</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>UD {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>UD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Q0 {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Quant Z-sum</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>R0 {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/core/account.py" line="112"/> + <source>Relat Z-sum</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>AccountConfigurationDialog</name> + <message> + <location filename="../../ui/account_cfg.ui" line="14"/> + <source>Add an account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="30"/> + <source>Account parameters</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="51"/> + <source>Account name (uid)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="68"/> + <source>Wallets</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="111"/> + <source>Delete account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="140"/> + <source>Key parameters</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="170"/> + <source>CryptoID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="180"/> + <source>Your password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="193"/> + <source>Please repeat your password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="212"/> + <source>Show public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="242"/> + <source>Communities membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="257"/> + <source>Add a community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="264"/> + <source>Remove selected community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="288"/> + <source>Previous</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/account_cfg.ui" line="308"/> + <source>Next</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CertificationDialog</name> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> + <source>Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="20"/> + <source>Community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="32"/> + <source>Certify user</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="40"/> + <source>Contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="61"/> + <source>User public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/certification.ui" line="80"/> + <source>Key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="56"/> + <source>Success certifying {0} from {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="68"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="77"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/certification.py" line="80"/> + <source>Not a member</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CommunityConfigurationDialog</name> + <message> + <location filename="../../ui/community_cfg.ui" line="17"/> + <source>Add a community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="46"/> + <source>Please enter the address of a node :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="61"/> + <source>:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="98"/> + <source>Check node connectivity</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="124"/> + <source>Communities nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="142"/> + <source>Server</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="162"/> + <source>Add</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="183"/> + <source>Previous</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_cfg.ui" line="206"/> + <source>Next</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CommunityTabWidget</name> + <message> + <location filename="../../ui/community_tab.ui" line="17"/> + <source>communityTabWidget</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="40"/> + <source>Identities</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="53"/> + <source>Research a pubkey, an uid...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="60"/> + <source>Search</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="118"/> + <source>Quality : </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="125"/> + <source>Publish UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="132"/> + <source>Revoke UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="351"/> + <source>Renew membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/community_tab.ui" line="146"/> + <source>Send leaving demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="59"/> + <source>Web of Trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="60"/> + <source>Members</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="63"/> + <source>Direct connections</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> + <source>Membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="78"/> + <source>Success sending Membership demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Revoke</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="84"/> + <source>Success sending Revoke demand</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Self Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="90"/> + <source>Success sending Self Certification document</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="104"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="107"/> + <source>Add as contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="111"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="115"/> + <source>Certify identity</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="119"/> + <source>View in Web of Trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="196"/> + <source>Are you sure ? +Sending a leaving demand cannot be canceled. +The process to join back the community later will have to be done again.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="209"/> + <source>Are you sure ? +Publishing your UID can be canceled by Revoke UID.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> + <source>UID Publishing</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="220"/> + <source>Success publishing your UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="223"/> + <source>Publish UID error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> + <source>Network error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="226"/> + <source>Couldn't connect to network : {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="235"/> + <source>Are you sure ? +Revoking your UID can only success if it is not already validated by the network.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/community_tab.py" line="358"/> + <source>Send membership demand</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ConfigureContactDialog</name> + <message> + <location filename="../../ui/contact.ui" line="14"/> + <source>Add a contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/contact.ui" line="22"/> + <source>Name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/contact.ui" line="36"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/contact.py" line="58"/> + <source>Contact already exists</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CreateWalletDialog</name> + <message> + <location filename="../../ui/create_wallet.ui" line="14"/> + <source>Create a new wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/create_wallet.ui" line="45"/> + <source>Wallet name :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/create_wallet.ui" line="83"/> + <source>Previous</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/create_wallet.ui" line="103"/> + <source>Next</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>CurrencyTabWidget</name> + <message> + <location filename="../../ui/currency_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="43"/> + <source>Warning : Your membership is expiring soon.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="45"/> + <source>Warning : Your could miss certifications soon.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="72"/> + <source>Wallets</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="76"/> + <source>Transactions</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="80"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="84"/> + <source>Community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="88"/> + <source>Network</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> + <source>Membership expiration</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="123"/> + <source><b>Warning : Membership expiration in {0} days</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> + <source>Certifications number</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="130"/> + <source><b>Warning : You are certified by only {0} persons, need {1}</b></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/currency_tab.py" line="161"/> + <source> Block {0}</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>DialogMember</name> + <message> + <location filename="../../ui/member.ui" line="14"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/member.ui" line="34"/> + <source>Member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/member.ui" line="65"/> + <source>uid</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/member.ui" line="72"/> + <source>properties</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>HistoryTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Date</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>UID/Public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Payment</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Deposit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/txhistory.py" line="183"/> + <source>Comment</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>HomeScreenWidget</name> + <message> + <location filename="../../ui/homescreen.ui" line="20"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="49"/> + <source><html><head/><body><p><br/></p></body></html></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="67"/> + <source>Create a new account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="100"/> + <source>Import an existing account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/homescreen.ui" line="127"/> + <source>Get to know more about ucoin</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="35"/> + <source>Please get the latest release {version}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/homescreen.py" line="39"/> + <source> + <h1>Welcome to Cutecoin {version}</h1> + <h2>{version_info}</h2> + <h3><a href={version_url}>Download link</a></h3> + </source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>IdentitiesTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="84"/> + <source>UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="85"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="86"/> + <source>Renewed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/identities.py" line="87"/> + <source>Expiration</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ImportAccountDialog</name> + <message> + <location filename="../../ui/import_account.ui" line="14"/> + <source>Import an account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/import_account.ui" line="25"/> + <source>Import a file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/import_account.ui" line="36"/> + <source>Name of the account :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="34"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> + <source>Account import</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="38"/> + <source>Account imported succefully !</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> + <source>Import an account file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="43"/> + <source>All account files (*.acc)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="58"/> + <source>Please enter a name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="63"/> + <source>Name already exists</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/import_account.py" line="67"/> + <source>File is not an account format</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>InformationsTabWidget</name> + <message> + <location filename="../../ui/informations_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="52"/> + <source>General</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="61"/> + <source>label_general</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="77"/> + <source>Rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="83"/> + <source>label_rules</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="96"/> + <source>Money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="102"/> + <source>label_money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="115"/> + <source>WoT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/informations_tab.ui" line="121"/> + <source>label_wot</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Universal Dividend UD(t) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass M(t-1) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Members N(t)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Monetary Mass per member M(t-1)/N(t) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Actual growth c = UD(t)/[M(t-1)/N(t)]</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Last UD date and time (t)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="130"/> + <source>Next UD date and time (t+1)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="202"/> + <source>No Universal Dividend created yet.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>{:2.0%} / {:} days</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>Fundamental growth (c) / Delta time (dt)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>UD(t+1) = MAX { UD(t) ; c &#215; M(t) / N(t+1) }</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>Universal Dividend (formula)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>{:} = MAX {{ {:} {:} ; {:2.0%} &#215; {:} {:} / {:} }}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="177"/> + <source>Universal Dividend (computed)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:2.0%} / {:} days</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:2.0%}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Fundamental growth (c)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Initial Universal Dividend UD(0) in</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Time period (dt) in days (86400 seconds) between two UD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>Number of blocks used for calculating median time</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The average time in seconds for writing 1 block (wished time)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The number of blocks required to evaluate again PoWMin value</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The number of previous blocks to check for personalized difficulty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="205"/> + <source>The percent of previous issuers to reach for personalized difficulty</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Minimum delay between 2 identical certifications (in days)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Maximum age of a valid signature (in days)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Minimum quantity of signatures to be part of the WoT</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Minimum quantity of valid made certifications to be part of the WoT for distance rule</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Maximum age of a valid membership (in days)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/informations_tab.py" line="240"/> + <source>Maximum distance between each WoT member and a newcomer</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>MainWindow</name> + <message> + <location filename="../../ui/mainwindow.ui" line="41"/> + <source>Fi&le</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="142"/> + <source>Account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="55"/> + <source>&Contacts</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="61"/> + <source>&Open</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="76"/> + <source>&Help</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="87"/> + <source>Manage accounts</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="92"/> + <source>Configure trustable nodes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="97"/> + <source>&Add a contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="117"/> + <source>Send a message</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="122"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="127"/> + <source>Remove contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="132"/> + <source>Save</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="137"/> + <source>&Quit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="147"/> + <source>&Transfer money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="152"/> + <source>&Configure</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="157"/> + <source>&Import</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="162"/> + <source>&Export</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="167"/> + <source>&Certification</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="172"/> + <source>&Set as default</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="177"/> + <source>A&bout</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="182"/> + <source>&Preferences</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/mainwindow.ui" line="187"/> + <source>&Add account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="190"/> + <source>Latest release : {version}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="197"/> + <source>Download link</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="204"/> + <source> + <h1>Cutecoin</h1> + + <p>Python/Qt uCoin client</p> + + <p>Version : {:}</p> + {new_version_text} + + <p>License : MIT</p> + + <p><b>Authors</b></p> + + <p>inso</p> + <p>vit</p> + <p>canercandan</p> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="229"/> + <source>Please get the latest release {version}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="272"/> + <source>Edit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="275"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="291"/> + <source>CuteCoin {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="316"/> + <source>CuteCoin {0} - Account : {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="336"/> + <source>Export an account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="337"/> + <source>All account files (*.acc)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/mainwindow.py" line="338"/> + <source>Export</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>MemberDialog</name> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="31"/> + <source>not a member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="43"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> + <source>Public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="47"/> + <source>Join date</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="65"/> + <source><tr><td align="right"><b>{:}</b></div></td><td>{:}</td></tr></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="56"/> + <source>Distance</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/member.py" line="61"/> + <source>Path</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NetworkFilterProxyModel</name> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="41"/> + <source>Address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="42"/> + <source>Port</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="43"/> + <source>Block</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="44"/> + <source>UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="45"/> + <source>Member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="46"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="47"/> + <source>Software</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="48"/> + <source>Version</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> + <source>yes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> + <source>no</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="61"/> + <source>offline</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NetworkTabWidget</name> + <message> + <location filename="../../ui/network_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="61"/> + <source>Unset root node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="67"/> + <source>Set as root node</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/network_tab.py" line="73"/> + <source>Open in browser</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>NetworkTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="114"/> + <source>Online</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="115"/> + <source>Offline</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="116"/> + <source>Unsynchronized</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/network.py" line="117"/> + <source>Corrupted</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PasswordAskerDialog</name> + <message> + <location filename="../../ui/password_asker.ui" line="14"/> + <source>Password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/password_asker.ui" line="23"/> + <source>Please enter your account password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/password_asker.ui" line="32"/> + <source>Remember my password during this session</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> + <source>Bad password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="48"/> + <source>Non printable characters in password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> + <source>Failed to get private key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/password_asker.py" line="54"/> + <source>Wrong password typed. Cannot open the private key</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>PreferencesDialog</name> + <message> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> + <source>Preferences</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="108"/> + <source>Default account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="122"/> + <source>Default &referential</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="159"/> + <source>Enable expert mode</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="191"/> + <source>Digits after commas </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="205"/> + <source>Language</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="239"/> + <source>Maximize Window at Startup</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/preferences.ui" line="266"/> + <source>Enable notifications</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/preferences.py" line="60"/> + <source>A restart is needed to apply your new preferences.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessConfigureAccount</name> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="165"/> + <source>New account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="172"/> + <source>Configure {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="187"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> + <source>Public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="208"/> + <source>These parameters pubkeys are : {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="225"/> + <source>This action will delete your account locally. +Please note your key parameters (salt and password) if you wish to recover it later. +Your account won't be removed from the networks it joined. +Are you sure ?</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="248"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>ProcessConfigureCommunity</name> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="126"/> + <source>Configure community {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="129"/> + <source>Add a community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="192"/> + <source>Delete</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>UID Publishing</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="204"/> + <source>Success publishing your UID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="216"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> + <source>Pubkey not found</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="230"/> + <source>The public key of your account wasn't found in the community. : + +{0} + +Would you like to publish the key ?</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>Toast</name> + <message> + <location filename="../../ui/toast.ui" line="14"/> + <source>MainWindow</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>TransactionsTabWidget</name> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="93"/> + <source>Received {0} {1} from {2} transfers</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="97"/> + <source>New transactions received</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="123"/> + <source><b>Deposits</b> {:} {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="127"/> + <source><b>Payments</b> {:} {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="131"/> + <source><b>Balance</b> {:} {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="140"/> + <source>Actions</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="153"/> + <source>Send again</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="158"/> + <source>Cancel</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="164"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="169"/> + <source>Add as contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="174"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="180"/> + <source>View in Web of Trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="185"/> + <source>Copy pubkey to clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> + <source>Warning</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transactions_tab.py" line="222"/> + <source>Are you sure ? +This money transfer will be removed and not sent.</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>TransferMoneyDialog</name> + <message> + <location filename="../../ui/transfer.ui" line="14"/> + <source>Transfer money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="20"/> + <source>Community</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="32"/> + <source>Transfer money to</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="40"/> + <source>Contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="61"/> + <source>Recipient public key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="80"/> + <source>Key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="106"/> + <source>Wallet :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="125"/> + <source>Availalble currency : </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="134"/> + <source>Amount :</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="144"/> + <source> UD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transfer.ui" line="162"/> + <source>Transaction message</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> + <source>Money transfer</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="68"/> + <source>No amount. Please give the transfert amount</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Transfer</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="90"/> + <source>Success sending money to {0}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>Error</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/transfer.py" line="102"/> + <source>{0} : {1}</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WalletsTab</name> + <message> + <location filename="../../ui/wallets_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="43"/> + <source>Account</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="52"/> + <source>label_general</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="68"/> + <source>Balance</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wallets_tab.ui" line="74"/> + <source>label_balance</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WalletsTabWidget</name> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> + <source>Membership</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="82"/> + <source>Last renewal on {:}, expiration on {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source>Your web of trust</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source>Certified by {:} members; Certifier of {:} members</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="100"/> + <source>Not a member</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source> + <table cellpadding="5"> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + </table> + </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>Your money share </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>{:.2f}%</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>Your part </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="134"/> + <source>{:} {:} in [{:} ; {:}] {:}</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="181"/> + <source>New Wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="184"/> + <source>Rename</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="188"/> + <source>Copy pubkey to clipboard</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/wallets_tab.py" line="193"/> + <source>Transfer to...</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WalletsTableModel</name> + <message> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> + <source>Name</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> + <source>Amount</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/models/wallets.py" line="82"/> + <source>Pubkey</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WoT.Node</name> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="288"/> + <source>Informations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="293"/> + <source>Add as contact</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="298"/> + <source>Send money</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/views/wot.py" line="303"/> + <source>Certify identity</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>WotTabWidget</name> + <message> + <location filename="../../ui/wot_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/wot_tab.ui" line="33"/> + <source>Me</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>self.config_dialog</name> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_community.py" line="92"/> + <source>Ok</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="71"/> + <source>Forbidden : salt is too short</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="75"/> + <source>Forbidden : password is too short</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="79"/> + <source>Forbidden : Invalid characters in salt field</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="83"/> + <source>Forbidden : Invalid characters in password field</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../../src/cutecoin/gui/process_cfg_account.py" line="89"/> + <source>Error : passwords are different</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>transactionsTabWidget</name> + <message> + <location filename="../../ui/transactions_tab.ui" line="14"/> + <source>Form</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="37"/> + <source>dd/MM/yyyy</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="83"/> + <source>Payment:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="90"/> + <source>Deposit:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../../ui/transactions_tab.ui" line="100"/> + <source>Balance:</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/res/icons/AUTHORS b/res/icons/AUTHORS index 4a45430043be9f31134cde6a45b84c54d509f48f..4dc1d78f3193393fcd73bb1eec24f2c481b5d9a0 100644 --- a/res/icons/AUTHORS +++ b/res/icons/AUTHORS @@ -11,4 +11,8 @@ noun_43022_cc.svg : Created by Piotrek Chuchla noun_7440_cc.svg : Created by Yuri Mamae noun_62479_cc.svg : Created by Bridget Gahagan noun_76373_cc.svg : Created by João Paulo -noun_5197_cc.svg : Created by Bibzee \ No newline at end of file +noun_5197_cc.svg : Created by Bibzee +noun_38960_cc.svg : Created by Agarunov Oktay-Abraham +noun_42425_cc.svg : Created by Luis Rodrigues +noun_62146_cc.svg : Created by Sergey Krivoy +noun_2149_cc.svg : Created by Anand A Nair \ No newline at end of file diff --git a/res/icons/icons.qrc b/res/icons/icons.qrc index f53129c9c7cbed6c60f20bdd8799eba72b4e621d..0de880855d731b4fa7485765f7c1e8689e3a8af4 100644 --- a/res/icons/icons.qrc +++ b/res/icons/icons.qrc @@ -12,7 +12,11 @@ <file alias="members_icon">noun_18704_cc.svg</file> <file alias="wallet_icon">noun_29542_cc.svg</file> <file alias="tx_icon">noun_63271_cc.svg</file> + <file alias="refresh_icon">noun_2149_cc.svg</file> <file alias="currency_icon">noun_43022_cc.svg</file> + <file alias="settings_display_icon">noun_38960_cc.svg</file> + <file alias="settings_app_icon">noun_42425_cc.svg</file> + <file alias="settings_network_icon">noun_62146_cc.svg</file> <file alias="connected">connected.svg</file> <file alias="weak_connect">weak_connect.svg</file> <file alias="disconnected">disconnected.svg</file> diff --git a/res/icons/noun_2149_cc.svg b/res/icons/noun_2149_cc.svg new file mode 100644 index 0000000000000000000000000000000000000000..164b75ec606987190ecbaea2bafc0862e7be3941 --- /dev/null +++ b/res/icons/noun_2149_cc.svg @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 100 125" + enable-background="new 0 0 100 100" + xml:space="preserve" + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="noun_2149_cc.svg"><metadata + id="metadata16"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs14" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="743" + inkscape:window-height="480" + id="namedview12" + showgrid="false" + inkscape:zoom="1.888" + inkscape:cx="48.267139" + inkscape:cy="72.791055" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="svg2" /><path + d="m 68.45393,90.514664 c -5.405642,3.785074 -11.719343,5.688849 -18.182514,5.596695 -0.851867,-0.01236 -1.701486,-0.05058 -2.54099,-0.126993 -0.341646,-0.03146 -0.686663,-0.08878 -1.030556,-0.133737 -0.657443,-0.08204 -1.314886,-0.171946 -1.963338,-0.300063 -0.39559,-0.07642 -0.781066,-0.17307 -1.171036,-0.267473 -0.631595,-0.147222 -1.25757,-0.294445 -1.872307,-0.479877 -0.297817,-0.08878 -0.585519,-0.197795 -0.877715,-0.293321 -0.712511,-0.236005 -1.417155,-0.485496 -2.103818,-0.773198 -0.157337,-0.06406 -0.306807,-0.133736 -0.463019,-0.204537 -0.804666,-0.351761 -1.595846,-0.727121 -2.364548,-1.144064 -0.03484,-0.01798 -0.06743,-0.03821 -0.102269,-0.05731 -2.601676,-1.422776 -4.97971,-3.204054 -7.063298,-5.285395 -0.03484,-0.03709 -0.06744,-0.07529 -0.102269,-0.109012 -0.643957,-0.6507 -1.26319,-1.335115 -1.850955,-2.042007 -0.12587,-0.147222 -0.241624,-0.312426 -0.36637,-0.466391 -4.248094,-5.31012 -6.808187,-12.031773 -6.808187,-19.347934 l 8.171398,0 -13.074688,-19.606413 -13.0724403,19.608661 8.1713992,0 c 0,8.598456 2.6803451,16.565317 7.2329971,23.153234 0.05731,0.09665 0.09553,0.191051 0.159585,0.280958 0.473134,0.676548 0.995717,1.295781 1.499195,1.934119 0.191052,0.242748 0.364122,0.492239 0.561917,0.727121 0.74735,0.901315 1.538529,1.756552 2.349937,2.585942 0.07529,0.07642 0.150594,0.159584 0.230386,0.236005 2.732041,2.739909 5.814719,5.042645 9.176107,6.881236 0.09215,0.0517 0.178689,0.10901 0.270844,0.15397 0.970992,0.52258 1.961091,0.99684 2.975914,1.43625 0.24949,0.10901 0.494486,0.22927 0.746225,0.33828 0.868724,0.35738 1.750934,0.67093 2.643259,0.96313 0.421437,0.14834 0.842875,0.29444 1.269932,0.42143 0.777693,0.23601 1.576739,0.42706 2.381405,0.61924 0.532697,0.12137 1.062023,0.25624 1.604835,0.36412 0.223643,0.0449 0.437171,0.10901 0.661939,0.1461 0.758587,0.13486 1.5183,0.21128 2.27464,0.30118 0.275339,0.0326 0.541688,0.0831 0.820399,0.10902 1.358715,0.13373 2.719679,0.22364 4.075024,0.22364 8.308506,0 16.409104,-2.54211 23.360018,-7.411686 2.215078,-1.552015 2.75789,-4.608845 1.199132,-6.823922 -1.553139,-2.222944 -4.616712,-2.759012 -6.82617,-1.206998 z" + id="path4" + inkscape:connector-curvature="0" + style="fill:#000000" /><path + d="M 91.499276,65.082295 C 91.492503,56.51643 88.831293,48.561931 84.298869,41.987501 84.234812,41.878489 84.189857,41.763858 84.113437,41.655969 83.551519,40.854675 82.93903,40.105078 82.325416,39.350985 82.255736,39.262201 82.19168,39.165553 82.115259,39.07677 77.971682,34.047611 72.762711,30.160269 66.864828,27.618156 c -0.165203,-0.06968 -0.325911,-0.146098 -0.485496,-0.2169 -0.944021,-0.388847 -1.907147,-0.733864 -2.87814,-1.054157 -0.351759,-0.118002 -0.695653,-0.242748 -1.047413,-0.350636 -0.849618,-0.25511 -1.704857,-0.459648 -2.573581,-0.663063 -0.478753,-0.107887 -0.950764,-0.230385 -1.43626,-0.325911 -0.234881,-0.04383 -0.465267,-0.115755 -0.700149,-0.159584 -0.64508,-0.112384 -1.290161,-0.160709 -1.941985,-0.242749 -0.446162,-0.05731 -0.887829,-0.128117 -1.333991,-0.173071 -1.086749,-0.102268 -2.165629,-0.146098 -3.24451,-0.162955 -0.196671,-0.0022 -0.385475,-0.0281 -0.579898,-0.0281 -0.03821,0 -0.0708,0.009 -0.105641,0.01348 -8.29502,0.0057 -16.383256,2.508398 -23.32518,7.373477 -2.218449,1.550891 -2.756766,4.609968 -1.201378,6.827293 1.549767,2.217324 4.60772,2.756765 6.826168,1.202503 5.361814,-3.753607 11.621571,-5.658506 18.036416,-5.597819 0.919297,0.0057 1.827355,0.05058 2.725298,0.137108 0.274217,0.0281 0.548432,0.06968 0.823771,0.10564 0.740607,0.08878 1.474471,0.191052 2.18923,0.331531 0.318045,0.06406 0.631594,0.147222 0.94402,0.216901 0.70914,0.160708 1.417155,0.332655 2.107189,0.542812 0.215777,0.06406 0.433801,0.146098 0.649577,0.216899 0.792303,0.258482 1.57112,0.537193 2.330832,0.856362 0.07642,0.03147 0.152841,0.0708 0.229262,0.101145 4.570634,1.96671 8.580475,4.992072 11.706982,8.778269 0.02023,0.02247 0.03821,0.05058 0.05843,0.07193 4.397563,5.359566 7.047564,12.208212 7.053183,19.663729 l -8.177018,0 13.080307,19.609783 13.069066,-19.609783 -8.164653,0 z" + id="path6" + inkscape:connector-curvature="0" + style="fill:#000000" /></svg> \ No newline at end of file diff --git a/res/icons/noun_38960_cc.svg b/res/icons/noun_38960_cc.svg new file mode 100644 index 0000000000000000000000000000000000000000..827cabaa965fade8e120c23aae0d4c58e5f97462 --- /dev/null +++ b/res/icons/noun_38960_cc.svg @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 50 62.5" + enable-background="new 0 0 50 50" + xml:space="preserve" + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="noun_38960_cc.svg"><metadata + id="metadata44"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs42" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1366" + inkscape:window-height="709" + id="namedview40" + showgrid="false" + inkscape:zoom="3.776" + inkscape:cx="25" + inkscape:cy="31.25" + inkscape:window-x="-2" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" /><path + d="M30.711,14.454H8.617c-1.107,0-2.008,0.9-2.008,2.008V29.6c0,1.107,0.901,2.009,2.008,2.009h22.095 c1.107,0,2.008-0.901,2.008-2.009V16.462C32.72,15.354,31.82,14.454,30.711,14.454z M8.617,15.792h22.095 c0.37,0,0.67,0.299,0.67,0.67v2.265H7.948v-2.265C7.948,16.091,8.25,15.792,8.617,15.792z M7.948,29.599v-9.533h6.242v10.203H8.617 C8.25,30.269,7.948,29.968,7.948,29.599z M30.711,30.269H15.529V20.065h15.852v9.533C31.381,29.968,31.082,30.269,30.711,30.269z" + id="path4" /><circle + cx="9.789" + cy="17.293" + r="0.717" + id="circle6" /><circle + cx="11.797" + cy="17.293" + r="0.717" + id="circle8" /><circle + cx="13.806" + cy="17.293" + r="0.717" + id="circle10" /><rect + x="9.07" + y="21.319" + width="3.935" + height="1.338" + id="rect12" /><rect + x="9.07" + y="23.368" + width="3.935" + height="1.339" + id="rect14" /><rect + x="9.07" + y="25.503" + width="3.935" + height="1.339" + id="rect16" /><rect + x="9.07" + y="27.68" + width="3.935" + height="1.34" + id="rect18" /><path + d="M20.425,21.477h-3.683v2.972h3.683V21.477z M19.086,23.108h-1.004v-0.292h1.004V23.108z" + id="path20" /><rect + x="16.743" + y="25.503" + width="13.308" + height="1.339" + id="rect22" /><rect + x="16.743" + y="27.68" + width="13.308" + height="1.34" + id="rect24" /><path + d="M25.238,21.477h-3.683v2.972h3.683V21.477z M23.899,23.108h-1.004v-0.292h1.004V23.108z" + id="path26" /><path + d="M26.367,24.448h3.684v-2.972h-3.684V24.448z M27.706,22.815h1.004v0.292h-1.004V22.815z" + id="path28" /><g + id="g30"><path + d="M44.773,44.332H5.196c-2.079,0-3.769-1.69-3.769-3.769V13.065c0-2.079,1.69-3.769,3.769-3.769h39.577 c2.078,0,3.768,1.69,3.768,3.769v27.498C48.541,42.642,46.852,44.332,44.773,44.332z M5.196,10.427 c-1.455,0-2.639,1.184-2.639,2.639v27.498c0,1.455,1.184,2.639,2.639,2.639h39.577c1.455,0,2.638-1.184,2.638-2.639V13.065 c0-1.455-1.183-2.639-2.638-2.639H5.196z" + id="path32" /></g><path + d="M43.834,34.272l-2.076-2.076l2.197-1.099c0.186-0.093,0.292-0.29,0.27-0.495c-0.022-0.206-0.171-0.376-0.37-0.425 l-7.254-1.814c-0.168-0.042-0.346,0.008-0.469,0.131s-0.172,0.3-0.129,0.468l1.813,7.254c0.05,0.199,0.22,0.347,0.425,0.37 c0.018,0.003,0.036,0.004,0.054,0.004c0.187,0,0.357-0.104,0.442-0.274l1.099-2.196l2.076,2.076 c0.266,0.267,0.613,0.398,0.962,0.398c0.347,0,0.696-0.132,0.961-0.398C44.365,35.665,44.365,34.804,43.834,34.272z M43.136,35.498 c-0.094,0.095-0.205,0.109-0.263,0.109s-0.168-0.015-0.264-0.111l-3.045-3.043l-1.114,2.228l-1.291-5.159l5.16,1.29l-2.229,1.114 l3.047,3.045c0.095,0.095,0.108,0.206,0.108,0.264S43.231,35.403,43.136,35.498z" + id="path34" /></svg> \ No newline at end of file diff --git a/res/icons/noun_42425_cc.svg b/res/icons/noun_42425_cc.svg new file mode 100644 index 0000000000000000000000000000000000000000..f6b8c6e98e51165c7c9b09219753c92be3d49ea7 --- /dev/null +++ b/res/icons/noun_42425_cc.svg @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 100 125" + enable-background="new 0 0 100 100" + xml:space="preserve" + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="noun_42425_cc.svg"><metadata + id="metadata18"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs16" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1366" + inkscape:window-height="709" + id="namedview14" + showgrid="false" + inkscape:zoom="1.888" + inkscape:cx="50" + inkscape:cy="62.5" + inkscape:window-x="-2" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" /><g + id="g4" + transform="translate(0,12)"><path + d="M 99,57.669 99,41.331 87.822,38.01 C 87.084,35.6 86.217,33.29 85.052,31.102 L 90.793,20.71 79.266,9.158 68.886,14.851 C 66.698,13.687 64.394,12.915 61.985,12.178 L 58.669,1 42.331,1 39.01,12.178 c -2.41,0.737 -4.72,1.605 -6.908,2.769 L 21.71,9.206 10.158,20.734 15.851,31.113 c -1.164,2.188 -2.436,4.493 -3.173,6.902 L 1,41.331 1,57.669 12.678,60.99 c 0.737,2.41 1.855,4.721 3.019,6.908 l -5.616,10.391 11.591,11.553 10.411,-5.693 c 2.188,1.164 4.508,2.437 6.917,3.174 L 42.331,99 58.669,99 61.99,87.322 c 2.41,-0.737 4.721,-1.855 6.908,-3.02 l 10.391,5.616 11.553,-11.591 -5.693,-10.41 c 1.164,-2.188 1.937,-4.509 2.674,-6.918 L 99,57.669 Z M 50.5,79.873 c -16.774,0 -30.373,-13.599 -30.373,-30.373 0,-16.774 13.598,-30.372 30.373,-30.372 16.775,0 30.373,13.598 30.373,30.372 0,16.774 -13.598,30.373 -30.373,30.373 z" + id="path6" + inkscape:connector-curvature="0" /><path + d="m 53.125,50.135 4.353,-4.354 c 1.64,-1.64 0.89,-4.44 -1.35,-5.04 L 40.15,36.461 c -2.24,-0.6 -4.289,1.449 -3.689,3.689 l 4.281,15.977 c 0.6,2.24 3.4,2.99 5.04,1.35 l 4.354,-4.353 9.907,9.907 c 0.817,0.817 2.142,0.817 2.959,0 l 0.03,-0.03 c 0.817,-0.817 0.817,-2.142 0,-2.959 l -9.907,-9.907 z" + id="path8" + inkscape:connector-curvature="0" /></g></svg> \ No newline at end of file diff --git a/res/icons/noun_62146_cc.svg b/res/icons/noun_62146_cc.svg new file mode 100644 index 0000000000000000000000000000000000000000..00572988306e49595e82d232f764202d4c188a73 --- /dev/null +++ b/res/icons/noun_62146_cc.svg @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 100 125" + enable-background="new 0 0 100 100" + xml:space="preserve" + id="svg2" + inkscape:version="0.91 r13725" + sodipodi:docname="noun_62146_cc.svg"><metadata + id="metadata26"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs24" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1366" + inkscape:window-height="709" + id="namedview22" + showgrid="false" + inkscape:zoom="1.888" + inkscape:cx="50" + inkscape:cy="39.907766" + inkscape:window-x="-2" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" /><g + id="g4" + transform="matrix(1.1447278,0,0,1.1447278,-8.4204046,3.5272159)"><rect + x="31" + y="32.5" + width="41" + height="4" + id="rect6" /></g><g + id="g8" + transform="matrix(1.1447278,0,0,1.1447278,-8.4204046,3.5272159)"><rect + x="48" + y="82.5" + width="9" + height="17.5" + id="rect10" /></g><path + d="m 37.368709,4.8871525 0,33.5542625 4.578911,0 0,-33.5542625 -4.578911,0 z m 3.451355,7.0824315 -2.324943,0 0,-5.5805485 2.324943,0 0,5.5805485 z" + id="path12" + inkscape:connector-curvature="0" /><path + d="m 60.263266,4.8871525 0,33.5542625 4.578911,0 0,-33.5542625 -4.578911,0 z m 3.451354,7.0824315 -2.324942,0 0,-5.5805485 2.324942,0 0,5.5805485 z" + id="path14" + inkscape:connector-curvature="0" /><path + d="m 27.066159,43.020326 0,30.907652 12.019642,21.749829 24.039285,0 L 74,73.927978 l 0,-30.907652 -46.933841,0 z m 40.065474,32.05238 -34.341835,0 0,-2.289456 34.341835,0 0,2.289456 z" + id="path16" + inkscape:connector-curvature="0" /></svg> \ No newline at end of file diff --git a/res/ui/community_cfg.ui b/res/ui/community_cfg.ui index 9c17e89ff35b30851b3d74b4c1aa6d76966ed9eb..0d18585d3a33202199ab2f5ae604aa3032e3099a 100644 --- a/res/ui/community_cfg.ui +++ b/res/ui/community_cfg.ui @@ -75,7 +75,31 @@ </layout> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"/> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="topMargin"> + <number>6</number> + </property> + <item> + <spacer name="horizontalSpacer"> + <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="QPushButton" name="button_checknode"> + <property name="text"> + <string>Check node connectivity</string> + </property> + </widget> + </item> + </layout> </item> <item> <spacer name="verticalSpacer"> diff --git a/res/ui/currency_tab.ui b/res/ui/currency_tab.ui index d56ceb510acec4ea49fce7866c330b6d3a3a2885..96fb8c5971e30da25a72bb274cce7d4ad3b27974 100644 --- a/res/ui/currency_tab.ui +++ b/res/ui/currency_tab.ui @@ -29,9 +29,6 @@ <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QTabWidget" name="tabs_account"> - <property name="enabled"> - <bool>false</bool> - </property> <property name="currentIndex"> <number>-1</number> </property> diff --git a/res/ui/network_tab.ui b/res/ui/network_tab.ui index 9398b9b6f43dd936bb775fc00effd0a9054beeba..25edc66568e46a69ea74f9ce6314559b6fa5075b 100644 --- a/res/ui/network_tab.ui +++ b/res/ui/network_tab.ui @@ -16,6 +16,43 @@ <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="topMargin"> + <number>6</number> + </property> + <item> + <spacer name="horizontalSpacer"> + <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="QPushButton" name="button_manual_refresh"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../icons/icons.qrc"> + <normaloff>:/icons/refresh_icon</normaloff>:/icons/refresh_icon</iconset> + </property> + <property name="iconSize"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + </widget> + </item> + </layout> + </item> <item> <widget class="QTableView" name="table_network"> <property name="contextMenuPolicy"> @@ -48,7 +85,9 @@ </item> </layout> </widget> - <resources/> + <resources> + <include location="../icons/icons.qrc"/> + </resources> <connections> <connection> <sender>table_network</sender> @@ -66,8 +105,25 @@ </hint> </hints> </connection> + <connection> + <sender>button_manual_refresh</sender> + <signal>clicked()</signal> + <receiver>NetworkTabWidget</receiver> + <slot>manual_nodes_refresh()</slot> + <hints> + <hint type="sourcelabel"> + <x>373</x> + <y>28</y> + </hint> + <hint type="destinationlabel"> + <x>199</x> + <y>149</y> + </hint> + </hints> + </connection> </connections> <slots> <slot>node_context_menu()</slot> + <slot>manual_nodes_refresh()</slot> </slots> </ui> diff --git a/res/ui/preferences.ui b/res/ui/preferences.ui index a1f722da639b20a40c71ff916e50ef8963329112..f4af53d09b701235bf8bff4706e420d2b42b2313 100644 --- a/res/ui/preferences.ui +++ b/res/ui/preferences.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>300</height> + <width>443</width> + <height>315</height> </rect> </property> <property name="windowTitle"> @@ -15,45 +15,282 @@ </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> - <layout class="QHBoxLayout" name="horizontalLayout"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="topMargin"> + <number>6</number> + </property> <item> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Default account</string> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <property name="leftMargin"> + <number>6</number> </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="combo_account"/> + <item> + <widget class="QPushButton" name="button_app"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../icons/icons.qrc"> + <normaloff>:/icons/settings_app_icon</normaloff>:/icons/settings_app_icon</iconset> + </property> + <property name="iconSize"> + <size> + <width>65</width> + <height>65</height> + </size> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_display"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../icons/icons.qrc"> + <normaloff>:/icons/settings_display_icon</normaloff>:/icons/settings_display_icon</iconset> + </property> + <property name="iconSize"> + <size> + <width>65</width> + <height>65</height> + </size> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_network"> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="../icons/icons.qrc"> + <normaloff>:/icons/settings_network_icon</normaloff>:/icons/settings_network_icon</iconset> + </property> + <property name="iconSize"> + <size> + <width>65</width> + <height>65</height> + </size> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Default referential</string> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> </widget> </item> <item> - <widget class="QComboBox" name="combo_referential"/> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Language</string> + <widget class="QStackedWidget" name="stackedWidget"> + <property name="currentIndex"> + <number>0</number> </property> + <widget class="QWidget" name="page"> + <layout class="QVBoxLayout" name="verticalLayout_7"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Default account</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="combo_account"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Default &referential</string> + </property> + <property name="buddy"> + <cstring>label_3</cstring> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="combo_referential"/> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <spacer name="horizontalSpacer"> + <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="QCheckBox" name="checkbox_expertmode"> + <property name="text"> + <string>Enable expert mode</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + <zorder></zorder> + <zorder></zorder> + <zorder>verticalSpacer</zorder> + <zorder>line_2</zorder> + </widget> + <widget class="QWidget" name="page_2"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Digits after commas </string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinbox_digits_comma"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <item> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Language</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="combo_language"/> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <spacer name="horizontalSpacer_2"> + <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="QCheckBox" name="checkbox_maximize"> + <property name="text"> + <string>Maximize Window at Startup</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <property name="topMargin"> + <number>6</number> + </property> + <item> + <spacer name="horizontalSpacer_3"> + <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="QCheckBox" name="checkbox_notifications"> + <property name="text"> + <string>Enable notifications</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + <zorder></zorder> + <zorder></zorder> + <zorder>verticalSpacer_2</zorder> + <zorder>line_3</zorder> + </widget> + <widget class="QWidget" name="page_3"/> </widget> </item> - <item> - <widget class="QComboBox" name="combo_language"/> - </item> </layout> </item> <item> @@ -68,7 +305,9 @@ </item> </layout> </widget> - <resources/> + <resources> + <include location="../icons/icons.qrc"/> + </resources> <connections> <connection> <sender>buttonBox</sender> diff --git a/src/cutecoin/core/__init__.py b/src/cutecoin/core/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6de4a62c9259773dd9a301d9f7e5b616c7328489 100644 --- a/src/cutecoin/core/__init__.py +++ b/src/cutecoin/core/__init__.py @@ -0,0 +1,4 @@ +from .community import Community +from .wallet import Wallet +from .account import Account +from .app import Application \ No newline at end of file diff --git a/src/cutecoin/core/account.py b/src/cutecoin/core/account.py index 165adb6419c96281bc188e42e749855b7e52077b..7ba205acc8397ca894c3f87e87dd7693cd7ba159 100644 --- a/src/cutecoin/core/account.py +++ b/src/cutecoin/core/account.py @@ -4,8 +4,6 @@ Created on 1 févr. 2014 @author: inso """ -from ucoinpy import PROTOCOL_VERSION -from ucoinpy.api import bma from ucoinpy.documents.certification import SelfCertification, Certification, Revocation from ucoinpy.documents.membership import Membership from ucoinpy.key import SigningKey @@ -13,14 +11,18 @@ from ucoinpy.key import SigningKey import logging import time import math +import json +import asyncio from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication, QT_TRANSLATE_NOOP +from PyQt5.QtNetwork import QNetworkReply from .wallet import Wallet from .community import Community -from .person import Person +from .registry import Identity, IdentitiesRegistry from ..tools.exceptions import ContactAlreadyExists - +from ..core.net.api import bma as qtbma +from ..core.net.api.bma import PROTOCOL_VERSION def quantitative(units, community): """ @@ -41,14 +43,21 @@ def relative(units, community): :param cutecoin.core.community.Community community: Community instance :return: float """ - # fixme: the value "community.nb_members" is not up to date, luckyly the good value is in "community.get_ud_block()['membersCount']" # calculate ud(t+1) - ud = math.ceil( - max(community.dividend, - community.parameters['c'] * community.monetary_mass / community.get_ud_block()['membersCount']) - ) - relative_value = units / float(ud) - return relative_value + ud_block = community.get_ud_block() + if ud_block: + ud = math.ceil( + max(community.dividend(), + float(0) if ud_block['membersCount'] == 0 else + community.parameters['c'] * community.monetary_mass / ud_block['membersCount'])) + + if ud == 0: + return float(0) + else: + relative_value = units / float(ud) + return relative_value + else: + return float(0) def quantitative_zerosum(units, community): @@ -111,9 +120,17 @@ class Account(QObject): ) loading_progressed = pyqtSignal(int, int) - - def __init__(self, salt, pubkey, name, communities, wallets, contacts): - ''' + loading_finished = pyqtSignal(list) + inner_data_changed = pyqtSignal(str) + wallets_changed = pyqtSignal() + membership_broadcasted = pyqtSignal() + certification_broadcasted = pyqtSignal() + selfcert_broadcasted = pyqtSignal() + revoke_broadcasted = pyqtSignal() + broadcast_error = pyqtSignal(int, str) + + def __init__(self, salt, pubkey, name, communities, wallets, contacts, identities_registry): + """ Create an account :param str salt: The root key salt @@ -121,12 +138,13 @@ class Account(QObject): 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 + :param list of cutecoin.core.Community communities: Community objects referenced by this account + :param list of cutecoin.core.Wallet wallets: Wallet objects owned by this account + :param list of dict contacts: Contacts of this account + :param cutecoin.core.registry.IdentitiesRegistry: The identities registry intance .. warnings:: The class methods create and load should be used to create an account - ''' + """ super().__init__() self.salt = salt self.pubkey = pubkey @@ -134,11 +152,13 @@ class Account(QObject): self.communities = communities self.wallets = wallets self.contacts = contacts + self._refreshing = False + self._identities_registry = identities_registry self.referential = 0 @classmethod - def create(cls, name): - ''' + def create(cls, name, identities_registry): + """ Factory method to create an empty account object This new account doesn't have any key and it should be given one later @@ -148,17 +168,20 @@ class Account(QObject): :param str name: The account name, same as network identity uid :return: A new empty account object - ''' - account = cls(None, None, name, [], [], []) + """ + account = cls(None, None, name, [], [], [], identities_registry) return account @classmethod - def load(cls, json_data): - ''' + def load(cls, json_data, network_manager, identities_registry): + """ Factory method to create an Account object from its json view. + :rtype : cutecoin.core.account.Account :param dict json_data: The account view as a json dict + :param PyQt5.QtNetwork import QNetworkManager: network_manager + :param cutecoin.core.registry.self._identities_registry: identities_registry :return: A new account object created from the json datas - ''' + """ salt = json_data['salt'] pubkey = json_data['pubkey'] @@ -170,34 +193,34 @@ class Account(QObject): wallets = [] for data in json_data['wallets']: - wallets.append(Wallet.load(data)) + wallets.append(Wallet.load(data, identities_registry)) communities = [] for data in json_data['communities']: - community = Community.load(data) + community = Community.load(network_manager, data) communities.append(community) account = cls(salt, pubkey, name, communities, wallets, - contacts) + contacts, identities_registry) 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) @@ -210,39 +233,72 @@ class Account(QObject): self.contacts.append(new_contact) def add_community(self, community): - ''' + """ Add a community to the account :param community: A community object to add - ''' + """ self.communities.append(community) return community - def refresh_cache(self): - ''' + def refresh_transactions(self, community): + """ 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): - account_value = maximum * len(self.communities) * loaded_wallets + value - account_max = maximum * len(self.communities) * len(self.wallets) - self.loading_progressed.emit(account_value, account_max) - - for w in self.wallets: - w.refresh_progressed.connect(progressing) - QCoreApplication.processEvents() - for c in self.communities: - w.init_cache(c) - loaded_wallets = loaded_wallets + 1 - QCoreApplication.processEvents() + """ + logging.debug("Start refresh transactions") + if not self._refreshing: + self._refreshing = True + loaded_wallets = 0 + received_list = [] + values = {} + maximums = {} + + def progressing(value, maximum, hash): + logging.debug("Loading = {0} : {1} : {2}".format(value, maximum, loaded_wallets)) + values[hash] = value + maximums[hash] = maximum + account_value = sum(values.values()) + account_max = sum(maximums.values()) + self.loading_progressed.emit(account_value, account_max) + + def wallet_finished(received): + logging.debug("Finished loading wallet") + nonlocal received_list + received_list = received_list + received + nonlocal loaded_wallets + loaded_wallets += 1 + if loaded_wallets == len(self.wallets): + logging.debug("All wallets loaded") + self._refreshing = False + self.loading_finished.emit(received_list) + for w in self.wallets: + w.refresh_progressed.disconnect(progressing) + w.refresh_finished.disconnect(wallet_finished) + + for w in self.wallets: + w.refresh_progressed.connect(progressing) + w.refresh_finished.connect(wallet_finished) + w.init_cache(community) + w.refresh_transactions(community, received_list) def set_display_referential(self, index): self.referential = index + def identity(self, community): + """ + Get the account identity in the specified community + :param cutecoin.core.community.Community community: The community where to look after the identity + :return: The account identity in the community + :rtype: cutecoin.core.registry.Identity + """ + identity = self._identities_registry.lookup(self.pubkey, community) + if identity.status == Identity.NOT_FOUND: + identity.uid = self.name + return identity + @property def units_to_ref(self): return Account.referentials[self.referential][0] @@ -268,131 +324,82 @@ class Account(QObject): return Account.referentials[self.referential][5] 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 {0}".format(i), self._identities_registry) 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 cutecoin.core.community.Community community: The community target of the certification - :param str pubkey: The certified identity pubkey - """ - certified = Person.lookup(pubkey, community) - blockid = community.current_blockid() - - certification = Certification(PROTOCOL_VERSION, community.currency, - self.pubkey, certified.pubkey, - blockid['number'], blockid['hash'], None) - - selfcert = certified.selfcert(community) - logging.debug("SelfCertification : {0}".format(selfcert.raw())) - - key = SigningKey(self.salt, password) - certification.sign(selfcert, [key]) - signed_cert = certification.signed_raw(selfcert) - logging.debug("Certification : {0}".format(signed_cert)) - - data = {'pubkey': certified.pubkey, - 'self_': selfcert.signed_raw(), - 'other': "{0}\n".format(certification.inline())} - logging.debug("Posted data : {0}".format(data)) - community.broadcast(bma.wot.Add, {}, data) - - def revoke(self, password, community): - """ - Revoke self-identity on server, not in blockchain - - :param str password: The account SigningKey password - :param cutecoin.core.community.Community community: The community target of the revocation - """ - revoked = Person.lookup(self.pubkey, community) - - revocation = Revocation(PROTOCOL_VERSION, community.currency, None) - - selfcert = revoked.selfcert(community) - - key = SigningKey(self.salt, password) - revocation.sign(selfcert, [key]) - - logging.debug("Self-Revocation Document : \n{0}".format(revocation.raw(selfcert))) - logging.debug("Signature : \n{0}".format(revocation.signatures[0])) - - data = { - 'pubkey': revoked.pubkey, - 'self_': selfcert.signed_raw(), - 'sig': revocation.signatures[0] - } - logging.debug("Posted data : {0}".format(data)) - community.broadcast(bma.wot.Revoke, {}, data) + self.wallets_changed.emit() 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: sent.extend(w.transfers(community)) return sent - def amount(self, community): - ''' + def dividends(self, community): + """ + Get all dividends received in this community + by the first wallet of this account + + :param community: The target community + :return: All account dividends + """ + return self.wallets[0].dividends(community) + + @asyncio.coroutine + def future_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) + val = yield from w.future_value(community) + value += val return value - def published_uid(self, community): - ''' - 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.published_uid(community) - - def member_of(self, community): - ''' - Check if this account identity is a member of a community + 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: True if the account is a member of the target community - ''' - self_person = Person.lookup(self.pubkey, community) - logging.debug("Self person : {0}".format(self_person.uid)) - return self_person.is_member(community) + :return: The value of all wallets values accumulated + """ + value = 0 + for w in self.wallets: + val = w.value(community) + value += val + return value + @asyncio.coroutine def send_selfcert(self, password, community): - ''' + """ Send our self certification to a target 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, @@ -402,22 +409,54 @@ class Account(QObject): key = SigningKey(self.salt, password) selfcert.sign([key]) logging.debug("Key publish : {0}".format(selfcert.signed_raw())) - community.broadcast(bma.wot.Add, {}, {'pubkey': self.pubkey, + replies = community.broadcast(qtbma.wot.Add, {}, {'pubkey': self.pubkey, 'self_': selfcert.signed_raw(), 'other': []}) + for r in replies: + r.finished.connect(lambda reply=r: self.__handle_selfcert_replies(replies, reply)) + def __handle_selfcert_replies(self, replies, reply): + """ + Handle the reply, if the request was accepted, disconnect + all other replies + + :param QNetworkReply reply: The reply of this handler + :param list of QNetworkReply replies: All request replies + :return: + """ + strdata = bytes(reply.readAll()).decode('utf-8') + logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata)) + if reply.error() == QNetworkReply.NoError: + for r in replies: + try: + r.disconnect() + except TypeError as e: + if "disconnect()" in str(e): + logging.debug("Could not disconnect a reply") + else: + raise + self.selfcert_broadcasted.emit() + else: + for r in replies: + if not r.isFinished() or r.error() == QNetworkReply.NoError: + return + self.broadcast_error.emit(r.error(), strdata) + + @asyncio.coroutine def send_membership(self, password, community, mstype): - ''' - Send a membership document to a target community + """ + Send a membership document to a target community. + Signal "document_broadcasted" is emitted at the end. :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) + """ + logging.debug("Send membership") - blockid = community.current_blockid() + blockid = yield from community.blockid() + self_identity = yield from self._identities_registry.future_lookup(self.pubkey, community) + selfcert = yield from self_identity.selfcert(community) membership = Membership(PROTOCOL_VERSION, community.currency, selfcert.pubkey, blockid['number'], @@ -426,15 +465,164 @@ class Account(QObject): key = SigningKey(self.salt, password) membership.sign([key]) logging.debug("Membership : {0}".format(membership.signed_raw())) - community.broadcast(bma.blockchain.Membership, {}, + replies = community.bma_access.broadcast(qtbma.blockchain.Membership, {}, {'membership': membership.signed_raw()}) + for r in replies: + r.finished.connect(lambda reply=r: self.__handle_membership_replies(replies, reply)) + + def __handle_membership_replies(self, replies, reply): + """ + Handle the reply, if the request was accepted, disconnect + all other replies + + :param QNetworkReply reply: The reply of this handler + :param list of QNetworkReply replies: All request replies + :return: + """ + strdata = bytes(reply.readAll()).decode('utf-8') + logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata)) + if reply.error() == QNetworkReply.NoError: + for r in replies: + try: + r.disconnect() + except TypeError as e: + if "disconnect()" in str(e): + logging.debug("Could not disconnect a reply") + else: + raise + self.membership_broadcasted.emit() + else: + for r in replies: + if not r.isFinished() or r.error() == QNetworkReply.NoError: + return + self.broadcast_error.emit(r.error(), strdata) + + @asyncio.coroutine + def certify(self, password, community, pubkey): + """ + Certify an other identity + + :param str password: The account SigningKey password + :param cutecoin.core.community.Community community: The community target of the certification + :param str pubkey: The certified identity pubkey + """ + logging.debug("Certdata") + blockid = yield from community.blockid() + identity = yield from self._identities_registry.future_lookup(pubkey, community) + selfcert = yield from identity.selfcert(community) + certification = Certification(PROTOCOL_VERSION, community.currency, + self.pubkey, pubkey, + blockid['number'], blockid['hash'], None) + + key = SigningKey(self.salt, password) + certification.sign(selfcert, [key]) + signed_cert = certification.signed_raw(selfcert) + logging.debug("Certification : {0}".format(signed_cert)) + + data = {'pubkey': pubkey, + 'self_': selfcert.signed_raw(), + 'other': "{0}\n".format(certification.inline())} + logging.debug("Posted data : {0}".format(data)) + replies = community.bma_access.broadcast(qtbma.wot.Add, {}, data) + for r in replies: + r.finished.connect(lambda reply=r: self.__handle_certification_reply(replies, reply)) + + def __handle_certification_reply(self, replies, reply): + """ + Handle the reply, if the request was accepted, disconnect + all other replies + + :param QNetworkReply reply: The reply of this handler + :param list of QNetworkReply replies: All request replies + :return: + """ + strdata = bytes(reply.readAll()).decode('utf-8') + logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata)) + if reply.error() == QNetworkReply.NoError: + for r in replies: + try: + r.disconnect() + except TypeError as e: + if "disconnect()" in str(e): + logging.debug("Could not disconnect a reply") + else: + raise + self.certification_broadcasted.emit() + else: + for r in replies: + if not r.isFinished() or r.error() == QNetworkReply.NoError: + return + self.broadcast_error.emit(r.error(), strdata) + + @asyncio.coroutine + def revoke(self, password, community): + """ + Revoke self-identity on server, not in blockchain + + :param str password: The account SigningKey password + :param cutecoin.core.community.Community community: The community target of the revocation + """ + revoked = yield from self._identities_registry.future_lookup(self.pubkey, community) + + revocation = Revocation(PROTOCOL_VERSION, community.currency, None) + selfcert = revoked.selfcert(community) + + key = SigningKey(self.salt, password) + revocation.sign(selfcert, [key]) + + logging.debug("Self-Revocation Document : \n{0}".format(revocation.raw(selfcert))) + logging.debug("Signature : \n{0}".format(revocation.signatures[0])) + + data = { + 'pubkey': revoked.pubkey, + 'self_': selfcert.signed_raw(), + 'sig': revocation.signatures[0] + } + logging.debug("Posted data : {0}".format(data)) + replies = community.broadcast(qtbma.wot.Revoke, {}, data) + for r in replies: + r.finished.connect(lambda reply=r: self.__handle_certification_reply(replies, reply)) + + def __handle_revoke_reply(self, replies, reply): + """ + Handle the reply, if the request was accepted, disconnect + all other replies + + :param QNetworkReply reply: The reply of this handler + :param list of QNetworkReply replies: All request replies + :return: + """ + strdata = bytes(reply.readAll()).decode('utf-8') + logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata)) + if reply.error() == QNetworkReply.NoError: + for r in replies: + try: + r.disconnect() + except TypeError as e: + if "disconnect()" in str(e): + logging.debug("Could not disconnect a reply") + else: + raise + self.revoke_broadcasted.emit() + else: + for r in replies: + if not r.isFinished() or r.error() == QNetworkReply.NoError: + return + self.broadcast_error.emit(r.error(), strdata) + + def stop_coroutines(self): + for c in self.communities: + c.stop_coroutines() + + for w in self.wallets: + w.stop_coroutines() def jsonify(self): - ''' + """ Get the account in a json format. :return: A dict view of the account to be saved as json - ''' + """ data_communities = [] for c in self.communities: data_communities.append(c.jsonify()) @@ -450,7 +638,3 @@ class Account(QObject): 'wallets': data_wallets, 'contacts': self.contacts} return data - - def get_person(self): - return Person.from_metadata({'text': self.name, - 'id': self.pubkey}) diff --git a/src/cutecoin/core/app.py b/src/cutecoin/core/app.py index ca266b27eb0dc05e0d6f04c784917fe53e29826d..7b8195377cd8740a65aa63716ba1e11fe979d96d 100644 --- a/src/cutecoin/core/app.py +++ b/src/cutecoin/core/app.py @@ -1,8 +1,8 @@ -''' +""" Created on 1 févr. 2014 @author: inso -''' +""" import os import logging @@ -18,47 +18,74 @@ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkReques from . import config from .account import Account -from . import person -from .watching.monitor import Monitor +from .registry.identities import IdentitiesRegistry from .. import __version__ from ..tools.exceptions import NameAlreadyExists, BadAccountFile class Application(QObject): - ''' + """ Managing core application datas : Accounts list and general configuration Saving and loading the application state - ''' + """ - loading_progressed = pyqtSignal(int, int) version_requested = pyqtSignal() - def __init__(self, argv, qapp): - ''' + def __init__(self, qapp, loop, network_manager, identities_registry): + """ Create a new "cutecoin" application :param argv: The argv parameters of the call - ''' + """ super().__init__() + self.qapp = qapp self.accounts = {} self.current_account = None - self.monitor = None + self.loop = loop self.available_version = (True, __version__, "") - config.parse_arguments(argv) - self._network_manager = QNetworkAccessManager() - self._network_manager.finished.connect(self.read_available_version) + self._identities_registry = identities_registry + self._network_manager = network_manager self.preferences = {'account': "", 'lang': 'en_GB', - 'ref': 0 + 'ref': 0, + 'expert_mode': False, + 'digits_after_comma': 6, + 'maximized': False, + 'notifications': True } - self.load() - - translator = QTranslator(qapp) + @classmethod + def startup(cls, argv, qapp, loop): + config.parse_arguments(argv) + network_manager = QNetworkAccessManager() + identities_registry = IdentitiesRegistry() + app = cls(qapp, loop, network_manager, identities_registry) + app.load() + app.switch_language() + if app.preferences["account"] != "": + account = app.get_account(app.preferences["account"]) + app.change_current_account(account) + # no default account... + else: + # if at least one account exists, set it as default... + if len(app.accounts) > 0: + # capture names sorted alphabetically + names = list(app.accounts.keys()) + names.sort() + # set first name in list as default in preferences + app.preferences['account'] = names[0] + app.save_preferences(app.preferences) + # open it + logging.debug("No default account in preferences. Set %s as default account." % names[0]) + + return app + + def switch_language(self): + translator = QTranslator(self.qapp) logging.debug("Loading translations") locale = self.preferences['lang'] QLocale.setDefault(QLocale(locale)) @@ -69,42 +96,50 @@ class Application(QObject): logging.debug("Couldn't load translation") 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(): + self.load_account(name) return self.accounts[name] else: 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) + account = Account.create(name, self._identities_registry) return account + @property + def identities_registry(self): + return self._identities_registry + + @property + def network_manager(self): + return self._network_manager + def add_account(self, account): 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 @@ -115,35 +150,35 @@ class Application(QObject): self.save_preferences(self.preferences) def change_current_account(self, account): - ''' + """ Change current account displayed and refresh its cache. - :param account: The account object to display + :param cutecoin.core.Account 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) - + """ if self.current_account is not None: - if self.monitor: - self.monitor.stop_watching() - self.save_cache(self.current_account) - account.loading_progressed.connect(progressing) - account.refresh_cache() - self.monitor = Monitor(account) - self.monitor.prepare_watching() + self.stop_current_account() + self.current_account = account + def stop_current_account(self): + """ + Save the account to the cache + and stop the coroutines + """ + self.save_cache(self.current_account) + self.current_account.stop_coroutines() + 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() + """ + self.load_registries() self.load_preferences() try: logging.debug("Loading data...") @@ -154,44 +189,44 @@ class Application(QObject): except FileNotFoundError: pass - def load_persons(self): - ''' + def load_registries(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__') - with open(persons_path, 'r') as persons_path: - data = json.load(persons_path) - person.load_cache(data) + identities_path = os.path.join(config.parameters['home'], + '__identities__') + with open(identities_path, 'r') as identities_data: + data = json.load(identities_data) + self._identities_registry.load_json(data) except FileNotFoundError: 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: data = json.load(json_data) - account = Account.load(data) + account = Account.load(data, self._network_manager, self._identities_registry) self.load_cache(account) 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'], + bma_path = os.path.join(config.parameters['home'], account.name, '__cache__', - community.currency) + community.currency + '_bma') network_path = os.path.join(config.parameters['home'], account.name, '__cache__', @@ -202,21 +237,23 @@ class Application(QObject): data = json.load(json_data) if 'version' in data and data['version'] == __version__: logging.debug("Merging network : {0}".format(data)) - community.load_merge_network(data['network']) + community.network.merge_with_json(data['network']) else: os.remove(network_path) - if os.path.exists(community_path): - with open(community_path, 'r') as json_data: + if os.path.exists(bma_path): + with open(bma_path, 'r') as json_data: data = json.load(json_data) if 'version' in data and data['version'] == __version__: - community.load_cache(data) + community.bma_access.load_from_json(data['cache']) else: - os.remove(community_path) + os.remove(bma_path) for wallet in account.wallets: + for c in account.communities: + wallet.init_cache(c) wallet_path = os.path.join(config.parameters['home'], - account.name, '__cache__', wallet.pubkey) + account.name, '__cache__', wallet.pubkey + "_wal") if os.path.exists(wallet_path): with open(wallet_path, 'r') as json_data: data = json.load(json_data) @@ -226,25 +263,26 @@ class Application(QObject): os.remove(wallet_path) def load_preferences(self): - ''' + """ Load the preferences. - ''' + """ try: preferences_path = os.path.join(config.parameters['home'], 'preferences') with open(preferences_path, 'r') as json_data: data = json.load(json_data) - self.preferences = data + for key in data: + self.preferences[key] = data[key] except FileNotFoundError: pass def save_preferences(self, preferences): - ''' + """ Save the preferences. :param preferences: A dict containing the keys/values of the preferences - ''' + """ assert('lang' in preferences) assert('account' in preferences) assert('ref' in preferences) @@ -256,11 +294,11 @@ class Application(QObject): json.dump(preferences, outfile, indent=4) 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'], @@ -276,14 +314,14 @@ class Application(QObject): account_path = os.path.join(config.parameters['home'], account.name) 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: - data = person.jsonify_cache() + def save_registries(self): + """ + Save the registries + """ + identities_path = os.path.join(config.parameters['home'], + '__identities__') + with open(identities_path, 'w')as outfile: + data = self.identities_registry.jsonify() data['version'] = __version__ json.dump(data, outfile, indent=4, sort_keys=True) @@ -299,18 +337,18 @@ class Application(QObject): os.makedirs(os.path.join(config.parameters['home'], account.name, '__cache__')) wallet_path = os.path.join(config.parameters['home'], - account.name, '__cache__', wallet.pubkey) + account.name, '__cache__', wallet.pubkey + "_wal") with open(wallet_path, 'w') as outfile: data = wallet.jsonify_caches() data['version'] = __version__ 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'], @@ -319,9 +357,9 @@ class Application(QObject): self.save_wallet(account, wallet) for community in account.communities: - community_path = os.path.join(config.parameters['home'], + bma_path = os.path.join(config.parameters['home'], account.name, '__cache__', - community.currency) + community.currency + '_bma') network_path = os.path.join(config.parameters['home'], account.name, '__cache__', @@ -329,22 +367,22 @@ class Application(QObject): with open(network_path, 'w') as outfile: data = dict() - data['network'] = community.jsonify_network() + data['network'] = community.network.jsonify() data['version'] = __version__ json.dump(data, outfile, indent=4, sort_keys=True) - with open(community_path, 'w') as outfile: - data = community.jsonify_cache() + with open(bma_path, 'w') as outfile: + data['cache'] = community.bma_access.jsonify() data['version'] = __version__ json.dump(data, outfile, indent=4, sort_keys=True) def import_account(self, file, name): - ''' + """ Import an account from a tar file and open it :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) @@ -366,12 +404,12 @@ class Application(QObject): self.change_current_account(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'], @@ -379,11 +417,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: @@ -391,22 +429,30 @@ class Application(QObject): return data def jsonify(self): - ''' + """ Jsonify the app datas :return: The accounts of the app to format as json - ''' + """ data = {'local_accounts': self.jsonify_accounts()} return data + def stop(self): + if self.current_account: + self.stop_current_account() + + self.save_registries() + def get_last_version(self): url = QUrl("https://api.github.com/repos/ucoin-io/cutecoin/releases") request = QNetworkRequest(url) - self._network_manager.get(request) + reply = self._network_manager.get(request) + reply.finished.connect(self.read_available_version) @pyqtSlot(QNetworkReply) - def read_available_version(self, reply): + def read_available_version(self): latest = None + reply = self.sender() releases = reply.readAll().data().decode('utf-8') logging.debug(releases) if reply.error() == QNetworkReply.NoError: diff --git a/src/cutecoin/core/community.py b/src/cutecoin/core/community.py index be5e70adf47c298e1c9ad62eafcad290fb0669ef..367a97e2d06c6d44042166d0c154445821ee5b34 100644 --- a/src/cutecoin/core/community.py +++ b/src/cutecoin/core/community.py @@ -1,203 +1,103 @@ -''' +""" Created on 1 févr. 2014 @author: inso -''' +""" -from PyQt5.QtCore import QObject, pyqtSignal -from ucoinpy.api import bma -from ucoinpy.documents.block import Block -from ..tools.exceptions import NoPeerAvailable -from .net.node import Node -from .net.network import Network import logging -import inspect import hashlib import re import time -from requests.exceptions import RequestException - - -class Cache(): - _saved_requests = [str(bma.blockchain.Block), str(bma.blockchain.Parameters)] +import asyncio +import math - 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'] - cache_key = (key[0], key[1], key[2], key[3], key[4]) - self.data[cache_key] = entry['value'] - - self.latest_block = data['latest_block'] +from PyQt5.QtCore import QObject, pyqtSignal +from requests.exceptions import RequestException - 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()} - entries = [] - for d in data: - entries.append({'key': d, - 'value': data[d]}) - return {'latest_block': self.latest_block, - '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. - ''' - logging.debug("Refresh : {0}/{1}".format(self.latest_block, - self.community.network.latest_block)) - if self.latest_block < self.community.network.latest_block: - self.latest_block = self.community.network.latest_block - 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 = (str(request), - str(tuple(frozenset(sorted(req_args.keys())))), - str(tuple(frozenset(sorted(req_args.values())))), - str(tuple(frozenset(sorted(get_args.keys())))), - str(tuple(frozenset(sorted(get_args.values()))))) - - if cache_key not in self.data.keys(): - result = self.community.request(request, req_args, get_args, - cached=False) - # For block 0, we should have a different behaviour - # Community members and certifications - # Should be requested without caching - self.data[cache_key] = result - return self.data[cache_key] - else: - return self.data[cache_key] +from ucoinpy.documents.block import Block +from ..tools.exceptions import NoPeerAvailable +from .net.network import Network +from .net.api import bma as qtbma +from .net.api.bma.access import BmaAccess class Community(QObject): - ''' + """ 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. - ''' + """ + inner_data_changed = pyqtSignal(str) - def __init__(self, currency, network): - ''' + def __init__(self, currency, network, bma_access): + """ 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 + :param cutecoin.core.net.network.Network network: The network of the community + :param cutecoin.core.net.api.bma.access.BmaAccess bma_access: The BMA Access object .. warning:: The community object should be created using its factory class methods. - ''' + """ super().__init__() self.currency = currency self._network = network - self._cache = Cache(self) - self._cache.refresh() + self._bma_access = bma_access @classmethod - def create(cls, node): - ''' + def create(cls, network_manager, 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) + """ + network = Network.create(network_manager, node) + bma_access = BmaAccess.create(network) + community = cls(node.currency, network, bma_access) logging.debug("Creating community") return community @classmethod - def load(cls, json_data): - ''' + def load(cls, network_manager, 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) + network = Network.from_json(network_manager, currency, json_data['peers']) + bma_access = BmaAccess.create(network) + community = cls(currency, network, bma_access) return community - 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): - ''' + def load_cache(self, bma_access_cache, network_cache): + """ 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): - ''' - Get the network jsonified. - ''' - return self._network.jsonify() + :param dict bma_access_cache: The BmaAccess cache in json + :param dict network_cache: The network cache in json + """ + self._bma_access.load_from_json(bma_access_cache) + self._network.merge_with_json(network_cache) @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: @@ -210,74 +110,92 @@ class Community(QObject): @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 + #@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'] else: return 1 + @property + def computed_dividend(self): + """ + Get the computed community universal dividend. + + Calculation based on t = last UD block time and on values from the that block : + + UD(computed) = CEIL(MAX(UD(t) ; c * M(t) / N(t))) + + :return: The computed UD or 1 if no UD was generated. + """ + block = self.get_ud_block() + if block: + return math.ceil( + max( + self.dividend(), + float(0) if block['membersCount'] == 0 else + self.parameters['c'] * block['monetaryMass'] / block['membersCount'] + ) + ) + + else: + 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'] + """ + blocks = self.bma_access.get(self, qtbma.blockchain.UD)['result']['blocks'] if len(blocks) > 0: block_number = blocks[len(blocks)-(1+x)] - block = self.request(bma.blockchain.Block, + block = self.bma_access.get(self, qtbma.blockchain.Block, req_args={'number': block_number}) return block else: - return False + return None @property def monetary_mass(self): - ''' + """ Get the community monetary mass :return: The monetary mass value - ''' - try: - # Get cached block by block number - block_number = self.network.latest_block - block = self.request(bma.blockchain.Block, - req_args={'number': block_number}) - return block['monetaryMass'] - except ValueError as e: - if '404' in e: - return 0 - except NoPeerAvailable as e: - return 0 + """ + # Get cached block by block number + block_number = self.network.latest_block + block = self.bma_access.get(self, qtbma.blockchain.Block, + req_args={'number': block_number}) + return block['monetaryMass'] @property def nb_members(self): - ''' + """ Get the community members number :return: The community members number - ''' + """ try: # Get cached block by block number block_number = self.network.latest_block - block = self.request(bma.blockchain.Block, + block = self.bma_access.get(qtbma.blockchain.Block, req_args={'number': block_number}) return block['membersCount'] except ValueError as e: @@ -288,204 +206,98 @@ class Community(QObject): @property def network(self): - ''' + """ Get the community network instance. :return: The community network instance. - ''' + :rtype: cutecoin.core.net.network.Network + """ return self._network - def network_quality(self): - ''' - Get a ratio of the synced nodes vs the rest - ''' - synced = len(self._network.synced_nodes) - #online = len(self._network.online_nodes) - total = len(self._network.nodes) - ratio_synced = synced / total - return ratio_synced + @property + def bma_access(self): + """ + Get the community bma_access instance + + :return: The community bma_access instace + :rtype: cutecoin.core.net.api.bma.access.BmaAccess + """ + return self._bma_access @property def parameters(self): - ''' + """ Return community parameters in bma format - ''' - return self.request(bma.blockchain.Parameters) + """ + return self.bma_access.get(self, qtbma.blockchain.Parameters) def certification_expired(self, certtime): - ''' + """ Return True if the certificaton time is too old - ''' + """ return time.time() - certtime > self.parameters['sigValidity'] def add_node(self, node): - ''' + """ Add a peer to the community. :param peer: The new peer as a ucoinpy Peer object. - ''' + """ self._network.add_root_node(node) def remove_node(self, index): - ''' + """ Remove a node from the community. :param index: The index of the removed node. - ''' + """ self._network.remove_root_node(index) 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) + data = self.bma_access.get(self, qtbma.blockchain.Current) else: logging.debug("Requesting block {0}".format(number)) - data = self.request(bma.blockchain.Block, + data = self.bma_access.get(self, qtbma.blockchain.Block, req_args={'number': number}) + return data - return Block.from_signed_raw("{0}{1}\n".format(data['raw'], - data['signature'])) - - def current_blockid(self): - ''' - Get the current block id. + @asyncio.coroutine + def blockid(self): + """ + Get the 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']) - block_hash = hashlib.sha1(signed_raw.encode("ascii")).hexdigest().upper() - block_number = block['number'] - except ValueError as e: - if '404' in str(e): - block_number = 0 - block_hash = "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709" - else: - raise + """ + block = yield from self.bma_access.future_request(qtbma.blockchain.Current) + signed_raw = "{0}{1}\n".format(block['raw'], block['signature']) + block_hash = hashlib.sha1(signed_raw.encode("ascii")).hexdigest().upper() + block_number = block['number'] return {'number': block_number, 'hash': block_hash} def members_pubkeys(self): - ''' + """ Listing members pubkeys of a community :return: All members pubkeys. - ''' - memberships = self.request(bma.wot.Members) + """ + memberships = self.bma_access.get(self, qtbma.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: - nodes = self._network.synced_nodes - for node in nodes: - try: - req = request(node.endpoint.conn_handler(), **req_args) - data = req.get(**get_args) - - if inspect.isgenerator(data): - generated = [] - for d in data: - generated.append(d) - return generated - else: - return data - except ValueError as e: - if '502' in str(e): - continue - else: - raise - except RequestException as e: - logging.debug("Error : {1} : {0}".format(str(e), - str(request))) - continue - 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) - logging.debug("Trying to connect to : " + node.pubkey) - req = request(node.endpoint.conn_handler(), **req_args) - try: - req.post(**post_args) - return - except ValueError as e: - raise - except RequestException: - continue - 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 - nodes = self._network.online_nodes - for node in nodes: - logging.debug("Trying to connect to : " + node.pubkey) - req = request(node.endpoint.conn_handler(), **req_args) - try: - req.post(**post_args) - ok = True - except ValueError as e: - value_error = e - continue - except RequestException: - tries = tries + 1 - continue - - if not ok: - raise value_error - - if tries == len(nodes): - raise NoPeerAvailable(self.currency, len(nodes)) + def stop_coroutines(self): + self.network.stop_coroutines() def jsonify(self): - ''' + """ Jsonify the community datas. :return: The community as a dict in json format. - ''' + """ nodes_data = [] for node in self._network.root_nodes: diff --git a/src/cutecoin/core/config.py b/src/cutecoin/core/config.py index 3f35a2550146a95f62a0ac582008859d25aeb341..8a4202d20e3481342de0b88d55335eebc5ff3808 100644 --- a/src/cutecoin/core/config.py +++ b/src/cutecoin/core/config.py @@ -1,8 +1,8 @@ -''' +""" Created on 7 févr. 2014 @author: inso -''' +""" import logging from logging import FileHandler diff --git a/src/cutecoin/core/graph.py b/src/cutecoin/core/graph.py index 01b8b4af412fd1aa1ac9bda6fb3b441fdfcabcea..64d9955f8036f51c43813aafaf9ab815a7bc2caf 100644 --- a/src/cutecoin/core/graph.py +++ b/src/cutecoin/core/graph.py @@ -2,7 +2,7 @@ import logging import time import datetime from PyQt5.QtCore import QLocale, QDateTime -from cutecoin.core.person import Person +from ..core.registry import Identity from cutecoin.gui.views.wot import NODE_STATUS_HIGHLIGHTED, NODE_STATUS_OUT, ARC_STATUS_STRONG, ARC_STATUS_WEAK @@ -36,29 +36,29 @@ class Graph(object): """ return self._graph - def get_shortest_path_between_members(self, from_person, to_person): + def get_shortest_path_between_members(self, from_identity, to_identity): """ - Return path list of nodes from from_person to to_person - :param Person from_person: - :param Person to_person: + Return path list of nodes from from_identity to to_identity + :param identity from_identity: + :param identity to_identity: :return: """ path = list() - logging.debug("path between %s to %s..." % (from_person.uid, to_person.uid)) - if from_person.pubkey not in self._graph.keys(): - self.add_person(from_person) - certifier_list = from_person.certifiers_of(self.community) - self.add_certifier_list(certifier_list, from_person, to_person) - certified_list = from_person.certified_by(self.community) - self.add_certified_list(certified_list, from_person, to_person) + logging.debug("path between %s to %s..." % (from_identity.uid, to_identity.uid)) + if from_identity.pubkey not in self._graph.keys(): + self.add_identity(from_identity) + certifier_list = from_identity.certifiers_of(self.community) + self.add_certifier_list(certifier_list, from_identity, to_identity) + certified_list = from_identity.certified_by(self.community) + self.add_certified_list(certified_list, from_identity, to_identity) - if to_person.pubkey not in self._graph.keys(): + if to_identity.pubkey not in self._graph.keys(): # recursively feed graph searching for account node... - self.explore_to_find_member(to_person, self._graph[from_person.pubkey]['connected'], list()) - if len(self._graph[from_person.pubkey]['connected']) > 0: - # calculate path of nodes between person and to_person - path = self.find_shortest_path(self._graph[from_person.pubkey], self._graph[to_person.pubkey]) + self.explore_to_find_member(to_identity, self._graph[from_identity.pubkey]['connected'], list()) + if len(self._graph[from_identity.pubkey]['connected']) > 0: + # calculate path of nodes between identity and to_identity + path = self.find_shortest_path(self._graph[from_identity.pubkey], self._graph[to_identity.pubkey]) if path: logging.debug([node['text'] for node in path]) @@ -67,10 +67,10 @@ class Graph(object): return path - def explore_to_find_member(self, person, connected=None, done=None): + def explore_to_find_member(self, identity, connected=None, done=None): """ - Scan graph recursively to find person - :param Person person: Person instance to find + Scan graph recursively to find identity + :param identity identity: identity instance to find :param list connected: Optional, default=None, Pubkey list of the connected nodes around the current scanned node :param list done: Optional, default=None, List of node already scanned @@ -79,7 +79,7 @@ class Graph(object): # functions keywords args are persistent... Need to reset it with None trick connected = connected or (list() and (connected is None)) done = done or (list() and (done is None)) - logging.debug("search %s in " % person.uid) + logging.debug("search %s in " % identity.uid) logging.debug([self._graph[pubkey]['text'] for pubkey in connected]) # for each pubkey connected... for pubkey in tuple(connected): @@ -87,20 +87,20 @@ class Graph(object): node = self._graph[pubkey] if node['id'] in tuple(done): continue - person_selected = Person.from_metadata(node) - certifier_list = person_selected.certifiers_of(self.community) - self.add_certifier_list(certifier_list, person_selected, person) - if person.pubkey in tuple(self._graph.keys()): + identity_selected = identity.from_metadata(node) + certifier_list = identity_selected.certifiers_of(self.community) + self.add_certifier_list(certifier_list, identity_selected, identity) + if identity.pubkey in tuple(self._graph.keys()): return False - certified_list = person_selected.certified_by(self.community) - self.add_certified_list(certified_list, person_selected, person) - if person.pubkey in tuple(self._graph.keys()): + certified_list = identity_selected.certified_by(self.community) + self.add_certified_list(certified_list, identity_selected, identity) + if identity.pubkey in tuple(self._graph.keys()): return False if node['id'] not in tuple(done): done.append(node['id']) if len(done) >= len(self._graph): return True - result = self.explore_to_find_member(person, self._graph[person_selected.pubkey]['connected'], done) + result = self.explore_to_find_member(identity, self._graph[identity_selected.pubkey]['connected'], done) if not result: return False @@ -130,12 +130,12 @@ class Graph(object): shortest = newpath return shortest - def add_certifier_list(self, certifier_list, person, person_account): + def add_certifier_list(self, certifier_list, identity, identity_account): """ Add list of certifiers to graph :param list certifier_list: List of certifiers from api - :param Person person: Person instance which is certified - :param Person person_account: Account person instance + :param identity identity: identity instance which is certified + :param identity identity_account: Account identity instance :return: """ #  add certifiers of uid @@ -146,7 +146,7 @@ class Graph(object): # new node if certifier['pubkey'] not in self._graph.keys(): node_status = 0 - if certifier['pubkey'] == person_account.pubkey: + if certifier['pubkey'] == identity_account.pubkey: node_status += NODE_STATUS_HIGHLIGHTED if certifier['isMember'] is False: node_status += NODE_STATUS_OUT @@ -156,7 +156,7 @@ class Graph(object): 'text': certifier['uid'], 'tooltip': certifier['pubkey'], 'status': node_status, - 'connected': [person.pubkey] + 'connected': [identity.pubkey] } # keep only the latest certification @@ -169,7 +169,7 @@ class Graph(object): else: arc_status = ARC_STATUS_STRONG arc = { - 'id': person.pubkey, + 'id': identity.pubkey, 'status': arc_status, 'tooltip': QLocale.toString( QLocale(), @@ -180,17 +180,17 @@ class Graph(object): } #  add arc to certifier self._graph[certifier['pubkey']]['arcs'].append(arc) - # if certifier node not in person nodes - if certifier['pubkey'] not in tuple(self._graph[person.pubkey]['connected']): - # add certifier node to person node - self._graph[person.pubkey]['connected'].append(certifier['pubkey']) + # if certifier node not in identity nodes + if certifier['pubkey'] not in tuple(self._graph[identity.pubkey]['connected']): + # add certifier node to identity node + self._graph[identity.pubkey]['connected'].append(certifier['pubkey']) - def add_certified_list(self, certified_list, person, person_account): + def add_certified_list(self, certified_list, identity, identity_account): """ Add list of certified from api to graph :param list certified_list: List of certified from api - :param Person person: Person instance which is certifier - :param Person person_account: Account person instance + :param identity identity: identity instance which is certifier + :param identity identity_account: Account identity instance :return: """ # add certified by uid @@ -200,7 +200,7 @@ class Graph(object): continue if certified['pubkey'] not in self._graph.keys(): node_status = 0 - if certified['pubkey'] == person_account.pubkey: + if certified['pubkey'] == identity_account.pubkey: node_status += NODE_STATUS_HIGHLIGHTED if certified['isMember'] is False: node_status += NODE_STATUS_OUT @@ -210,7 +210,7 @@ class Graph(object): 'text': certified['uid'], 'tooltip': certified['pubkey'], 'status': node_status, - 'connected': [person.pubkey] + 'connected': [identity.pubkey] } # display validity status if (time.time() - certified['cert_time']['medianTime']) > self.ARC_STATUS_STRONG_time: @@ -231,42 +231,42 @@ class Graph(object): # replace old arc if this one is more recent new_arc = True index = 0 - for a in self._graph[person.pubkey]['arcs']: + for a in self._graph[identity.pubkey]['arcs']: # if same arc already exists... if a['id'] == arc['id']: # if arc more recent, dont keep old one... if arc['cert_time'] >= a['cert_time']: - self._graph[person.pubkey]['arcs'][index] = arc + self._graph[identity.pubkey]['arcs'][index] = arc new_arc = False index += 1 #  if arc not in graph... if new_arc: # add arc in graph - self._graph[person.pubkey]['arcs'].append(arc) - # if certified node not in person nodes - if certified['pubkey'] not in tuple(self._graph[person.pubkey]['connected']): - # add certified node to person node - self._graph[person.pubkey]['connected'].append(certified['pubkey']) + self._graph[identity.pubkey]['arcs'].append(arc) + # if certified node not in identity nodes + if certified['pubkey'] not in tuple(self._graph[identity.pubkey]['connected']): + # add certified node to identity node + self._graph[identity.pubkey]['connected'].append(certified['pubkey']) - def add_person(self, person, status=None, arcs=None, connected=None): + def add_identity(self, identity, status=None, arcs=None, connected=None): """ - Add person as a new node in graph - :param Person person: Person instance + Add identity as a new node in graph + :param identity identity: identity instance :param int status: Optional, default=None, Node status (see cutecoin.gui.views.wot) - :param list arcs: Optional, default=None, List of arcs (certified by person) - :param list connected: Optional, default=None, Public key list of the connected nodes around the person + :param list arcs: Optional, default=None, List of arcs (certified by identity) + :param list connected: Optional, default=None, Public key list of the connected nodes around the identity :return: """ # functions keywords args are persistent... Need to reset it with None trick status = status or (0 and (status is None)) arcs = arcs or (list() and (arcs is None)) connected = connected or (list() and (connected is None)) - self._graph[person.pubkey] = { - 'id': person.pubkey, + self._graph[identity.pubkey] = { + 'id': identity.pubkey, 'arcs': arcs, - 'text': person.uid, - 'tooltip': person.pubkey, + 'text': identity.uid, + 'tooltip': identity.pubkey, 'status': status, 'connected': connected } diff --git a/src/cutecoin/core/net/__init__.py b/src/cutecoin/core/net/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6ea6364fb9f0854431d8352f088cf781026b1a1d 100644 --- a/src/cutecoin/core/net/__init__.py +++ b/src/cutecoin/core/net/__init__.py @@ -0,0 +1,2 @@ +from .node import Node +from .network import Network \ No newline at end of file diff --git a/src/cutecoin/core/watching/__init__.py b/src/cutecoin/core/net/api/__init__.py similarity index 100% rename from src/cutecoin/core/watching/__init__.py rename to src/cutecoin/core/net/api/__init__.py diff --git a/src/cutecoin/core/net/api/bma/__init__.py b/src/cutecoin/core/net/api/bma/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1b6b94bcd252ff8a5c49a1a71636f956eeee2adb --- /dev/null +++ b/src/cutecoin/core/net/api/bma/__init__.py @@ -0,0 +1,129 @@ + + +__all__ = ['api'] + +from PyQt5.QtNetwork import QNetworkRequest +from PyQt5.QtCore import QUrl, QUrlQuery +import logging + +logger = logging.getLogger("ucoin") + +PROTOCOL_VERSION = "1" + +class ConnectionHandler(object): + """Helper class used by other API classes to ease passing server connection information.""" + + def __init__(self, network_manager, server, port): + """ + Arguments: + - `server`: server hostname + - `port`: port number + """ + + self.network_manager = network_manager + self.server = server + self.port = port + + def __str__(self): + return 'connection info: %s:%d' % (self.server, self.port) + + +class API(object): + """APIRequest is a class used as an interface. The intermediate derivated classes are the modules and the leaf classes are the API requests.""" + + def __init__(self, conn_handler, module): + """ + Asks a module in order to create the url used then by derivated classes. + + Arguments: + - `module`: module name + - `connection_handler`: connection handler + """ + + self.module = module + self.conn_handler = conn_handler + self.headers = {} + + def reverse_url(self, path): + """ + Reverses the url using self.url and path given in parameter. + + Arguments: + - `path`: the request path + """ + + server, port = self.conn_handler.server, self.conn_handler.port + + url = 'http://%s:%d/%s' % (server, port, self.module) + return url + path + + def get(self, **kwargs): + """wrapper of overloaded __get__ method.""" + + return self.__get__(**kwargs) + + def post(self, **kwargs): + """wrapper of overloaded __post__ method.""" + + logger.debug('do some work with') + + data = self.__post__(**kwargs) + + logger.debug('and send back') + + return data + + def __get__(self, **kwargs): + """interface purpose for GET request""" + + pass + + def __post__(self, **kwargs): + """interface purpose for POST request""" + + pass + + def requests_get(self, path, **kwargs): + """ + Requests GET wrapper in order to use API parameters. + + Arguments: + - `path`: the request path + """ + query = QUrlQuery() + for k,v in kwargs.items(): + query.addQueryItem(k, v); + url = QUrl(self.reverse_url(path)) + url.setQuery(query) + request = QNetworkRequest(url) + logging.debug(url.toString()) + reply = self.conn_handler.network_manager.get(request) + + return reply + + def requests_post(self, path, **kwargs): + """ + Requests POST wrapper in order to use API parameters. + + Arguments: + - `path`: the request path + """ + if 'self_' in kwargs: + kwargs['self'] = kwargs.pop('self_') + + logging.debug("POST : {0}".format(kwargs)) + post_data = QUrlQuery() + for k,v in kwargs.items(): + post_data.addQueryItem(k.replace("+", "%2b"), v.replace("+", "%2b")) + url = QUrl(self.reverse_url(path)) + url.setQuery(post_data) + + request = QNetworkRequest(url) + request.setHeader(QNetworkRequest.ContentTypeHeader, + "application/x-www-form-urlencoded") + reply = self.conn_handler.network_manager.post(request, + post_data.toString(QUrl.FullyEncoded).encode('utf-8')) + logging.debug(url.toString(QUrl.FullyEncoded)) + return reply + +from . import network, blockchain, tx, wot, ud, node \ No newline at end of file diff --git a/src/cutecoin/core/net/api/bma/access.py b/src/cutecoin/core/net/api/bma/access.py new file mode 100644 index 0000000000000000000000000000000000000000..0178cf92a8eb19868343fc9c88ef8216b0e2e355 --- /dev/null +++ b/src/cutecoin/core/net/api/bma/access.py @@ -0,0 +1,307 @@ +from PyQt5.QtCore import QObject, pyqtSlot +from PyQt5.QtNetwork import QNetworkReply +from . import blockchain, ConnectionHandler +from .....tools.exceptions import NoPeerAvailable +from ..... import __version__ +import logging +import json +import asyncio +import random + +class BmaAccess(QObject): + """ + This class is used to access BMA API. + """ + + __saved_requests = [str(blockchain.Block), str(blockchain.Parameters)] + + def __init__(self, data, network): + """ + Constructor of a network + + :param dict data: The data present in this cache + :param cutecoin.core.net.network.Network network: The network used to connect + """ + super().__init__() + self._data = data + self._pending_requests = {} + self._network = network + + @classmethod + def create(cls, network): + """ + Initialize a new BMAAccess object with empty data. + + :param cutecoin.core.net.network.Network network: + :return: A new BmaAccess object + :rtype: cutecoin.core.net.api.bma.access.BmaAccess + """ + return cls({}, network) + + @property + def data(self): + return self._data.copy() + + def load_from_json(self, json_data): + """ + Put data in the cache from json datas. + + :param dict data: The cache in json format + """ + data = {} + for entry in json_data: + key = entry['key'] + cache_key = (key[0], key[1], key[2], key[3], key[4]) + data[cache_key] = entry['value'] + self._data = data + + 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()} + entries = [] + for d in data: + entries.append({'key': d, + 'value': data[d]}) + return entries + + @staticmethod + def _gen_cache_key(request, req_args, get_args): + return (str(request), + str(tuple(frozenset(sorted(req_args.keys())))), + str(tuple(frozenset(sorted(req_args.values())))), + str(tuple(frozenset(sorted(get_args.keys())))), + str(tuple(frozenset(sorted(get_args.values()))))) + + def _compare_json(self, first, second): + """ + Compare two json dicts + :param first: the first dictionnary + :param second: the second dictionnary + :return: True if the json dicts are the same + :rtype: bool + """ + if first is not None: + if not isinstance(first, type(second)): + return False + if isinstance(first, dict): + for key in first: + if isinstance(second, dict): + if key in second: + sec = second[key] + else: + # there are key in the first, that is not presented in the second + return False + # recursive call + return self._compare_json(first[key], sec) + else: + # second is not dict + return False + # if object is list, loop over it and check. + elif isinstance(first, list): + for (index, item) in enumerate(first): + # try to get the same index from second + sec = None + if second is not None: + try: + sec = second[index] + except (IndexError, KeyError): + # goes to difference + return False + # recursive call + return self._compare_json(first[index], sec) + # not list, not dict. check for equality + elif first != second: + return False + else: + return True + + def _get_from_cache(self, request, req_args, get_args): + """ + Get data from the cache + :param request: The requested data + :param cache_key: The key + :return: + """ + cache_key = BmaAccess._gen_cache_key(request, req_args, get_args) + if cache_key in self._data.keys(): + need_reload = False + if 'metadata' in self._data[cache_key]: + if str(request) not in BmaAccess.__saved_requests \ + and self._data[cache_key]['metadata']['block'] < self._network.latest_block: + need_reload = True + else: + need_reload = True + ret_data = self._data[cache_key]['value'] + else: + need_reload = True + ret_data = request.null_value + return need_reload, ret_data + + def _update_cache(self, request, req_args, get_args, data): + """ + Update data in cache and returns True if cached data changed + :param class request: A bma request class calling for data + :param dict req_args: Arguments to pass to the request constructor + :param dict get_args: Arguments to pass to the request __get__ method + :param dict data: Json data to save in cache + :return: True if data changed + :rtype: bool + """ + cache_key = BmaAccess._gen_cache_key(request, req_args, get_args) + if cache_key not in self._data: + self._data[cache_key] = {} + + if 'metadata' not in self._data[cache_key]: + self._data[cache_key]['metadata'] = {} + + if 'value' not in self._data[cache_key]: + self._data[cache_key]['value'] = {} + + self._data[cache_key]['metadata']['block'] = self._network.latest_block + self._data[cache_key]['metadata']['cutecoin_version'] = __version__ + if not self._compare_json(self._data[cache_key]['value'], data): + self._data[cache_key]['value'] = data + return True + return False + + def get(self, caller, request, req_args={}, get_args={}, tries=0): + """ + Get Json data from the specified URL and emit "inner_data_changed" + on the caller if the data changed. + + :param PyQt5.QtCore.QObject caller: The objet calling + :param class request: A bma request class calling for data + :param dict req_args: Arguments to pass to the request constructor + :param dict get_args: Arguments to pass to the request __get__ method + :return: The cached data + :rtype: dict + """ + data = self._get_from_cache(request, req_args, get_args) + need_reload = data[0] + ret_data = data[1] + cache_key = BmaAccess._gen_cache_key(request, req_args, get_args) + + if need_reload: + #Move to network nstead of community + #after removing qthreads + if cache_key in self._pending_requests: + if caller not in self._pending_requests[cache_key]: + logging.debug("New caller".format(caller)) + self._pending_requests[cache_key].append(caller) + logging.debug("Callers".format(self._pending_requests[cache_key])) + else: + reply = self.simple_request(request, req_args, get_args) + logging.debug("New pending request {0}, caller {1}".format(cache_key, caller)) + self._pending_requests[cache_key] = [caller] + reply.finished.connect(lambda: self.handle_reply(request, req_args, get_args, tries)) + return ret_data + + @pyqtSlot(int, dict, dict, int) + def handle_reply(self, request, req_args, get_args, tries): + reply = self.sender() + logging.debug("Handling QtNetworkReply for {0}".format(str(request))) + cache_key = BmaAccess._gen_cache_key(request, req_args, get_args) + + if reply.error() == QNetworkReply.NoError: + strdata = bytes(reply.readAll()).decode('utf-8') + json_data = json.loads(strdata) + # If data changed, we emit a change signal to all callers + if self._update_cache(request, req_args, get_args, json_data): + logging.debug(self._pending_requests.keys()) + for caller in self._pending_requests[cache_key]: + logging.debug("Emit change for {0} : {1} ".format(caller, request)) + caller.inner_data_changed.emit(str(request)) + self._pending_requests.pop(cache_key) + else: + logging.debug("Error in reply : {0}".format(reply.error())) + if tries < 3: + tries += 1 + try: + pending_request = self._pending_requests.pop(cache_key) + for caller in pending_request: + self.get(caller, request, req_args, get_args, tries=tries) + except KeyError: + logging.debug("{0} is not present anymore in pending requests".format(cache_key)) + + def future_request(self, request, req_args={}, get_args={}): + """ + Start a request to the network and returns a future. + + :param class request: A bma request class calling for data + :param dict req_args: Arguments to pass to the request constructor + :param dict get_args: Arguments to pass to the request __get__ method + :return: The future data + :rtype: dict + """ + def handle_future_reply(reply): + if reply.error() == QNetworkReply.NoError: + strdata = bytes(reply.readAll()).decode('utf-8') + json_data = json.loads(strdata) + self._update_cache(request, req_args, get_args, json_data) + future_data.set_result(json_data) + else: + future_data.set_result(request.null_value) + + future_data = asyncio.Future() + data = self._get_from_cache(request, req_args, get_args) + need_reload = data[0] + + if need_reload: + nodes = self._network.synced_nodes + if len(nodes) > 0: + node = random.choice(nodes) + conn_handler = node.endpoint.conn_handler(self._network.network_manager) + req = request(conn_handler, **req_args) + reply = req.get(**get_args) + reply.finished.connect(lambda: handle_future_reply(reply)) + else: + raise NoPeerAvailable(self.currency, len(nodes)) + else: + future_data.set_result(data[1]) + return future_data + + def simple_request(self, request, req_args={}, get_args={}): + """ + Start a request to the network but don't cache its result. + + :param class request: A bma request class calling for data + :param dict req_args: Arguments to pass to the request constructor + :param dict get_args: Arguments to pass to the request __get__ method + :return: The returned data if cached = True else return the QNetworkReply + """ + nodes = self._network.synced_nodes + if len(nodes) > 0: + node = random.choice(nodes) + req = request(node.endpoint.conn_handler(self._network.network_manager), **req_args) + reply = req.get(**get_args) + return reply + else: + raise NoPeerAvailable(self.currency, len(nodes)) + + def broadcast(self, request, req_args={}, post_args={}): + """ + Broadcast data to a network. + 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: All nodes replies + :rtype: tuple of QNetworkReply + + .. note:: If one node accept the requests (returns 200), + the broadcast should be considered accepted by the network. + """ + nodes = self._network.online_nodes + replies = [] + for node in nodes: + logging.debug("Trying to connect to : " + node.pubkey) + conn_handler = node.endpoint.conn_handler(self._network.network_manager) + req = request(conn_handler, **req_args) + reply = req.post(**post_args) + replies.append(reply) + return tuple(replies) diff --git a/src/cutecoin/core/net/api/bma/blockchain/__init__.py b/src/cutecoin/core/net/api/bma/blockchain/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..eecdfa3de4798cc7a24ef953e7ff87bea95fed7c --- /dev/null +++ b/src/cutecoin/core/net/api/bma/blockchain/__init__.py @@ -0,0 +1,251 @@ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Caner Candan <caner@candan.fr>, http://caner.candan.fr +# + +from .. import API, logging + +logger = logging.getLogger("ucoin/blockchain") + + +class Blockchain(API): + def __init__(self, conn_handler, module='blockchain'): + super(Blockchain, self).__init__(conn_handler, module) + + +class Parameters(Blockchain): + """GET the blockchain parameters used by this node.""" + + def __get__(self, **kwargs): + return self.requests_get('/parameters', **kwargs) + + null_value = { + 'currency': "", + 'c': 0, + 'dt': 0, + 'ud0': 0, + 'sigDelay': 0, + 'sigValidity': 0, + 'sigQty': 0, + 'sigWoT': 0, + 'msValidity': 0, + 'stepMax': 0, + 'medianTimeBlocks': 0, + 'avgGenTime': 0, + 'dtDiffEval': 0, + 'blocksRot': 0, + 'percentRot': 0 + } + + +class Membership(Blockchain): + """GET/POST a Membership document.""" + + null_value = \ + { + "pubkey": "", + "uid": "", + "sigDate": 0, + "memberships": [] + } + + def __init__(self, conn_handler, search=None): + super().__init__(conn_handler) + self.search = search + + def __post__(self, **kwargs): + assert 'membership' in kwargs + + return self.requests_post('/membership', **kwargs) + + def __get__(self, **kwargs): + assert self.search is not None + return self.requests_get('/memberships/%s' % self.search, **kwargs) + + +class Block(Blockchain): + """GET/POST a block from/to the blockchain.""" + null_value = { + "version": 1, + "nonce": 0, + "number": -1, + "powMin": 0, + "time": 0, + "medianTime": 0, + "membersCount": 0, + "monetaryMass": 0, + "currency": "", + "issuer": "", + "signature": "", + "hash": "", + "previousHash": "", + "previousIssuer": "", + "dividend": 0, + "membersChanges": [], + "identities": [], + "joiners": [], + "actives": [], + "leavers": [], + "excluded": [], + "certifications": [], + "transactions": [], + "raw": "" + } + + def __init__(self, conn_handler, number=None): + """ + Use the number parameter in order to select a block number. + + Arguments: + - `number`: block number to select + """ + + super(Block, self).__init__(conn_handler) + + self.number = number + + def __get__(self, **kwargs): + assert self.number is not None + return self.requests_get('/block/%d' % self.number, **kwargs) + + def __post__(self, **kwargs): + assert 'block' in kwargs + assert 'signature' in kwargs + + return self.requests_post('/block', **kwargs) + + +class Current(Blockchain): + """GET, same as block/[number], but return last accepted block.""" + null_value = { + "version": 1, + "nonce": 0, + "number": -1, + "powMin": 0, + "time": 0, + "medianTime": 0, + "membersCount": 0, + "monetaryMass": 0, + "currency": "", + "issuer": "", + "signature": "", + "hash": "", + "previousHash": None, + "previousIssuer": None, + "dividend": None, + "membersChanges": [], + "identities": [], + "joiners": [], + "actives": [], + "leavers": [], + "excluded": [], + "certifications": [], + "transactions": [], + "raw": "" + } + + def __get__(self, **kwargs): + return self.requests_get('/current', **kwargs) + + +class Hardship(Blockchain): + """GET hardship level for given member's fingerprint for writing next block.""" + + def __init__(self, conn_handler, fingerprint): + """ + Use the number parameter in order to select a block number. + + Arguments: + - `fingerprint`: member fingerprint + """ + + super(Hardship, self).__init__(conn_handler) + + self.fingerprint = fingerprint + + def __get__(self, **kwargs): + assert self.fingerprint is not None + return self.requests_get('/hardship/%s' % self.fingerprint.upper(), **kwargs) + + +class Newcomers(Blockchain): + """GET, return block numbers containing newcomers.""" + + def __get__(self, **kwargs): + return self.requests_get('/with/newcomers', **kwargs) + + +class Certifications(Blockchain): + """GET, return block numbers containing certifications.""" + + def __get__(self, **kwargs): + return self.requests_get('/with/certs', **kwargs) + + +class Joiners(Blockchain): + """GET, return block numbers containing joiners.""" + + def __get__(self, **kwargs): + return self.requests_get('/with/joiners', **kwargs) + + +class Actives(Blockchain): + """GET, return block numbers containing actives.""" + + def __get__(self, **kwargs): + return self.requests_get('/with/actives', **kwargs) + + +class Leavers(Blockchain): + """GET, return block numbers containing leavers.""" + + def __get__(self, **kwargs): + return self.requests_get('/with/leavers', **kwargs) + + +class Excluded(Blockchain): + """GET, return block numbers containing excluded.""" + + def __get__(self, **kwargs): + return self.requests_get('/with/excluded', **kwargs) + + +class UD(Blockchain): + """GET, return block numbers containing universal dividend.""" + null_value = \ + { + "result": + { + "blocks": [] + } + } + + def __get__(self, **kwargs): + return self.requests_get('/with/ud', **kwargs) + + +class TX(Blockchain): + """GET, return block numbers containing transactions.""" + null_value = \ + { + "result": + { + "blocks": [] + } + } + + def __get__(self, **kwargs): + return self.requests_get('/with/tx', **kwargs) diff --git a/src/cutecoin/core/net/api/bma/network/__init__.py b/src/cutecoin/core/net/api/bma/network/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..252219946af9fd6471dd6f20bf3f95659173d19c --- /dev/null +++ b/src/cutecoin/core/net/api/bma/network/__init__.py @@ -0,0 +1,35 @@ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Caner Candan <caner@candan.fr>, http://caner.candan.fr +# + +from .. import API, logging + +logger = logging.getLogger("ucoin/network") + + +class Network(API): + def __init__(self, conn_handler, module='network'): + super(Network, self).__init__(conn_handler, module) + + +class Peering(Network): + """GET peering information about a peer.""" + + def __get__(self, **kwargs): + return self.requests_get('/peering', **kwargs) + +from . import peering diff --git a/src/cutecoin/core/net/api/bma/network/peering/__init__.py b/src/cutecoin/core/net/api/bma/network/peering/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8da405735be29d99eaea8c590d6c328b3d95a954 --- /dev/null +++ b/src/cutecoin/core/net/api/bma/network/peering/__init__.py @@ -0,0 +1,51 @@ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Caner Candan <caner@candan.fr>, http://caner.candan.fr +# + +from .. import Network, logging + +logger = logging.getLogger("ucoin/network/peering") + + +class Base(Network): + def __init__(self, conn_handler): + super(Base, self).__init__(conn_handler, 'network/peering') + + +class Peers(Base): + """GET peering entries of every node inside the currency network.""" + + def __get__(self, **kwargs): + """creates a generator with one peering entry per iteration.""" + + return self.requests_get('/peers', **kwargs) + + def __post__(self, **kwargs): + assert 'entry' in kwargs + assert 'signature' in kwargs + + return self.requests_post('/peers', **kwargs) + + +class Status(Base): + """POST a network status document to this node in order notify of its status.""" + + def __post__(self, **kwargs): + assert 'status' in kwargs + assert 'signature' in kwargs + + return self.requests_post('/status', **kwargs) diff --git a/src/cutecoin/core/net/api/bma/node/__init__.py b/src/cutecoin/core/net/api/bma/node/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..975a14649861f5cf8e10069f7dba6e7be06cc6ad --- /dev/null +++ b/src/cutecoin/core/net/api/bma/node/__init__.py @@ -0,0 +1,36 @@ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Caner Candan <caner@candan.fr>, http://caner.candan.fr +# + +from .. import API, logging + +logger = logging.getLogger("ucoin/node") + +class Node(API): + def __init__(self, connection_handler, module='node'): + super(Node, self).__init__(connection_handler, module) + + +class Summary(Node): + """GET Certification data over a member.""" + + def __init__(self, connection_handler, module='node'): + super(Summary, self).__init__(connection_handler, module) + + def __get__(self, **kwargs): + return self.requests_get('/summary', **kwargs) + diff --git a/src/cutecoin/core/net/api/bma/tx/__init__.py b/src/cutecoin/core/net/api/bma/tx/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..22b63d450be93484ef6888a7872aea6e5e1ac8ad --- /dev/null +++ b/src/cutecoin/core/net/api/bma/tx/__init__.py @@ -0,0 +1,78 @@ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Caner Candan <caner@candan.fr>, http://caner.candan.fr +# + +from .. import API, logging + +logger = logging.getLogger("ucoin/tx") + + +class Tx(API): + def __init__(self, conn_handler, module='tx'): + super(Tx, self).__init__(conn_handler, module) + + +class Process(Tx): + """POST a transaction.""" + + def __post__(self, **kwargs): + assert 'transaction' in kwargs + + return self.requests_post('/process', **kwargs) + + +class History(Tx): + """Get transaction sources.""" + + null_value = { + "currency": "", + "pubkey": "", + "history": { + "sent": [], + "received": [] + } + } + + def __init__(self, conn_handler, pubkey, module='tx'): + super(Tx, self).__init__(conn_handler, module) + self.pubkey = pubkey + + def __get__(self, **kwargs): + assert self.pubkey is not None + return self.requests_get('/history/%s' % self.pubkey, **kwargs) + + +class Sources(Tx): + """Get transaction sources.""" + + null_value = { + "currency": "", + "pubkey": "", + "sources": + [ + ] + } + + def __init__(self, conn_handler, pubkey, module='tx'): + super(Tx, self).__init__(conn_handler, module) + self.pubkey = pubkey + + def __get__(self, **kwargs): + assert self.pubkey is not None + return self.requests_get('/sources/%s' % self.pubkey, **kwargs) + +from . import history \ No newline at end of file diff --git a/src/cutecoin/core/net/api/bma/tx/history/__init__.py b/src/cutecoin/core/net/api/bma/tx/history/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..471c4579c0eee3ebe9c5b7582e0fa84ad28601e9 --- /dev/null +++ b/src/cutecoin/core/net/api/bma/tx/history/__init__.py @@ -0,0 +1,41 @@ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Caner Candan <caner@candan.fr>, http://caner.candan.fr +# + +from .. import History, logging + +logger = logging.getLogger("ucoin/tx") + + +class Blocks(History): + def __init__(self, conn_handler, pubkey, from_, to_, module='tx'): + super(Blocks, self).__init__(conn_handler, pubkey, module) + self.from_ = from_ + self.to_ = to_ + + null_value = { + "hash": "", + "currency": "", + "pubkey": "", + "history": { + "sent": [], + "received": [] + } + } + + def __get__(self, **kwargs): + return self.requests_get('/history/%s/blocks/%s/%s' % (self.pubkey, self.from_, self.to_), **kwargs) diff --git a/src/cutecoin/core/net/api/bma/ud/__init__.py b/src/cutecoin/core/net/api/bma/ud/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..39ef282632a6288d0b103b702e2ef62dfe712235 --- /dev/null +++ b/src/cutecoin/core/net/api/bma/ud/__init__.py @@ -0,0 +1,47 @@ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Caner Candan <caner@candan.fr>, http://caner.candan.fr +# + +from .. import API, logging + +logger = logging.getLogger("ucoin/ud") + + +class Ud(API): + def __init__(self, conn_handler, module='ud'): + super(Ud, self).__init__(conn_handler, module) + + + +class History(Ud): + """Get UD history.""" + + null_value = { + "currency": "", + "pubkey": "", + "history": { + "history": [] + } + } + + def __init__(self, conn_handler, pubkey, module='ud'): + super(Ud, self).__init__(conn_handler, module) + self.pubkey = pubkey + + def __get__(self, **kwargs): + assert self.pubkey is not None + return self.requests_get('/history/%s' % self.pubkey, **kwargs) diff --git a/src/cutecoin/core/net/api/bma/wot/__init__.py b/src/cutecoin/core/net/api/bma/wot/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..745c5fef249b931eaaed1dc5f3679de8609ce34f --- /dev/null +++ b/src/cutecoin/core/net/api/bma/wot/__init__.py @@ -0,0 +1,113 @@ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Authors: +# Caner Candan <caner@candan.fr>, http://caner.candan.fr +# + +from .. import API, logging + +logger = logging.getLogger("ucoin/wot") + + +class WOT(API): + def __init__(self, conn_handler, module='wot'): + super(WOT, self).__init__(conn_handler, module) + + +class Add(WOT): + """POST Public key data.""" + + def __post__(self, **kwargs): + assert 'pubkey' in kwargs + assert 'self_' in kwargs + assert 'other' in kwargs + + return self.requests_post('/add', **kwargs) + + +class Lookup(WOT): + """GET Public key data.""" + null_value = \ + { + "partial": False, + "results": [] + } + + def __init__(self, conn_handler, search, module='wot'): + super(WOT, self).__init__(conn_handler, module) + + self.search = search + + def __get__(self, **kwargs): + assert self.search is not None + + return self.requests_get('/lookup/%s' % self.search, **kwargs) + + +class CertifiersOf(WOT): + """GET Certification data over a member.""" + null_value = \ + { + "pubkey": "", + "uid": "", + "isMember": False, + "certifications": [] + } + + def __init__(self, conn_handler, search, module='wot'): + super(WOT, self).__init__(conn_handler, module) + + self.search = search + + def __get__(self, **kwargs): + assert self.search is not None + + return self.requests_get('/certifiers-of/%s' % self.search, **kwargs) + + +class CertifiedBy(WOT): + """GET Certification data from a member.""" + null_value = \ + { + "pubkey": "", + "uid": "", + "isMember": False, + "certifications": [] + } + + def __init__(self, conn_handler, search, module='wot'): + super(WOT, self).__init__(conn_handler, module) + + self.search = search + + def __get__(self, **kwargs): + assert self.search is not None + + return self.requests_get('/certified-by/%s' % self.search, **kwargs) + + +class Members(WOT): + """GET List all current members of the Web of Trust.""" + + null_value = \ + { + "results": [] + } + + def __init__(self, conn_handler, module='wot'): + super(WOT, self).__init__(conn_handler, module) + + def __get__(self, **kwargs): + return self.requests_get('/members', **kwargs) diff --git a/src/cutecoin/core/net/endpoint.py b/src/cutecoin/core/net/endpoint.py new file mode 100644 index 0000000000000000000000000000000000000000..7ddbf792520195a42a29522a05cfa5ece496a1f9 --- /dev/null +++ b/src/cutecoin/core/net/endpoint.py @@ -0,0 +1,76 @@ +import re +import ucoinpy +from .api.bma import ConnectionHandler + +HANDLED_API=["BASIC_MERKLED_API"] + +class Endpoint(): + """ + Describing endpoints + """ + def __init__(self, pyendpoint): + self.pyendpoint = pyendpoint + + @staticmethod + def from_inline(inline): + for api in HANDLED_API: + if (inline.startswith(api)): + if (api == "BASIC_MERKLED_API"): + return BMAEndpoint.from_inline(inline) + return UnknownEndpoint.from_inline(inline) + + @property + def server(self): + return self.pyendpoint.server + + @property + def ipv4(self): + return self.pyendpoint.ipv4 + + @property + def ipv6(self): + return self.pyendpoint.ipv6 + + @property + def port(self): + return self.pyendpoint.port + + +class UnknownEndpoint(Endpoint): + + def __init__(self, pyendpoint): + super().__init__(pyendpoint) + + @classmethod + def from_inline(cls, inline): + endpoint = ucoinpy.documents.peer.UnknownEndpoint.from_inline(inline) + return cls(endpoint) + + def inline(self): + self.pyendpoint.inline() + + +class BMAEndpoint(Endpoint): + re_inline = re.compile('^BASIC_MERKLED_API(?: ([a-z0-9-_.]*(?:.[a-zA-Z])))?(?: ((?:[0-9.]{1,4}){4}))?(?: ((?:[0-9a-f:]{4,5}){4,8}))?(?: ([0-9]+))$') + + def __init__(self, pyendpoint): + super().__init__(pyendpoint) + + @classmethod + def from_inline(cls, inline): + endpoint = ucoinpy.documents.peer.BMAEndpoint.from_inline(inline) + return cls(endpoint) + + def inline(self): + return self.pyendpoint.inline() + + def conn_handler(self, network_manager): + if self.server: + return ConnectionHandler(network_manager, + self.pyendpoint.server, self.pyendpoint.port) + elif self.ipv4: + return ConnectionHandler(network_manager, self.pyendpoint.ipv4, + self.pyendpoint.port) + else: + return ConnectionHandler(network_manager, + self.pyendpoint.ipv6, self.pyendpoint.port) diff --git a/src/cutecoin/core/net/network.py b/src/cutecoin/core/net/network.py index 32602d00be232c4ac90ff9f491561d834aace8dc..26d1a4c9a1fabe61478e878afe8b2ac24c2dc402 100644 --- a/src/cutecoin/core/net/network.py +++ b/src/cutecoin/core/net/network.py @@ -1,65 +1,66 @@ -''' -Created on 24 feb. 2015 +""" +Created on 24 févr. 2015 @author: inso -''' -from .node import Node +""" +from cutecoin.core.net.node import Node import logging import time +import asyncio +from ucoinpy.documents.peer import Peer -from PyQt5.QtCore import pyqtSignal, pyqtSlot, QMutex, QCoreApplication -from ..watching.watcher import Watcher +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QTimer -class Network(Watcher): +class Network(QObject): """ A network is managing nodes polling and crawling of a given community. """ nodes_changed = pyqtSignal() new_block_mined = pyqtSignal(int) - stopped_perpetual_crawling = pyqtSignal() - def __init__(self, currency, nodes): - ''' + def __init__(self, network_manager, currency, nodes): + """ Constructor of a network :param str currency: The currency name of the community :param list nodes: The root nodes of the network - ''' + """ super().__init__() self._root_nodes = nodes self._nodes = [] - self._mutex = QMutex() + for n in nodes: + self.add_node(n) self.currency = currency - self.nodes = nodes self._must_crawl = False - self._is_perpetual = False + self.network_manager = network_manager self._block_found = self.latest_block + self._timer = QTimer() @classmethod - def create(cls, node): - ''' + def create(cls, network_manager, node): + """ 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 = [node] - network = cls(node.currency, nodes) + network = cls(network_manager, node.currency, nodes) return network def merge_with_json(self, json_data): - ''' + """ 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) + node = Node.from_json(self.network_manager, self.currency, data) if node.pubkey not in [n.pubkey for n in self.nodes]: self.add_node(node) logging.debug("Loading : {:}".format(data['pubkey'])) @@ -71,220 +72,159 @@ class Network(Watcher): other_node.state = node.state @classmethod - def from_json(cls, currency, json_data): - ''' + def from_json(cls, network_manager, 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) + node = Node.from_json(network_manager, currency, data) nodes.append(node) - network = cls(currency, nodes) + network = cls(network_manager, currency, nodes) return network 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 stop_crawling(self): - ''' + @property + def quality(self): + """ + Get a ratio of the synced nodes vs the rest + """ + synced = len(self.synced_nodes) + total = len(self.nodes) + ratio_synced = synced / total + return ratio_synced + + def stop_coroutines(self): + """ Stop network nodes crawling. - ''' + """ self._must_crawl = False def continue_crawling(self): - if self._is_perpetual: - return self._must_crawl - else: - return True + return self._must_crawl @property def synced_nodes(self): - ''' + """ Get nodes which are in the ONLINE state. - ''' - latest = self.latest_block - for n in self._nodes: - if n.state in (Node.ONLINE, Node.DESYNCED): - n.check_sync(latest) + """ return [n for n in self.nodes if n.state == Node.ONLINE] @property def online_nodes(self): - ''' + """ Get nodes which are in the ONLINE state. - ''' + """ return [n for n in self.nodes if n.state in (Node.ONLINE, Node.DESYNCED)] @property def nodes(self): - ''' + """ Get all knew nodes. - ''' + """ return self._nodes @property def root_nodes(self): - ''' + """ Get root nodes. - ''' + """ return self._root_nodes - @nodes.setter - def nodes(self, new_nodes): - ''' - Set new nodes - ''' - self._mutex.lock() - try: - for n in self.nodes: - try: - n.disconnect() - except TypeError: - logging.debug("Error disconnecting node {0}".format(n.pubkey[:5])) - - self._nodes = [] - for n in new_nodes: - self.add_node(n) - finally: - self._mutex.unlock() - @property def latest_block(self): - ''' + """ Get latest block known - ''' + """ return max([n.block for n in self.nodes]) def add_node(self, node): - ''' + """ Add a node to the network. - ''' + """ self._nodes.append(node) node.changed.connect(self.handle_change) - logging.debug("{:} connected".format(node.pubkey)) + node.neighbour_found.connect(self.handle_new_node) + logging.debug("{:} connected".format(node.pubkey[:5])) def add_root_node(self, node): - ''' + """ Add a node to the root nodes list - ''' + """ self._root_nodes.append(node) def remove_root_node(self, index): - ''' + """ Remove a node from the root nodes list - ''' + """ self._root_nodes.pop(index) def is_root_node(self, node): - ''' + """ Check if this node is in the root nodes - ''' + """ return node in self._root_nodes def root_node_index(self, index): - ''' + """ Get the index of a root node from its index in all nodes list - ''' + """ node = self.nodes[index] return self._root_nodes.index(node) - def moveToThread(self, thread): - for n in self.nodes: - n.moveToThread(thread) - super().moveToThread(thread) - - def watch(self): - self.stopped_perpetual_crawling.connect(self.watching_stopped) - self.start_perpetual_crawling() - - def stop(self): - self.stop_crawling() + def refresh_once(self): + for node in self._nodes: + node.refresh() - def start_perpetual_crawling(self): - ''' + @asyncio.coroutine + def discover_network(self): + """ Start crawling which never stops. To stop this crawling, call "stop_crawling" method. - ''' + """ self._must_crawl = True while self.continue_crawling(): - emit_change = False - nodes = self.crawling(interval=2) - - new_inlines = [n.endpoint.inline() for n in nodes] - last_inlines = [n.endpoint.inline() for n in self.nodes] - - hash_new_nodes = str(tuple(frozenset(sorted(new_inlines)))) - hash_last_nodes = str(tuple(frozenset(sorted(last_inlines)))) - if hash_new_nodes != hash_last_nodes: - logging.debug("Nodes changed...") - self.nodes = nodes - emit_change = True - for node in self.nodes: - if node.last_change + 3600 < time.time() and \ - node.state in (Node.OFFLINE, Node.CORRUPTED): - try: - node.changed.disconnect() - except TypeError: - logging.debug("Error : {0} not connected".format(node.pubkey)) - self.nodes.remove(node) - emit_change = True - - if emit_change: - self.nodes_changed.emit() - QCoreApplication.processEvents() - - self.stopped_perpetual_crawling.emit() + if self.continue_crawling(): + yield from asyncio.sleep(2) + node.refresh() + logging.debug("End of network discovery") + + @pyqtSlot(Peer, str) + def handle_new_node(self, peer, pubkey): + pubkeys = [n.pubkey for n in self.nodes] + if peer.pubkey not in pubkeys: + logging.debug("New node found : {0}".format(peer.pubkey[:5])) + node = Node.from_peer(self.network_manager, self.currency, peer, pubkey) + self.add_node(node) + self.nodes_changed.emit() @pyqtSlot() def handle_change(self): node = self.sender() - logging.debug("Handle change") if node.state in (Node.ONLINE, Node.DESYNCED): node.check_sync(self.latest_block) + else: + if node.last_change + 3600 < time.time(): + self.nodes.remove(node) + self.nodes_changed.emit() + logging.debug("{0} -> {1}".format(self.latest_block, self.latest_block)) if self._block_found < self.latest_block: - self._block_found = self.latest_block logging.debug("New block found : {0}".format(self.latest_block)) + self._block_found = self.latest_block self.new_block_mined.emit(self.latest_block) - - QCoreApplication.processEvents() - logging.debug("Syncing : {0} : last changed {1} : unsynced : {2}".format(node.pubkey[:5], - node.last_change, time.time() - node.last_change)) - - self.nodes_changed.emit() - - def crawling(self, interval=0): - ''' - One network crawling. - - :param int interval: The interval between two nodes request. - ''' - nodes = [] - traversed_pubkeys = [] - knew_pubkeys = [n.pubkey for n in self.nodes] - for n in self.nodes: - logging.debug(traversed_pubkeys) - logging.debug("Peering : next to read : {0} : {1}".format(n.pubkey, - (n.pubkey not in traversed_pubkeys))) - if self.continue_crawling(): - n.peering_traversal(knew_pubkeys, nodes, - traversed_pubkeys, interval, - self.continue_crawling) - QCoreApplication.processEvents() - time.sleep(interval) - - logging.debug("Nodes found : {0}".format(nodes)) - return nodes diff --git a/src/cutecoin/core/net/node.py b/src/cutecoin/core/net/node.py index c71bcce138130ea7b916c1a8ffa7a5d958db5f60..80ff1a3aad56f693030a9cdbc822e084aa875f57 100644 --- a/src/cutecoin/core/net/node.py +++ b/src/cutecoin/core/net/node.py @@ -1,32 +1,32 @@ -''' +""" Created on 21 févr. 2015 @author: inso -''' - -from ucoinpy.documents.peer import Peer, BMAEndpoint, Endpoint -from ucoinpy.api import bma -from ucoinpy.api.bma import ConnectionHandler -from requests.exceptions import RequestException, ConnectionError -from ...tools.exceptions import InvalidNodeCurrency, PersonNotFoundError -from ..person import Person +""" + +from ucoinpy.documents.peer import Peer +from ...tools.exceptions import InvalidNodeCurrency +from ..net.api import bma as qtbma +from ..net.endpoint import Endpoint, BMAEndpoint +from ..net.api.bma import ConnectionHandler + +import asyncio import logging import time -import ctypes -import sys - -from PyQt5.QtCore import QObject, pyqtSignal +import json +from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot +from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest class Node(QObject): - ''' + """ 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 OFFLINE = 2 @@ -34,76 +34,99 @@ class Node(QObject): CORRUPTED = 4 changed = pyqtSignal() + neighbour_found = pyqtSignal(Peer, str) - def __init__(self, currency, endpoints, uid, pubkey, block, - state, last_change, software, version): - ''' + def __init__(self, network_manager, currency, endpoints, uid, pubkey, block, + state, last_change, last_merkle, software, version): + """ Constructor - ''' + """ super().__init__() + self.network_manager = network_manager self._endpoints = endpoints self._uid = uid self._pubkey = pubkey - self._software = software - self._version = version self.block = block self._state = state self._neighbours = [] self._currency = currency self._last_change = last_change + self._last_merkle = last_merkle + self._software = software + self._version = version @classmethod - def from_address(cls, currency, address, port): - ''' + @asyncio.coroutine + def from_address(cls, network_manager, 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'], - peer_data['signature'])) - - if currency is not None: - if peer.currency != currency: - raise InvalidNodeCurrency(peer.currency, currency) - - node = cls(peer.currency, peer.endpoints, "", peer.pubkey, 0, - Node.ONLINE, time.time(), "", "") - logging.debug("Node from address : {:}".format(str(node))) - return node + """ + def handle_reply(reply): + if reply.error() == QNetworkReply.NoError: + strdata = bytes(reply.readAll()).decode('utf-8') + nonlocal peer_data + peer_data = json.loads(strdata) + future_reply.set_result(True) + else: + future_reply.set_result(False) + + future_reply = asyncio.Future() + peer_data = {} + reply = qtbma.network.Peering(ConnectionHandler(network_manager, address, port)).get() + reply.finished.connect(lambda: handle_reply(reply)) + + yield from future_reply + if future_reply.result(): + peer = Peer.from_signed_raw("{0}{1}\n".format(peer_data['raw'], + peer_data['signature'])) + + if currency is not None: + if peer.currency != currency: + raise InvalidNodeCurrency(peer.currency, currency) + + node = cls(network_manager, peer.currency, peer.endpoints, + "", peer.pubkey, 0, Node.ONLINE, time.time(), + {'root': "", 'leaves': []}, "", "") + logging.debug("Node from address : {:}".format(str(node))) + return node + else: + return None @classmethod - def from_peer(cls, currency, peer): - ''' + def from_peer(cls, network_manager, currency, peer, pubkey): + """ 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) - node = cls(peer.currency, peer.endpoints, "", "", 0, - Node.ONLINE, time.time(), "", "") + node = cls(network_manager, peer.currency, peer.endpoints, "", pubkey, 0, + Node.ONLINE, time.time(), + {'root': "", 'leaves': []}, + "", "") logging.debug("Node from peer : {:}".format(str(node))) return node @classmethod - def from_json(cls, currency, data): + def from_json(cls, network_manager, currency, data): endpoints = [] uid = "" pubkey = "" + software = "" + version = "" block = 0 last_change = time.time() state = Node.ONLINE - software = '' - version = '' logging.debug(data) for endpoint_data in data['endpoints']: endpoints.append(Endpoint.from_inline(endpoint_data)) @@ -125,21 +148,20 @@ class Node(QObject): if 'state' in data: state = data['state'] - else: - logging.debug("Error : no state in node") if 'software' in data: software = data['software'] - else: - logging.debug("Error : no software in node") if 'version' in data: version = data['version'] else: - logging.debug("Error : no version in node") + logging.debug("Error : no state in node") - node = cls(currency, endpoints, uid, pubkey, block, - state, last_change, software, version) + node = cls(network_manager, currency, endpoints, + uid, pubkey, block, + state, last_change, + {'root': "", 'leaves': []}, + software, version) logging.debug("Node from json : {:}".format(str(node))) return node @@ -161,9 +183,7 @@ class Node(QObject): 'currency': self._currency, 'state': self._state, 'last_change': self._last_change, - 'block': self.block, - 'software': self.software, - 'version': self.version} + 'block': self.block} endpoints = [] for e in self._endpoints: endpoints.append(e.inline()) @@ -182,14 +202,6 @@ class Node(QObject): def block(self): return self._block - @property - def version(self): - return self._version - - @property - def software(self): - return self._software - @block.setter def block(self, new_block): self._block = new_block @@ -214,182 +226,223 @@ class Node(QObject): def last_change(self): return self._last_change + @property + def software(self): + return self._software + + @software.setter + def software(self, new_soft): + if self._software != new_soft: + self._software = new_soft + self.changed.emit() + + @property + def version(self): + return self._version + + @version.setter + def version(self, new_version): + if self._version != new_version: + self._version = new_version + self.changed.emit() + @last_change.setter def last_change(self, val): - #logging.debug("{:} | Changed state : {:}".format(self.pubkey[:5],val)) + logging.debug("{:} | Changed state : {:}".format(self.pubkey[:5], + val)) self._last_change = val @state.setter def state(self, new_state): - #logging.debug("{:} | Last state : {:} / new state : {:}".format(self.pubkey[:5],self.state, new_state)) + logging.debug("{:} | Last state : {:} / new state : {:}".format(self.pubkey[:5], + self.state, new_state)) if self._state != new_state: self.last_change = time.time() self._state = new_state def check_sync(self, block): - #logging.debug("Check sync") + logging.debug("Check sync") if self.block < block: self.state = Node.DESYNCED else: self.state = Node.ONLINE - def _request_uid(self): - uid = "" - try: - data = bma.wot.Lookup(self.endpoint.conn_handler(), self.pubkey).get() - timestamp = 0 - for result in data['results']: - if result["pubkey"] == self.pubkey: - uids = result['uids'] - for uid in uids: - if uid["meta"]["timestamp"] > timestamp: - timestamp = uid["meta"]["timestamp"] - uid = uid["uid"] - except ValueError as e: - if '404' in str(e): - logging.debug("Error : node uid not found : {0}".format(self.pubkey)) - uid = "" - return uid - - def refresh_state(self): - logging.debug("Refresh state") - emit_change = False - try: - informations = bma.network.Peering(self.endpoint.conn_handler()).get() - node_pubkey = informations["pubkey"] - try: - block = bma.blockchain.Current(self.endpoint.conn_handler()).get() - block_number = block["number"] - except ValueError as e: - if '404' in str(e): - block_number = 0 - - peers_data = bma.network.peering.Peers(self.endpoint.conn_handler()).get() - neighbours = [] - for p in peers_data: - peer = Peer.from_signed_raw("{0}{1}\n".format(p['value']['raw'], - p['value']['signature'])) - neighbours.append(peer.endpoints) - logging.debug("Found neighbours : {0}".format(len(neighbours))) - - node_currency = informations["currency"] - node_uid = self._request_uid() - - implementation = bma.node.Summary(self.endpoint.conn_handler()).get() - software = implementation["ucoin"]["software"] - version = implementation["ucoin"]["version"] - - #If the nodes goes back online... - if self.state in (Node.OFFLINE, Node.CORRUPTED): + def check_noerror(self, error_code, status_code): + if error_code != QNetworkReply.NoError: + if self.state == Node.OFFLINE: self.state = Node.ONLINE - logging.debug("Change : new state online") - emit_change = True - except ConnectionError as e: - logging.debug(str(e)) - - if self.state != Node.OFFLINE: - self.state = Node.OFFLINE - logging.debug("Change : new state offine") - emit_change = True - # Dirty hack to reload resolv.conf on linux - if 'Connection aborted' in str(e) and 'gaierror' in str(e): - logging.debug("Connection Aborted") - if 'linux' in sys.platform: - try: - libc = ctypes.CDLL('libc.so.6') - res_init = getattr(libc, '__res_init') - res_init(None) - except: - logging.error('Error calling libc.__res_init') - except RequestException as e: - logging.debug(str(e)) - if self.state != Node.OFFLINE: - self.state = Node.OFFLINE - logging.debug("Change : new state offine") - emit_change = True - - # If not is offline, do not refresh last data - if self.state != Node.OFFLINE: - # If not changed its currency, consider it corrupted - if node_currency != self._currency: + return False + if status_code == 503: + return False + return True + + @pyqtSlot() + def refresh(self): + logging.debug("Refresh block") + self.refresh_block() + logging.debug("Refresh info") + self.refresh_informations() + logging.debug("Refresh uid") + self.refresh_uid() + logging.debug("Refresh peers") + self.refresh_peers() + logging.debug("Refresh summary") + self.refresh_summary() + + def refresh_block(self): + conn_handler = self.endpoint.conn_handler(self.network_manager) + + logging.debug("Requesting {0}".format(conn_handler)) + reply = qtbma.blockchain.Current(conn_handler).get() + + reply.finished.connect(self.handle_block_reply) + + @pyqtSlot() + def handle_block_reply(self): + reply = self.sender() + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + + if self.check_noerror(reply.error(), status_code): + if status_code == 200: + strdata = bytes(reply.readAll()).decode('utf-8') + block_data = json.loads(strdata) + block_number = block_data['number'] + elif status_code == 404: + block_number = 0 + + if block_number != self.block: + self.block = block_number + logging.debug("Changed block {0} -> {1}".format(self.block, + block_number)) + self.changed.emit() + + else: + logging.debug("Error in block reply") + + def refresh_informations(self): + conn_handler = self.endpoint.conn_handler(self.network_manager) + + peering_reply = qtbma.network.Peering(conn_handler).get() + peering_reply.finished.connect(self.handle_peering_reply) + + @pyqtSlot() + def handle_peering_reply(self): + reply = self.sender() + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + + if self.check_noerror(reply.error(), status_code): + strdata = bytes(reply.readAll()).decode('utf-8') + peering_data = json.loads(strdata) + logging.debug(peering_data) + node_pubkey = peering_data["pubkey"] + node_currency = peering_data["currency"] + + change = False + if node_pubkey != self.pubkey: + self._pubkey = node_pubkey + change = True + + if node_currency != self.currency: self.state = Node.CORRUPTED logging.debug("Change : new state corrupted") - emit_change = True - else: - if block_number != self.block: - logging.debug("Change : new block {0} -> {1}".format(self.block, - block_number)) - self.block = block_number - logging.debug("Changed block {0} -> {1}".format(self.block, - block_number)) - emit_change = True - - if node_pubkey != self._pubkey: - logging.debug("Change : new pubkey {0} -> {1}".format(self._pubkey, - node_pubkey)) - self._pubkey = node_pubkey - emit_change = True - - if node_uid != self._uid: - logging.debug("Change : new uid") - self._uid = node_uid - emit_change = True - - if software != self._software: - logging.debug("Change : new software") - self._software = software - emit_change = True - - if version != self._version: - logging.debug("Change : new version") - self._version = version - emit_change = True - - logging.debug(neighbours) - new_inlines = [e.inline() for n in neighbours for e in n] - last_inlines = [e.inline() for n in self._neighbours for e in n] - - hash_new_neighbours = hash(tuple(frozenset(sorted(new_inlines)))) - hash_last_neighbours = hash(tuple(frozenset(sorted(last_inlines)))) - if hash_new_neighbours != hash_last_neighbours: - self._neighbours = neighbours - logging.debug("Change : new neighbours {0} -> {1}".format(last_inlines, - new_inlines)) - emit_change = True - - if emit_change: - self.changed.emit() + change = True - def peering_traversal(self, knew_pubkeys, found_nodes, - traversed_pubkeys, interval, - continue_crawling): - logging.debug("Read {0} peering".format(self.pubkey)) - traversed_pubkeys.append(self.pubkey) - self.refresh_state() - - if self.pubkey not in [n.pubkey for n in found_nodes]: - # if node is corrupted remove it - if self.state != Node.CORRUPTED: - logging.debug("Found : {0} node".format(self.pubkey)) - found_nodes.append(self) - logging.debug(self.neighbours) - for n in self.neighbours: - try: - e = next(e for e in n if type(e) is BMAEndpoint) - peering = bma.network.Peering(e.conn_handler()).get() - except: - continue - peer = Peer.from_signed_raw("{0}{1}\n".format(peering['raw'], - peering['signature'])) - if peer.pubkey not in traversed_pubkeys and \ - peer.pubkey not in knew_pubkeys and continue_crawling(): - node = Node.from_peer(self._currency, peer) - logging.debug(traversed_pubkeys) - logging.debug("Traversing : next to read : {0} : {1}".format(node.pubkey, - (node.pubkey not in traversed_pubkeys))) - node.peering_traversal(knew_pubkeys, found_nodes, - traversed_pubkeys, interval, continue_crawling) - time.sleep(interval) + if change: + self.changed.emit() + else: + logging.debug("Error in peering reply") + + def refresh_summary(self): + conn_handler = self.endpoint.conn_handler(self.network_manager) + + summary_reply = qtbma.node.Summary(conn_handler).get() + summary_reply.finished.connect(self.handle_summary_reply) + + @pyqtSlot() + def handle_summary_reply(self): + reply = self.sender() + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + + if self.check_noerror(reply.error(), status_code): + strdata = bytes(reply.readAll()).decode('utf-8') + summary_data = json.loads(strdata) + self.software = summary_data["ucoin"]["software"] + self.version = summary_data["ucoin"]["version"] + + def refresh_uid(self): + conn_handler = self.endpoint.conn_handler(self.network_manager) + uid_reply = qtbma.wot.Lookup(conn_handler, self.pubkey).get() + uid_reply.finished.connect(self.handle_uid_reply) + uid_reply.error.connect(lambda code: logging.debug("Error : {0}".format(code))) + + @pyqtSlot() + def handle_uid_reply(self): + reply = self.sender() + status_code = reply.attribute( QNetworkRequest.HttpStatusCodeAttribute ); + + if self.check_noerror(reply.error(), status_code): + uid = '' + if status_code == 200: + strdata = bytes(reply.readAll()).decode('utf-8') + data = json.loads(strdata) + timestamp = 0 + for result in data['results']: + if result["pubkey"] == self.pubkey: + uids = result['uids'] + for uid in uids: + if uid["meta"]["timestamp"] > timestamp: + timestamp = uid["meta"]["timestamp"] + uid = uid["uid"] + elif status_code == 404: + logging.debug("UID not found") + + if self._uid != uid: + self._uid = uid + self.changed.emit() + else: + logging.debug("error in uid reply") + + def refresh_peers(self): + conn_handler = self.endpoint.conn_handler(self.network_manager) + + reply = qtbma.network.peering.Peers(conn_handler).get(leaves='true') + reply.finished.connect(self.handle_peers_reply) + reply.error.connect(lambda code: logging.debug("Error : {0}".format(code))) + + @pyqtSlot() + def handle_peers_reply(self): + reply = self.sender() + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + + if self.check_noerror(reply.error(), status_code): + strdata = bytes(reply.readAll()).decode('utf-8') + peers_data = json.loads(strdata) + if peers_data['root'] != self._last_merkle['root']: + leaves = [leaf for leaf in peers_data['leaves'] + if leaf not in self._last_merkle['leaves']] + for leaf_hash in leaves: + conn_handler = self.endpoint.conn_handler(self.network_manager) + leaf_reply = qtbma.network.peering.Peers(conn_handler).get(leaf=leaf_hash) + leaf_reply.finished.connect(self.handle_leaf_reply) + self._last_merkle = {'root' : peers_data['root'], + 'leaves': peers_data['leaves']} + else: + logging.debug("Error in peers reply") + + @pyqtSlot() + def handle_leaf_reply(self): + reply = self.sender() + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + + if self.check_noerror(reply.error(), status_code): + strdata = bytes(reply.readAll()).decode('utf-8') + leaf_data = json.loads(strdata) + doc = Peer.from_signed_raw("{0}{1}\n".format(leaf_data['leaf']['value']['raw'], + leaf_data['leaf']['value']['signature'])) + pubkey = leaf_data['leaf']['value']['pubkey'] + self.neighbour_found.emit(doc, pubkey) + else: + logging.debug("Error in leaf reply") def __str__(self): return ','.join([str(self.pubkey), str(self.endpoint.server), str(self.endpoint.port), str(self.block), diff --git a/src/cutecoin/core/person.py b/src/cutecoin/core/person.py deleted file mode 100644 index 276ef0e0fa752a4124bcb56051d9d26e0aa12c29..0000000000000000000000000000000000000000 --- a/src/cutecoin/core/person.py +++ /dev/null @@ -1,476 +0,0 @@ -''' -Created on 11 févr. 2014 - -@author: inso -''' - -import logging -import functools -import time - -from ucoinpy.api import bma -from ucoinpy import PROTOCOL_VERSION -from ucoinpy.documents.certification import SelfCertification -from ucoinpy.documents.membership import Membership -from ..tools.exceptions import Error, PersonNotFoundError,\ - MembershipNotFoundError, \ - NoPeerAvailable -from PyQt5.QtCore import QMutex - - -def load_cache(json_data): - for person_data in json_data['persons']: - person = Person.from_json(person_data) - Person._instances[person.pubkey] = person - - -def jsonify_cache(): - data = [] - for person in Person._instances.values(): - data.append(person.jsonify()) - return {'persons': data} - - -class cached(object): - ''' - Decorator. Caches a function's return value each time it is called. - If called later with the same arguments, the cached value is returned - (not reevaluated). - Delete it to clear it from the cache - ''' - def __init__(self, func): - self.func = func - - def __call__(self, inst, community): - inst._cache_mutex.lock() - try: - inst._cache[community.currency] - except KeyError: - inst._cache[community.currency] = {} - - try: - value = inst._cache[community.currency][self.func.__name__] - except KeyError: - value = self.func(inst, community) - inst._cache[community.currency][self.func.__name__] = value - - finally: - inst._cache_mutex.unlock() - - return value - - def __repr__(self): - '''Return the function's docstring.''' - return self.func.__repr__ - - def __get__(self, inst, objtype): - if inst is None: - return self.func - return functools.partial(self, inst) - - -#TODO: Change Person to Identity ? -class Person(object): - ''' - A person with a uid and a pubkey - ''' - _instances = {} - - def __init__(self, uid, pubkey, cache): - ''' - Initializing a person object. - - :param str uid: The person uid, also known as its uid on the network - :param str pubkey: The person pubkey - :param cache: The last returned values of the person properties. - ''' - super().__init__() - self.uid = uid - self.pubkey = pubkey - self._cache = cache - self._cache_mutex = QMutex() - - @classmethod - def lookup(cls, pubkey, community, cached=True): - ''' - 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 cached and pubkey in Person._instances: - return Person._instances[pubkey] - else: - try: - data = community.request(bma.wot.Lookup, req_args={'search': pubkey}, - cached=cached) - except ValueError as e: - if '404' in str(e): - raise PersonNotFoundError(pubkey, community.name) - - timestamp = 0 - - for result in data['results']: - if result["pubkey"] == pubkey: - uids = result['uids'] - person_uid = "" - for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: - timestamp = uid_data["meta"]["timestamp"] - person_uid = uid_data["uid"] - - person = cls(person_uid, pubkey, {}) - Person._instances[pubkey] = person - logging.debug("{0}".format(Person._instances.keys())) - return person - 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 uid, - 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. - ''' - uid = metadata['text'] - pubkey = metadata['id'] - if pubkey in Person._instances: - return Person._instances[pubkey] - else: - person = cls(uid, pubkey, {}) - Person._instances[pubkey] = person - return person - - @classmethod - 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_data['pubkey'] - if pubkey in Person._instances: - return Person._instances[pubkey] - else: - if 'name' in json_data: - uid = json_data['name'] - else: - uid = json_data['uid'] - if 'cache' in json_data: - cache = json_data['cache'] - else: - cache = {} - - person = cls(uid, pubkey, cache) - Person._instances[pubkey] = person - 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 - - for result in data['results']: - if result["pubkey"] == self.pubkey: - uids = result['uids'] - for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: - timestamp = uid_data["meta"]["timestamp"] - uid = uid_data["uid"] - signature = uid_data["self"] - - return SelfCertification(PROTOCOL_VERSION, - community.currency, - self.pubkey, - timestamp, - uid, - signature) - raise PersonNotFoundError(self.pubkey, community.name) - - @cached - 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 - if len(search['memberships']) > 0: - membership_data = search['memberships'][0] - return community.get_block(membership_data['blockNumber']).mediantime - else: - return None - except ValueError as e: - if '400' in str(e): - raise MembershipNotFoundError(self.pubkey, community.name) - except Exception as e: - logging.debug('bma.blockchain.Membership request error : ' + str(e)) - raise MembershipNotFoundError(self.pubkey, community.name) - -#TODO: Manage 'OUT' memberships ? Maybe ? - @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}) - block_number = -1 - for ms in search['memberships']: - if ms['blockNumber'] > block_number: - block_number = ms['blockNumber'] - 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) - except ValueError as e: - if '400' in str(e): - raise MembershipNotFoundError(self.pubkey, community.name) - except Exception as e: - logging.debug('bma.blockchain.Membership request error : ' + str(e)) - raise MembershipNotFoundError(self.pubkey, community.name) - - return membership_data - - @cached - def published_uid(self, community): - try: - data = community.request(bma.wot.Lookup, - req_args={'search': self.pubkey}, - cached=cached) - except ValueError as e: - if '404' in str(e): - return False - - timestamp = 0 - - for result in data['results']: - if result["pubkey"] == self.pubkey: - uids = result['uids'] - person_uid = "" - for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: - timestamp = uid_data["meta"]["timestamp"] - person_uid = uid_data["uid"] - if person_uid == self.uid: - return True - return False - - @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'] - except ValueError: - return False - except Exception as e: - logging.debug('bma.wot.CertifiersOf request error : ' + str(e)) - return False - - @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: - logging.debug('bma.wot.CertifiersOf request ValueError : ' + str(e)) - try: - data = community.request(bma.wot.Lookup, {'search': self.pubkey}) - except ValueError as e: - logging.debug('bma.wot.Lookup request ValueError : ' + str(e)) - return list() - - # convert api data to certifiers list - certifiers = list() - # add certifiers of uid - - for result in data['results']: - if result["pubkey"] == self.pubkey: - for uid_data in result['uids']: - for certifier_data in uid_data['others']: - for uid in certifier_data['uids']: - # add a certifier - certifier = {} - certifier['uid'] = uid - certifier['pubkey'] = certifier_data['pubkey'] - certifier['isMember'] = certifier_data['isMember'] - certifier['cert_time'] = dict() - certifier['cert_time']['medianTime'] = community.get_block(certifier_data['meta']['block_number']).mediantime - certifiers.append(certifier) - - return certifiers - - except Exception as e: - logging.debug('bma.wot.CertifiersOf request error : ' + str(e)) - return list() - - return certifiers['certifications'] - - def unique_valid_certifiers_of(self, community): - certifier_list = self.certifiers_of(community) - unique_valid = [] - #  add certifiers of uid - for certifier in tuple(certifier_list): - # add only valid certification... - if community.certification_expired(certifier['cert_time']['medianTime']): - continue - - # keep only the latest certification - already_found = [c['pubkey'] for c in unique_valid] - if certifier['pubkey'] in already_found: - index = already_found.index(certifier['pubkey']) - if certifier['cert_time']['medianTime'] > unique_valid[index]['cert_time']['medianTime']: - unique_valid[index] = certifier - else: - unique_valid.append(certifier) - return unique_valid - - @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: - logging.debug('bma.wot.CertifiersOf request ValueError : ' + str(e)) - try: - data = community.request(bma.wot.Lookup, {'search': self.pubkey}) - except ValueError as e: - logging.debug('bma.wot.Lookup request ValueError : ' + str(e)) - return list() - - certified_list = list() - for result in data['results']: - if result["pubkey"] == self.pubkey: - for certified in result['signed']: - certified['cert_time'] = dict() - certified['cert_time']['medianTime'] = certified['meta']['timestamp'] - certified_list.append(certified) - - return certified_list - - except Exception as e: - logging.debug('bma.wot.CertifiersOf request error : ' + str(e)) - return list() - - return certified_list['certifications'] - - def unique_valid_certified_by(self, community): - certified_list = self.certified_by(community) - unique_valid = [] - #  add certifiers of uid - for certified in tuple(certified_list): - # add only valid certification... - if community.certification_expired(certified['cert_time']['medianTime']): - continue - - # keep only the latest certification - already_found = [c['pubkey'] for c in unique_valid] - if certified['pubkey'] in already_found: - index = already_found.index(certified['pubkey']) - if certified['cert_time']['medianTime'] > unique_valid[index]['cert_time']['medianTime']: - unique_valid[index] = certified - else: - unique_valid.append(certified) - return unique_valid - - def membership_expiration_time(self, community): - join_block = self.membership(community)['blockNumber'] - join_date = community.get_block(join_block).mediantime - parameters = community.parameters - expiration_date = join_date + parameters['sigValidity'] - current_time = time.time() - return expiration_date - current_time - - 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() - change = False - try: - if community.currency not in self._cache: - self._cache[community.currency] = {} - - try: - before = self._cache[community.currency][func.__name__] - except KeyError: - change = True - - try: - value = func(self, community) - - if not change: - if type(value) is dict: - hash_before = (str(tuple(frozenset(sorted(before.keys())))), - str(tuple(frozenset(sorted(before.items()))))) - hash_after = (str(tuple(frozenset(sorted(value.keys())))), - str(tuple(frozenset(sorted(value.items()))))) - change = hash_before != hash_after - elif type(value) is bool: - change = before != value - self._cache[community.currency][func.__name__] = value - except Error: - return False - finally: - self._cache_mutex.unlock() - return change - - def jsonify(self): - ''' - Get the community as dict in json format. - :return: The community as a dict in json format - ''' - data = {'uid': self.uid, - 'pubkey': self.pubkey, - 'cache': self._cache} - return data diff --git a/src/cutecoin/core/registry/__init__.py b/src/cutecoin/core/registry/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4ab046a19ff274331c3bc1ffd25e16bd19985478 --- /dev/null +++ b/src/cutecoin/core/registry/__init__.py @@ -0,0 +1,2 @@ +from .identities import IdentitiesRegistry +from .identity import Identity \ No newline at end of file diff --git a/src/cutecoin/core/registry/identities.py b/src/cutecoin/core/registry/identities.py new file mode 100644 index 0000000000000000000000000000000000000000..17d6d46b6f74db1306de42d046d6fce4e1975925 --- /dev/null +++ b/src/cutecoin/core/registry/identities.py @@ -0,0 +1,141 @@ +from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal + +from cutecoin.core.net.api import bma as qtbma +from .identity import Identity + +import json +import asyncio +import logging + + +class IdentitiesRegistry: + """ + Core class to handle identities lookup + """ + def __init__(self, instances={}): + """ + Initializer of the IdentitiesRegistry + + :param list of Identity instances: + :return: An IdentitiesRegistry object + :rtype: IdentitiesRegistry + """ + self._instances = instances + + def load_json(self, json_data): + """ + Load json data + + :param dict json_data: The identities in json format + """ + instances = {} + + for person_data in json_data['registry']: + pubkey = person_data['pubkey'] + if pubkey not in instances: + person = Identity.from_json(person_data) + instances[person.pubkey] = person + self._instances = instances + + def jsonify(self): + identities_json = [] + for identity in self._instances.values(): + identities_json.append(identity.jsonify()) + return {'registry': identities_json} + + def lookup(self, pubkey, community): + """ + Get a person from the pubkey found in a community + + :param str pubkey: The person pubkey + :param cutecoin.core.community.Community 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. + :rtype: cutecoin.core.registry.Identity + """ + if pubkey in self._instances: + identity = self._instances[pubkey] + self._instances[pubkey] = identity + else: + identity = Identity.empty(pubkey) + self._instances[pubkey] = identity + reply = community.bma_access.simple_request(qtbma.wot.Lookup, req_args={'search': pubkey}) + reply.finished.connect(lambda: self.handle_lookup(reply, identity)) + return identity + + @asyncio.coroutine + def future_lookup(self, pubkey, community): + def handle_reply(reply): + strdata = bytes(reply.readAll()).decode('utf-8') + data = json.loads(strdata) + + timestamp = 0 + for result in data['results']: + if result["pubkey"] == identity.pubkey: + uids = result['uids'] + identity_uid = "" + for uid_data in uids: + if uid_data["meta"]["timestamp"] > timestamp: + timestamp = uid_data["meta"]["timestamp"] + identity_uid = uid_data["uid"] + identity.uid = identity_uid + identity.status = Identity.FOUND + logging.debug("Lookup : found {0}".format(identity)) + future_identity.set_result(True) + return + future_identity.set_result(True) + + future_identity = asyncio.Future() + if pubkey in self._instances: + identity = self._instances[pubkey] + future_identity.set_result(True) + else: + identity = Identity.empty(pubkey) + self._instances[pubkey] = identity + reply = community.bma_access.simple_request(qtbma.wot.Lookup, req_args={'search': pubkey}) + reply.finished.connect(lambda: handle_reply(reply)) + yield from future_identity + return identity + + def handle_lookup(self, reply, identity): + """ + :param cutecoin.core.registry.identity.Identity identity: The looked up identity + :return: + """ + strdata = bytes(reply.readAll()).decode('utf-8') + data = json.loads(strdata) + + timestamp = 0 + for result in data['results']: + if result["pubkey"] == identity.pubkey: + uids = result['uids'] + identity_uid = "" + for uid_data in uids: + if uid_data["meta"]["timestamp"] > timestamp: + timestamp = uid_data["meta"]["timestamp"] + identity_uid = uid_data["uid"] + identity.uid = identity_uid + identity.status = Identity.FOUND + logging.debug("Lookup : found {0}".format(identity)) + identity.inner_data_changed.emit(str(qtbma.wot.Lookup)) + return + + def from_metadata(self, metadata): + """ + Get a person from a metadata dict. + A metadata dict has a 'text' key corresponding to the person uid, + 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. + """ + pubkey = metadata['id'] + if pubkey in self._instances: + return self._instances[pubkey] + else: + identity = Identity.from_metadata(metadata) + self._instances[pubkey] = identity + return identity diff --git a/src/cutecoin/core/registry/identity.py b/src/cutecoin/core/registry/identity.py new file mode 100644 index 0000000000000000000000000000000000000000..23d35a1c509ec6522d204ebf15fe5b7a1e53c3db --- /dev/null +++ b/src/cutecoin/core/registry/identity.py @@ -0,0 +1,311 @@ +""" +Created on 11 févr. 2014 + +@author: inso +""" + +import logging +import time +import asyncio + +from ucoinpy.documents.certification import SelfCertification +from cutecoin.tools.exceptions import Error, NoPeerAvailable,\ + MembershipNotFoundError +from cutecoin.core.net.api import bma as qtbma +from cutecoin.core.net.api.bma import PROTOCOL_VERSION +from PyQt5.QtCore import QObject, pyqtSignal + + +class Identity(QObject): + """ + A person with a uid and a pubkey + """ + FOUND = 1 + NOT_FOUND = 0 + + inner_data_changed = pyqtSignal(str) + + def __init__(self, uid, pubkey, status): + """ + Initializing a person object. + + :param str uid: The person uid, also known as its uid on the network + :param str pubkey: The person pubkey + :param int status: The local status of the identity + """ + super().__init__() + assert(status in (Identity.FOUND, Identity.NOT_FOUND)) + self.uid = uid + self.pubkey = pubkey + self.status = status + + @classmethod + def empty(cls, pubkey): + return cls("", pubkey, Identity.NOT_FOUND) + + @classmethod + def from_metadata(cls, metadata): + return cls(metadata["text"], metadata["id"], Identity.NOT_FOUND) + + @classmethod + 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_data['pubkey'] + uid = json_data['uid'] + status = json_data['status'] + + return cls(uid, pubkey, status) + + @asyncio.coroutine + def selfcert(self, community): + """ + Get the identity self certification. + This request is not cached in the person object. + + :param cutecoin.core.community.Community community: The community target to request the self certification + :return: A SelfCertification ucoinpy object + :rtype: ucoinpy.documents.certification.SelfCertification + """ + timestamp = 0 + lookup_data = yield from community.bma_access.future_request(qtbma.wot.Lookup, req_args={'search': self.pubkey}) + for result in lookup_data['results']: + if result["pubkey"] == self.pubkey: + uids = result['uids'] + for uid_data in uids: + if uid_data["meta"]["timestamp"] > timestamp: + timestamp = uid_data["meta"]["timestamp"] + uid = uid_data["uid"] + signature = uid_data["self"] + + return SelfCertification(PROTOCOL_VERSION, + community.currency, + self.pubkey, + timestamp, + uid, + signature) + return None + + def get_join_date(self, community): + """ + Get the person join date. + This request is not cached in the person object. + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: A datetime object + """ + search = community.bma_access.get(self, qtbma.blockchain.Membership, {'search': self.pubkey}) + if search != qtbma.blockchain.Membership.null_value: + if len(search['memberships']) > 0: + membership_data = search['memberships'][0] + return community.get_block(membership_data['blockNumber'])['medianTime'] + else: + return None + else: + raise MembershipNotFoundError(self.pubkey, community.name) + + def get_expiration_date(self, community): + try: + join_block_number = self.membership(community)['blockNumber'] + try: + join_block = community.bma_access.get(self, qtbma.blockchain.Block, + req_args={'number': join_block_number}) + + parameters = community.bma_access.get(self, qtbma.blockchain.Parameters) + if join_block != qtbma.blockchain.Block.null_value \ + and parameters != qtbma.blockchain.Parameters.null_value: + join_date = join_block['medianTime'] + expiration_date = join_date + parameters['sigValidity'] + else: + return None + except NoPeerAvailable: + expiration_date = None + except MembershipNotFoundError: + expiration_date = None + return expiration_date + + + +#TODO: Manage 'OUT' memberships ? Maybe ? + def membership(self, community): + """ + Get the person last membership document. + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: The membership data in BMA json format + """ + search = community.bma_access.get(self, qtbma.blockchain.Membership, + {'search': self.pubkey}) + if search != qtbma.blockchain.Membership.null_value: + block_number = -1 + for ms in search['memberships']: + if ms['blockNumber'] > block_number: + block_number = ms['blockNumber'] + if 'type' in ms: + if ms['type'] is 'IN': + membership_data = ms + else: + membership_data = ms + return membership_data + else: + raise MembershipNotFoundError(self.pubkey, community.name) + + def published_uid(self, community): + data = community.bma_access.get(self, qtbma.wot.Lookup, + req_args={'search': self.pubkey}) + if data != qtbma.wot.Lookup.null_value: + timestamp = 0 + + for result in data['results']: + if result["pubkey"] == self.pubkey: + uids = result['uids'] + person_uid = "" + for uid_data in uids: + if uid_data["meta"]["timestamp"] > timestamp: + timestamp = uid_data["meta"]["timestamp"] + person_uid = uid_data["uid"] + if person_uid == self.uid: + return True + return False + + def is_member(self, community): + """ + Check if the person is a member of a community + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: True if the person is a member of a community + """ + certifiers = community.bma_access.get(self, qtbma.wot.CertifiersOf, {'search': self.pubkey}) + if certifiers != qtbma.wot.CertifiersOf.null_value: + return certifiers['isMember'] + return False + + def certifiers_of(self, community): + """ + Get the list of this person certifiers + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: The list of the certifiers of this community in BMA json format + """ + certifiers = community.bma_access.get(self, qtbma.wot.CertifiersOf, {'search': self.pubkey}) + if certifiers == qtbma.wot.CertifiersOf.null_value: + logging.debug('bma.wot.CertifiersOf request error') + data = community.bma_access.get(self, qtbma.wot.Lookup, {'search': self.pubkey}) + if data == qtbma.wot.Lookup.null_value: + logging.debug('bma.wot.Lookup request error') + return list() + + # convert api data to certifiers list + certifiers = list() + # add certifiers of uid + + for result in data['results']: + if result["pubkey"] == self.pubkey: + for uid_data in result['uids']: + for certifier_data in uid_data['others']: + for uid in certifier_data['uids']: + # add a certifier + certifier = {} + certifier['uid'] = uid + certifier['pubkey'] = certifier_data['pubkey'] + certifier['isMember'] = certifier_data['isMember'] + certifier['cert_time'] = dict() + certifier['cert_time']['medianTime'] = community.get_block( + certifier_data['meta']['block_number'])['medianTime'] + certifiers.append(certifier) + + return certifiers + return certifiers['certifications'] + + def unique_valid_certifiers_of(self, community): + certifier_list = self.certifiers_of(community) + unique_valid = [] + #  add certifiers of uid + for certifier in tuple(certifier_list): + # add only valid certification... + if community.certification_expired(certifier['cert_time']['medianTime']): + continue + + # keep only the latest certification + already_found = [c['pubkey'] for c in unique_valid] + if certifier['pubkey'] in already_found: + index = already_found.index(certifier['pubkey']) + if certifier['cert_time']['medianTime'] > unique_valid[index]['cert_time']['medianTime']: + unique_valid[index] = certifier + else: + unique_valid.append(certifier) + return unique_valid + + def certified_by(self, community): + """ + Get the list of persons certified by this person + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: The list of the certified persons of this community in BMA json format + """ + certified_list = community.bma_access.get(self, qtbma.wot.CertifiedBy, {'search': self.pubkey}) + if certified_list == qtbma.wot.CertifiedBy.null_value: + logging.debug('bma.wot.CertifiersOf request error') + data = community.bma_access.get(self, qtbma.wot.Lookup, {'search': self.pubkey}) + if data == qtbma.wot.Lookup.null_value: + logging.debug('bma.wot.Lookup request error') + return list() + else: + certified_list = list() + for result in data['results']: + if result["pubkey"] == self.pubkey: + for certified in result['signed']: + certified['cert_time'] = dict() + certified['cert_time']['medianTime'] = certified['meta']['timestamp'] + certified_list.append(certified) + + return certified_list + + return certified_list['certifications'] + + def unique_valid_certified_by(self, community): + certified_list = self.certified_by(community) + unique_valid = [] + #  add certifiers of uid + for certified in tuple(certified_list): + # add only valid certification... + if community.certification_expired(certified['cert_time']['medianTime']): + continue + + # keep only the latest certification + already_found = [c['pubkey'] for c in unique_valid] + if certified['pubkey'] in already_found: + index = already_found.index(certified['pubkey']) + if certified['cert_time']['medianTime'] > unique_valid[index]['cert_time']['medianTime']: + unique_valid[index] = certified + else: + unique_valid.append(certified) + return unique_valid + + def membership_expiration_time(self, community): + join_block = self.membership(community)['blockNumber'] + join_date = community.get_block(join_block)['medianTime'] + parameters = community.parameters + expiration_date = join_date + parameters['sigValidity'] + current_time = time.time() + return expiration_date - current_time + + def jsonify(self): + """ + Get the community as dict in json format. + :return: The community as a dict in json format + """ + data = {'uid': self.uid, + 'pubkey': self.pubkey, + 'status': self.status} + return data + + def __str__(self): + status_str = ("NOT_FOUND", "FOUND") + return "{0} - {1} - {2}".format(self.uid, + self.pubkey, + status_str[self.status]) \ No newline at end of file diff --git a/src/cutecoin/core/transfer.py b/src/cutecoin/core/transfer.py index dcfeb9cfbbcad3db288918c9997acfbfafade8ea..95b316a6014e56a584801ea7ade6535e954b899e 100644 --- a/src/cutecoin/core/transfer.py +++ b/src/cutecoin/core/transfer.py @@ -1,15 +1,17 @@ -''' +""" Created on 31 janv. 2015 @author: inso -''' +""" import logging -from ucoinpy.api import bma -from ucoinpy.documents.transaction import Transaction - - -class Transfer(object): - ''' +import asyncio +from .net.api import bma as qtbma +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject +from PyQt5.QtNetwork import QNetworkReply +import hashlib + +class Transfer(QObject): + """ 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 @@ -18,15 +20,18 @@ class Transfer(object): 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 DROPPED = 5 - def __init__(self, txdoc, state, metadata): - ''' + transfer_broadcasted = pyqtSignal(str) + broadcast_error = pyqtSignal(int, str) + + def __init__(self, hash, state, metadata): + """ The constructor of a transfer. Check for metadata keys which must be present : - receiver @@ -39,7 +44,7 @@ class Transfer(object): :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) assert('time' in metadata) @@ -49,126 +54,134 @@ class Transfer(object): assert('issuer_uid' in metadata) assert('receiver_uid' in metadata) assert('txid' in metadata) + super().__init__() - self.txdoc = txdoc + self.hash = hash self.state = state self._metadata = metadata @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): - ''' + def create_validated(cls, hash, metadata): + """ Create a new transfer in a "VALIDATED" state. - ''' - return cls(txdoc, Transfer.VALIDATED, metadata) + """ + return cls(hash, Transfer.VALIDATED, 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']) + """ + return cls(data['hash'], 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: - txraw = None - return {'txdoc': txraw, + """ + return {'hash': self.hash, 'state': self.state, 'metadata': self._metadata} + @asyncio.coroutine 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, - post_args={'transaction': self.txdoc.signed_raw()}) - self.state = Transfer.AWAITING - except ValueError as e: - if '400' in str(e): - self.state = Transfer.REFUSED - raise - finally: - self._metadata['block'] = community.current_blockid()['number'] - self._metadata['time'] = community.get_block().mediantime + """ + replies = community.bma_access.broadcast(qtbma.tx.Process, + post_args={'transaction': txdoc.signed_raw()}) + for r in replies: + r.finished.connect(lambda reply=r: self.__handle_transfers_reply(replies, reply)) + + self.state = Transfer.AWAITING + self.hash = hashlib.sha1(txdoc.signed_raw().encode("ascii")).hexdigest().upper() + blockid = yield from community.blockid() + self._metadata['block'] = blockid['number'] + self._metadata['time'] = community.get_block()['medianTime'] + + def __handle_transfers_reply(self, replies, reply): + strdata = bytes(reply.readAll()).decode('utf-8') + logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata)) + if reply.error() == QNetworkReply.NoError: + self.transfer_broadcasted.emit(self.metadata['receiver_uid']) + for r in replies: + try: + r.disconnect() + except TypeError as e: + if "disconnect()" in str(e): + logging.debug("Could not disconnect a reply") + else: + for r in replies: + if not r.isFinished() or r.error() == QNetworkReply.NoError: + return + self.broadcast_error.emit(r.error(), strdata) 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): - ''' + def __init__(self, hash, metadata): + """ 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) + """ + super().__init__(hash, 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']) + """ + return cls(data['hash'], data['metadata']) diff --git a/src/cutecoin/core/txhistory.py b/src/cutecoin/core/txhistory.py new file mode 100644 index 0000000000000000000000000000000000000000..edd10236d856dd6e438d87ab734474e05838bb23 --- /dev/null +++ b/src/cutecoin/core/txhistory.py @@ -0,0 +1,215 @@ +import asyncio +import logging +from .transfer import Transfer, Received +from ucoinpy.documents.transaction import InputSource, OutputSource +from ..tools.exceptions import LookupFailureError +from .net.api import bma as qtbma + +class TxHistory(): + def __init__(self, wallet): + self._latest_block = 0 + self.wallet = wallet + self._stop_coroutines = False + + self._transfers = [] + self.available_sources = [] + self._dividends = [] + + @property + def latest_block(self): + return self._latest_block + + @latest_block.setter + def latest_block(self, value): + self._latest_block = value + + def load_from_json(self, data): + self._transfers = [] + + data_sent = data['transfers'] + for s in data_sent: + if s['metadata']['issuer'] == self.wallet.pubkey: + self._transfers.append(Transfer.load(s)) + else: + self._transfers.append(Received.load(s)) + + for s in data['sources']: + self.available_sources.append(InputSource.from_inline(s['inline'])) + + for d in data['dividends']: + self._dividends.append(d) + + self.latest_block = data['latest_block'] + + def jsonify(self): + data_transfer = [] + for s in self.transfers: + data_transfer.append(s.jsonify()) + + data_sources = [] + for s in self.available_sources: + s.index = 0 + data_sources.append({'inline': "{0}\n".format(s.inline())}) + + data_dividends = [] + for d in self._dividends: + data_dividends.append(d) + + return {'latest_block': self.latest_block, + 'transfers': data_transfer, + 'sources': data_sources, + 'dividends': data_dividends} + + @property + def transfers(self): + return [t for t in self._transfers if t.state != Transfer.DROPPED] + + @property + def dividends(self): + return self._dividends.copy() + + def stop_coroutines(self): + self._stop_coroutines = True + + @asyncio.coroutine + def _parse_transaction(self, community, txdata, received_list, txid): + tx_outputs = [OutputSource.from_inline(o) for o in txdata['outputs']] + receivers = [o.pubkey for o in tx_outputs + if o.pubkey != txdata['issuers'][0]] + + block_number = txdata['block_number'] + mediantime = txdata['time'] + logging.debug(txdata) + + if len(receivers) == 0: + receivers = [txdata['issuers'][0]] + + try: + issuer = yield from self.wallet._identities_registry.future_lookup(txdata['issuers'][0], community) + issuer_uid = issuer.uid + except LookupFailureError: + issuer_uid = "" + + try: + receiver = yield from self.wallet._identities_registry.future_lookup(receivers[0], community) + receiver_uid = receiver.uid + except LookupFailureError: + receiver_uid = "" + + metadata = {'block': block_number, + 'time': mediantime, + 'comment': txdata['comment'], + 'issuer': txdata['issuers'][0], + 'issuer_uid': issuer_uid, + 'receiver': receivers[0], + 'receiver_uid': receiver_uid, + 'txid': txid} + + in_issuers = len([i for i in txdata['issuers'] + if i == self.wallet.pubkey]) > 0 + in_outputs = len([o for o in tx_outputs + if o.pubkey == self.wallet.pubkey]) > 0 + + # If the wallet pubkey is in the issuers we sent this transaction + if in_issuers: + outputs = [o for o in tx_outputs + if o.pubkey != self.wallet.pubkey] + amount = 0 + for o in outputs: + amount += o.amount + metadata['amount'] = amount + + awaiting = [t for t in self._transfers + if t.state == Transfer.AWAITING] + # We check if the transaction correspond to one we sent + if txdata['hash'] not in [t['hash'] for t in awaiting]: + transfer = Transfer.create_validated(txdata['hash'], + metadata.copy()) + return transfer + # If we are not in the issuers, + # maybe it we are in the recipients of this transaction + elif in_outputs: + outputs = [o for o in tx_outputs + if o.pubkey == self.wallet.pubkey] + amount = 0 + for o in outputs: + amount += o.amount + metadata['amount'] = amount + received = Received(txdata['hash'], metadata.copy()) + received_list.append(received) + return received + return None + + @asyncio.coroutine + def refresh(self, community, received_list): + """ + Refresh last transactions + + :param cutecoin.core.Community community: The community + :param list received_list: List of transactions received + """ + parsed_block = self.latest_block + current_block = community.network.latest_block + logging.debug("Refresh from : {0} to {1}".format(self.latest_block, current_block)) + dividends_data = qtbma.ud.History.null_value + while dividends_data == qtbma.ud.History.null_value: + dividends_data = yield from community.bma_access.future_request(qtbma.ud.History, + req_args={'pubkey': self.wallet.pubkey}) + + dividends = dividends_data['history']['history'] + for d in dividends: + if d['block_number'] not in range(parsed_block, parsed_block+99): + dividends.remove(d) + + new_transfers = [] + new_dividends = [] + # Lets look if transactions took too long to be validated + awaiting = [t for t in self._transfers + if t.state == Transfer.AWAITING] + while parsed_block < current_block: + tx_history = qtbma.tx.history.Blocks.null_value + while tx_history == qtbma.tx.history.Blocks.null_value: + tx_history = yield from community.bma_access.future_request(qtbma.tx.history.Blocks, + req_args={'pubkey': self.wallet.pubkey, + 'from_':str(parsed_block), + 'to_': str(parsed_block + 99)}) + if self._stop_coroutines: + return + + udid = 0 + for d in dividends: + if d['block_number'] in range(parsed_block, parsed_block+99): + d['id'] = udid + new_dividends.append(d) + udid += 1 + + # We parse only blocks with transactions + transactions = tx_history['history']['received'] + tx_history['history']['sent'] + for (txid, txdata) in enumerate(transactions): + if self._stop_coroutines: + return + if len(txdata['issuers']) == 0: + logging.debug("Error with : {0}, from {1} to {2}".format(self.wallet.pubkey, + parsed_block, + current_block)) + else: + transfer = yield from self._parse_transaction(community, txdata, received_list, udid + txid) + if transfer: + new_transfers.append(transfer) + + self.wallet.refresh_progressed.emit(parsed_block, current_block, self.wallet.pubkey) + parsed_block += 100 + + if current_block > self.latest_block: + self.available_sources = yield from self.wallet.future_sources(community) + if self._stop_coroutines: + return + self.latest_block = current_block + + for transfer in awaiting: + transfer.check_refused(current_block) + + self._transfers = self._transfers + new_transfers + self._dividends = self._dividends + new_dividends + + self.wallet.refresh_finished.emit(received_list) diff --git a/src/cutecoin/core/wallet.py b/src/cutecoin/core/wallet.py index 415f05bc2abf81fcc7262f310b27d992a1c50b1f..a0628c52f15ceb1d8e4afc7e5b6555cd6a2bc34b 100644 --- a/src/cutecoin/core/wallet.py +++ b/src/cutecoin/core/wallet.py @@ -1,297 +1,130 @@ -''' +""" Created on 1 févr. 2014 @author: inso -''' +""" -from ucoinpy import PROTOCOL_VERSION -from ucoinpy.api import bma -from ucoinpy.documents.block import Block from ucoinpy.documents.transaction import InputSource, OutputSource, Transaction from ucoinpy.key import SigningKey -from ..tools.exceptions import NotEnoughMoneyError, NoPeerAvailable, PersonNotFoundError +from .net.api import bma as qtbma +from .net.api.bma import PROTOCOL_VERSION +from ..tools.exceptions import NotEnoughMoneyError, NoPeerAvailable, LookupFailureError from .transfer import Transfer, Received -from .person import Person +from .txhistory import TxHistory +from .registry import IdentitiesRegistry, Identity -from PyQt5.QtCore import QObject, pyqtSignal +from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication import logging - - -class Cache(): - def __init__(self, wallet): - self._latest_block = 0 - self.wallet = wallet - - self._transfers = [] - self.available_sources = [] - - @property - def latest_block(self): - return self._latest_block - - @latest_block.setter - def latest_block(self, value): - self._latest_block = value - - def load_from_json(self, data): - self._transfers = [] - - data_sent = data['transfers'] - for s in data_sent: - if s['metadata']['issuer'] == self.wallet.pubkey: - self._transfers.append(Transfer.load(s)) - else: - self._transfers.append(Received.load(s)) - - for s in data['sources']: - self.available_sources.append(InputSource.from_inline(s['inline'])) - - self.latest_block = data['latest_block'] - - def jsonify(self): - data_transfer = [] - for s in self.transfers: - data_transfer.append(s.jsonify()) - - data_sources = [] - for s in self.available_sources: - s.index = 0 - data_sources.append({'inline': "{0}\n".format(s.inline())}) - - return {'latest_block': self.latest_block, - 'transfers': data_transfer, - 'sources': data_sources} - - @property - def transfers(self): - return [t for t in self._transfers if t.state != Transfer.DROPPED] - - def _parse_transaction(self, community, tx, block_number, - mediantime, received_list, txid): - #logging.debug(tx.signed_raw()) - receivers = [o.pubkey for o in tx.outputs - if o.pubkey != tx.issuers[0]] - - if len(receivers) == 0: - receivers = [tx.issuers[0]] - - try: - issuer_uid = Person.lookup(tx.issuers[0], community).uid - except PersonNotFoundError: - issuer_uid = "" - - try: - receiver_uid = Person.lookup(receivers[0], community).uid - except PersonNotFoundError: - receiver_uid = "" - - metadata = {'block': block_number, - 'time': mediantime, - 'comment': tx.comment, - 'issuer': tx.issuers[0], - 'issuer_uid': issuer_uid, - 'receiver': receivers[0], - 'receiver_uid': receiver_uid, - 'txid': txid} - - in_issuers = len([i for i in tx.issuers - if i == self.wallet.pubkey]) > 0 - in_outputs = len([o for o in tx.outputs - if o.pubkey == self.wallet.pubkey]) > 0 - - # If the wallet pubkey is in the issuers we sent this transaction - if in_issuers: - outputs = [o for o in tx.outputs - if o.pubkey != self.wallet.pubkey] - amount = 0 - for o in outputs: - amount += o.amount - metadata['amount'] = amount - - awaiting = [t for t in self._transfers - if t.state == Transfer.AWAITING] - # We check if the transaction correspond to one we sent - if tx.signed_raw() not in [t.txdoc.signed_raw() for t in awaiting]: - transfer = Transfer.create_validated(tx, - metadata.copy()) - self._transfers.append(transfer) - # If we are not in the issuers, - # maybe it we are in the recipients of this transaction - elif in_outputs: - outputs = [o for o in tx.outputs - if o.pubkey == self.wallet.pubkey] - amount = 0 - for o in outputs: - amount += o.amount - metadata['amount'] = amount - received = Received(tx, metadata.copy()) - received_list.append(received) - self._transfers.append(received) - - - def _parse_block(self, community, block_number, received_list): - block = community.request(bma.blockchain.Block, - req_args={'number': block_number}) - signed_raw = "{0}{1}\n".format(block['raw'], - block['signature']) - try: - block_doc = Block.from_signed_raw(signed_raw) - except: - logging.debug("Error in {0}".format(block_number)) - raise - for (txid, tx) in enumerate(block_doc.transactions): - self._parse_transaction(community, tx, block_number, - block_doc.mediantime, received_list, - txid) - - logging.debug("Received {0} transactions".format(len(received_list))) - awaiting = [t for t in self._transfers - if t.state == Transfer.AWAITING] - # After we checked all transactions, we check if - # sent transactions still waiting for validation - # have to be considered refused - for transfer in awaiting: - transfer.check_registered(tx, block_number, - block_doc.mediantime) - - def refresh(self, community, received_list): - current_block = 0 - try: - block_data = community.current_blockid() - current_block = block_data['number'] - - # Lets look if transactions took too long to be validated - awaiting = [t for t in self._transfers - if t.state == Transfer.AWAITING] - with_tx = community.request(bma.blockchain.TX) - - # We parse only blocks with transactions - parsed_blocks = reversed(range(self.latest_block + 1, - current_block + 1)) - logging.debug("Refresh from {0} to {1}".format(self.latest_block + 1, - current_block + 1)) - parsed_blocks = [n for n in parsed_blocks - if n in with_tx['result']['blocks']] - logging.debug(parsed_blocks) - self.wallet.refresh_progressed.emit(self.latest_block, current_block) - - for block_number in parsed_blocks: - self._parse_block(community, block_number, received_list) - self.wallet.refresh_progressed.emit(current_block - block_number, - current_block - self.latest_block) - - if current_block > self.latest_block: - self.available_sources = self.wallet.sources(community) - self.latest_block = current_block - - for transfer in awaiting: - transfer.check_refused(current_block) - - except NoPeerAvailable: - return +import asyncio class Wallet(QObject): - ''' + """ A wallet is used to manage money with a unique key. - ''' + """ - refresh_progressed = pyqtSignal(int, int) + inner_data_changed = pyqtSignal(str) + refresh_progressed = pyqtSignal(int, int, str) + refresh_finished = pyqtSignal(list) + transfer_broadcasted = pyqtSignal(str) + broadcast_error = pyqtSignal(int, str) - def __init__(self, walletid, pubkey, name): - ''' + def __init__(self, walletid, pubkey, name, identities_registry): + """ 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 = [] self.walletid = walletid self.pubkey = pubkey self.name = name + self._identities_registry = identities_registry self.caches = {} @classmethod - def create(cls, walletid, salt, password, name): - ''' + def create(cls, walletid, salt, password, name, identities_registry): + """ 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: key = SigningKey("{0}{1}".format(salt, walletid), password) - return cls(walletid, key.pubkey, name) + return cls(walletid, key.pubkey, name, identities_registry) @classmethod - def load(cls, json_data): - ''' + def load(cls, json_data, identities_registry): + """ 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) + return cls(walletid, pubkey, name, identities_registry) 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] = TxHistory(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 init_cache(self, community): - ''' + """ Init 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] = TxHistory(self) - def refresh_cache(self, community, received_list): - ''' + def refresh_transactions(self, community, received_list): + """ Refresh the cache of this wallet for the specified community. :param community: The community to refresh its cache - ''' - self.caches[community.currency].refresh(community, received_list) + """ + logging.debug("Refresh transactions for {0}".format(self.pubkey)) + asyncio.async(self.caches[community.currency].refresh(community, received_list)) 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) @@ -300,38 +133,53 @@ class Wallet(QObject): return (key.pubkey == self.pubkey) 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 + @asyncio.coroutine + def future_value(self, community): + """ + Get wallet absolute value + + :param community: The community to get value + :return: The wallet absolute value + """ + value = 0 + sources = yield from self.future_sources(community) + for s in sources: + value += s.amount + return 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): + sources = self.sources(community) + for s in sources: 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] @@ -350,7 +198,7 @@ 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 @@ -358,7 +206,7 @@ class Wallet(QObject): :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: @@ -371,9 +219,10 @@ class Wallet(QObject): outputs.append(OutputSource(self.pubkey, overhead)) return outputs + @asyncio.coroutine 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 @@ -382,11 +231,12 @@ class Wallet(QObject): :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'] - block = community.request(bma.blockchain.Block, + """ + blockid = yield from community.blockid() + block_number = blockid['number'] + block = yield from community.bma_access.future_request(qtbma.blockchain.Block, req_args={'number': block_number}) + time = block['medianTime'] txid = len(block['transactions']) key = None logging.debug("Key : {0} : {1}".format(salt, password)) @@ -397,13 +247,15 @@ class Wallet(QObject): logging.debug("Sender pubkey:{0}".format(key.pubkey)) try: - issuer_uid = Person.lookup(key.pubkey, community).uid - except PersonNotFoundError: + issuer = yield from self._identities_registry.future_lookup(key.pubkey, community) + issuer_uid = issuer.uid + except LookupFailureError as e: issuer_uid = "" try: - receiver_uid = Person.lookup(recipient, community).uid - except PersonNotFoundError: + receiver = yield from self._identities_registry.future_lookup(recipient, community) + receiver_uid = receiver.uid + except LookupFailureError as e: receiver_uid = "" metadata = {'block': block_number, @@ -422,10 +274,10 @@ class Wallet(QObject): result = self.tx_inputs(int(amount), community) inputs = result[0] - self.caches[community.currency].available_sources = result[1] + self.caches[community.currency].available_sources = result[1:] logging.debug("Inputs : {0}".format(inputs)) - outputs = self.tx_outputs(recipient, amount, inputs) + outputs = self.tx_outputs(recipient, amount, inputs) logging.debug("Outputs : {0}".format(outputs)) tx = Transaction(PROTOCOL_VERSION, community.currency, [self.pubkey], inputs, @@ -434,16 +286,33 @@ class Wallet(QObject): tx.sign([key]) logging.debug("Transaction : {0}".format(tx.signed_raw())) - transfer.send(tx, community) + transfer.transfer_broadcasted.connect(self.transfer_broadcasted) + transfer.broadcast_error.connect(self.broadcast_error) + yield from transfer.send(tx, community) + + @asyncio.coroutine + def future_sources(self, community): + """ + Get available sources in a given community + + :param cutecoin.core.community.Community community: The community where we want available sources + :return: List of InputSource ucoinpy objects + """ + data = yield from community.bma_access.future_request(qtbma.tx.Sources, + req_args={'pubkey': self.pubkey}) + tx = [] + for s in data['sources']: + tx.append(InputSource.from_bma(s)) + return tx def sources(self, community): - ''' + """ Get available sources in a given community - :param community: The community where we want available sources + :param cutecoin.core.community.Community community: The community where we want available sources :return: List of InputSource ucoinpy objects - ''' - data = community.request(bma.tx.Sources, + """ + data = community.bma_access.get(self, qtbma.tx.Sources, req_args={'pubkey': self.pubkey}) tx = [] for s in data['sources']: @@ -451,20 +320,39 @@ 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 + """ + if community.currency in self.caches: + return self.caches[community.currency].transfers + else: + return [] + + def dividends(self, community): + """ + Get all the dividends received by this wallet + + :param community: The community we want to get received dividends + :return: Result of udhistory request + """ + if community.currency in self.caches: + return self.caches[community.currency].dividends + else: + return [] + + def stop_coroutines(self): + for c in self.caches.values(): + c.stop_coroutines() 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/watching/blockchain.py b/src/cutecoin/core/watching/blockchain.py deleted file mode 100644 index a1f6c46179912a2a1302525307b20c7fbb4283f1..0000000000000000000000000000000000000000 --- a/src/cutecoin/core/watching/blockchain.py +++ /dev/null @@ -1,63 +0,0 @@ -''' -Created on 27 févr. 2015 - -@author: inso -''' - -import logging -import time -from requests.exceptions import RequestException -from ...tools.exceptions import NoPeerAvailable -from .watcher import Watcher -from PyQt5.QtCore import pyqtSignal - - -class BlockchainWatcher(Watcher): - - new_transfers = pyqtSignal(list) - loading_progressed = pyqtSignal(int, int) - - def __init__(self, account, community): - super().__init__() - self.account = account - self.community = community - self.time_to_wait = int(self.community.parameters['avgGenTime'] / 10) - self.exiting = False - self.last_block = self.community.network.latest_block - - def watch(self): - loaded_wallets = 0 - def progressing(value, maximum): - account_value = maximum * loaded_wallets + value - account_max = maximum * len(self.account.wallets) - self.loading_progressed.emit(account_value, account_max) - - try: - received_list = [] - block_number = self.community.network.latest_block - if self.last_block != block_number: - - for w in self.account.wallets: - w.refresh_progressed.connect(progressing) - - if not self.exiting: - self.community.refresh_cache() - for w in self.account.wallets: - if not self.exiting: - w.refresh_cache(self.community, received_list) - loaded_wallets = loaded_wallets + 1 - - logging.debug("New block, {0} mined in {1}".format(block_number, - self.community.currency)) - self.last_block = block_number - if len(received_list) > 0: - self.new_transfers.emit(received_list) - except NoPeerAvailable: - pass - except RequestException as e: - self.error.emit("Cannot check new block : {0}".format(str(e))) - finally: - self.watching_stopped.emit() - - def stop(self): - self.exiting = True diff --git a/src/cutecoin/core/watching/monitor.py b/src/cutecoin/core/watching/monitor.py deleted file mode 100644 index c7086527f3789b89555546f897e75a83ef512a05..0000000000000000000000000000000000000000 --- a/src/cutecoin/core/watching/monitor.py +++ /dev/null @@ -1,91 +0,0 @@ -''' -Created on 18 mars 2015 - -@author: inso -''' - -from PyQt5.QtCore import QThread, Qt, QObject -from .blockchain import BlockchainWatcher -from .persons import PersonsWatcher -import logging - - -class Monitor(object): - ''' - The monitor is managing watchers - ''' - - # Dirty hack to avoid GC on monitors - # GC was causing random crashes - # We will get rid of QThreads asap - #___dirty_monitors = [] - - def __init__(self, account): - ''' - Constructor - ''' - super().__init__() - self.account = account - self.threads_pool = [] - self._blockchain_watchers = {} - self._network_watchers = {} - self._persons_watchers = {} - #Monitor.___dirty_monitors.append(self) - - def blockchain_watcher(self, community): - return self._blockchain_watchers[community.name] - - def network_watcher(self, community): - return self._networks[community.name] - - def persons_watcher(self, community): - return self._persons_watchers[community.name] - - def connect_watcher_to_thread(self, watcher): - thread = QThread() - watcher.moveToThread(thread) - thread.started.connect(watcher.watch) - watcher.watching_stopped.connect(thread.exit) - - self.threads_pool.append(thread) - - def prepare_watching(self): - for c in self.account.communities: - persons_watcher = PersonsWatcher(c) - self.connect_watcher_to_thread(persons_watcher) - self._persons_watchers[c.name] = persons_watcher - - bc_watcher = BlockchainWatcher(self.account, c) - self.connect_watcher_to_thread(bc_watcher) - self._blockchain_watchers[c.name] = bc_watcher - - self.connect_watcher_to_thread(c.network) - self._network_watchers[c.name] = c.network - - def start_network_watchers(self): - for watcher in self._network_watchers.values(): - watcher.thread().start() - - def stop_watching(self): - for watcher in self._persons_watchers.values(): - watcher.stop() - self.threads_pool.remove(watcher.thread()) - watcher.deleteLater() - watcher.thread().deleteLater() - - for watcher in self._blockchain_watchers.values(): - watcher.stop() - self.threads_pool.remove(watcher.thread()) - watcher.deleteLater() - watcher.thread().deleteLater() - - for watcher in self._network_watchers.values(): - watcher.stop() - self.threads_pool.remove(watcher.thread()) - watcher.deleteLater() - watcher.thread().deleteLater() - - self.threads_pool = [] - self._blockchain_watchers = {} - self._network_watchers = {} - self._persons_watchers = {} diff --git a/src/cutecoin/core/watching/persons.py b/src/cutecoin/core/watching/persons.py deleted file mode 100644 index 6311acd524b39b8ed53ba3723ac7cac88c837daf..0000000000000000000000000000000000000000 --- a/src/cutecoin/core/watching/persons.py +++ /dev/null @@ -1,44 +0,0 @@ -''' -Created on 27 févr. 2015 - -@author: inso -''' - -from PyQt5.QtCore import pyqtSignal -from ..person import Person -from .watcher import Watcher -import logging - - -class PersonsWatcher(Watcher): - ''' - This will crawl the network to always - have up to date informations about the nodes - ''' - person_changed = pyqtSignal(str) - - def __init__(self, community): - super().__init__() - self.community = community - self.exiting = False - - def watch(self): - logging.debug("Watching persons") - instances = Person._instances.copy() - for p in instances.values(): - if not self.exiting: - for func in [Person.membership, - Person.is_member, - Person.certifiers_of, - Person.certified_by, - Person.published_uid]: - if not self.exiting: - if p.reload(func, self.community): - logging.debug("Change detected on {0} about {1}".format(p.pubkey, - func.__name__)) - self.person_changed.emit(p.pubkey) - logging.debug("Finished watching persons") - self.watching_stopped.emit() - - def stop(self): - self.exiting = True \ No newline at end of file diff --git a/src/cutecoin/core/watching/watcher.py b/src/cutecoin/core/watching/watcher.py deleted file mode 100644 index ead092282525b64fd85e309c242b545320f191c6..0000000000000000000000000000000000000000 --- a/src/cutecoin/core/watching/watcher.py +++ /dev/null @@ -1,23 +0,0 @@ -''' -Created on 20 mars 2015 - -@author: inso -''' - -from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal - - -class Watcher(QObject): - watching_stopped = pyqtSignal() - error = pyqtSignal(str) - - def __init__(self): - super().__init__() - - @pyqtSlot() - def watch(self): - pass - - @pyqtSlot() - def stop(self): - pass diff --git a/src/cutecoin/gen_resources/__init__.py b/src/cutecoin/gen_resources/__init__.py index 82d70b56cad15bed8a6accdacfde0024af7e40a8..260b8d64fc04e27477f54b75e0ced47834eb159f 100644 --- a/src/cutecoin/gen_resources/__init__.py +++ b/src/cutecoin/gen_resources/__init__.py @@ -1,5 +1,5 @@ -''' +""" Created on 11 mars 2014 @author: inso -''' +""" diff --git a/src/cutecoin/gui/__init__.py b/src/cutecoin/gui/__init__.py index 19560381f7b99b47389d0bd8d6fddad655d596eb..3d5bba2e32c7b314ba23fc09e9d0566c0319d4c8 100644 --- a/src/cutecoin/gui/__init__.py +++ b/src/cutecoin/gui/__init__.py @@ -1,6 +1,6 @@ -''' +""" Created on 11 mars 2014 @author: inso -''' +""" from PyQt5 import QtSvg \ No newline at end of file diff --git a/src/cutecoin/gui/certification.py b/src/cutecoin/gui/certification.py index 96f2c5aabf8dee010458bb9b79143af575040b97..1ef4d400f4af4bb1f509ae0bab5491399fcb903c 100644 --- a/src/cutecoin/gui/certification.py +++ b/src/cutecoin/gui/certification.py @@ -1,27 +1,29 @@ -''' +""" Created on 24 dec. 2014 @author: inso -''' +""" from PyQt5.QtWidgets import QDialog, QMessageBox, QDialogButtonBox, QApplication -from PyQt5.QtCore import Qt -from ..tools.exceptions import NoPeerAvailable +from PyQt5.QtCore import Qt, pyqtSlot +import quamash from ..gen_resources.certification_uic import Ui_CertificationDialog from . import toast +import asyncio class CertificationDialog(QDialog, Ui_CertificationDialog): - ''' + """ classdocs - ''' + """ - def __init__(self, certifier, password_asker): - ''' + def __init__(self, certifier, app, password_asker): + """ Constructor - ''' + """ super().__init__() self.setupUi(self) + self.app = app self.account = certifier self.password_asker = password_asker self.community = self.account.communities[0] @@ -43,32 +45,31 @@ class CertificationDialog(QDialog, Ui_CertificationDialog): if password == "": return - try: - QApplication.setOverrideCursor(Qt.WaitCursor) - self.account.certify(password, self.community, pubkey) - toast.display(self.tr("Certification"), - self.tr("Success certifying {0} from {1}").format(pubkey, - self.community.currency)) - except ValueError as e: - QMessageBox.critical(self, self.tr("Certification"), - self.tr("Something wrong happened : {0}").format(e), - QMessageBox.Ok) - return - except NoPeerAvailable as e: - QMessageBox.critical(self, self.tr("Certification"), - self.tr("Couldn't connect to network : {0}").format(e), - QMessageBox.Ok) - return - except Exception as e: - QMessageBox.critical(self, self.tr("Error"), - "{0}".format(e), - QMessageBox.Ok) - return - finally: - QApplication.restoreOverrideCursor() + QApplication.setOverrideCursor(Qt.WaitCursor) + self.account.certification_broadcasted.connect(lambda: self.certification_sent(self.community, + pubkey)) + self.account.broadcast_error.connect(self.handle_error) + asyncio.async(self.account.certify(password, self.community, pubkey)) + + def certification_sent(self, pubkey, currency): + toast.display(self.tr("Certification"), + self.tr("Success certifying {0} from {1}").format(pubkey, currency)) + self.account.certification_broadcasted.disconnect() + self.account.broadcast_error.disconnect(self.handle_error) + QApplication.restoreOverrideCursor() super().accept() + @pyqtSlot(int, str) + def handle_error(self, error_code, text): + if self.app.preferences['notifications']: + toast.display(self.tr("Error"), self.tr("{0} : {1}".format(error_code, text))) + else: + QMessageBox.Critical(self, self.tr("Error", self.tr("{0} : {1}".format(error_code, text)))) + self.account.certification_broadcasted.disconnect() + self.account.broadcast_error.disconnect(self.handle_error) + QApplication.restoreOverrideCursor() + def change_current_community(self, index): self.community = self.account.communities[index] if self.account.pubkey in self.community.members_pubkeys(): diff --git a/src/cutecoin/gui/community_tab.py b/src/cutecoin/gui/community_tab.py index 724b89957ab1cd00ae04802eb5816bf0c44bccf1..ae672340d2c7fbe3f5b2bc9f808fc04f2cae3bec 100644 --- a/src/cutecoin/gui/community_tab.py +++ b/src/cutecoin/gui/community_tab.py @@ -1,8 +1,8 @@ -''' +""" Created on 2 févr. 2014 @author: inso -''' +""" import logging from PyQt5.QtCore import Qt, pyqtSlot @@ -17,16 +17,16 @@ from .wot_tab import WotTabWidget from .transfer import TransferMoneyDialog from .certification import CertificationDialog from . import toast -from ..tools.exceptions import PersonNotFoundError, NoPeerAvailable -from ..core.person import Person -from ucoinpy.api import bma +import asyncio +from ..tools.exceptions import LookupFailureError, NoPeerAvailable +from ..core.net.api import bma as qtbma class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): - ''' + """ classdocs - ''' + """ def __init__(self, app, account, community, password_asker, parent): """ @@ -38,12 +38,15 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): :return: """ super().__init__() - self.setupUi(self) self.parent = parent + self.app = app self.community = community self.account = account self.password_asker = password_asker - identities_model = IdentitiesTableModel(community) + + self.setupUi(self) + + identities_model = IdentitiesTableModel(self.community) proxy = IdentitiesFilterProxyModel() proxy.setSourceModel(identities_model) self.table_identities.setModel(proxy) @@ -51,9 +54,8 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): self.table_identities.customContextMenuRequested.connect(self.identity_context_menu) self.table_identities.sortByColumn(0, Qt.AscendingOrder) self.table_identities.resizeColumnsToContents() - app.monitor.persons_watcher(self.community).person_changed.connect(self.refresh_person) - self.wot_tab = WotTabWidget(app, account, community, password_asker, self) + self.wot_tab = WotTabWidget(self.app, self.account, self.community, self.password_asker, self) self.tabs_information.addTab(self.wot_tab, QIcon(':/icons/wot_icon'), self.tr("Web of Trust")) members_action = QAction(self.tr("Members"), self) members_action.triggered.connect(self.search_members) @@ -61,9 +63,32 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): direct_connections = QAction(self.tr("Direct connections"), self) direct_connections.triggered.connect(self.search_direct_connections) self.button_search.addAction(direct_connections) - self.refresh() + + self.account.identity(self.community).inner_data_changed.connect(self.handle_account_identity_change) + self.search_direct_connections() + self.account.membership_broadcasted.connect(self.handle_membership_broadcasted) + self.account.revoke_broadcasted.connect(self.handle_revoke_broadcasted) + self.account.selfcert_broadcasted.connect(self.handle_selfcert_broadcasted) self.refresh_quality_buttons() + def handle_membership_broadcasted(self): + if self.app.preferences['notifications']: + toast.display(self.tr("Membership"), self.tr("Success sending Membership demand")) + else: + QMessageBox.information(self, self.tr("Membership"), self.tr("Success sending Membership demand")) + + def handle_revoke_broadcasted(self): + if self.app.preferences['notifications']: + toast.display(self.tr("Revoke"), self.tr("Success sending Revoke demand")) + else: + QMessageBox.information(self, self.tr("Revoke"), self.tr("Success sending Revoke demand")) + + def handle_selfcert_broadcasted(self): + if self.app.preferences['notifications']: + toast.display(self.tr("Self Certification"), self.tr("Success sending Self Certification document")) + else: + QMessageBox.information(self.tr("Self Certification"), self.tr("Success sending Self Certification document")) + def identity_context_menu(self, point): index = self.table_identities.indexAt(point) model = self.table_identities.model() @@ -73,13 +98,12 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): pubkey_index = model.sourceModel().index(source_index.row(), pubkey_col) pubkey = model.sourceModel().data(pubkey_index, Qt.DisplayRole) - identity = Person.lookup(pubkey, self.community) + identity = self.app.identities_registry.lookup(pubkey, self.community) menu = QMenu(self) informations = QAction(self.tr("Informations"), self) informations.triggered.connect(self.menu_informations) informations.setData(identity) - add_contact = QAction(self.tr("Add as contact"), self) add_contact.triggered.connect(self.menu_add_as_contact) add_contact.setData(identity) @@ -145,10 +169,10 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): currency_tab = self.window().currencies_tabwidget.currentWidget() currency_tab.tab_history.table_history.model().sourceModel().refresh_transfers() - def certify_identity(self, person): - dialog = CertificationDialog(self.account, self.password_asker) + def certify_identity(self, identity): + dialog = CertificationDialog(self.account, self.app, self.password_asker) dialog.combo_community.setCurrentText(self.community.name) - dialog.edit_pubkey.setText(person.pubkey) + dialog.edit_pubkey.setText(identity.pubkey) dialog.radio_pubkey.setChecked(True) dialog.exec_() @@ -166,25 +190,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): password = self.password_asker.exec_() if self.password_asker.result() == QDialog.Rejected: return - - try: - self.account.send_membership(password, self.community, 'IN') - toast.display(self.tr("Membership"), self.tr("Success sending membership demand")) - except ValueError as e: - QMessageBox.critical(self, self.tr("Join demand error"), - str(e)) - except PersonNotFoundError as e: - QMessageBox.critical(self, self.tr("Key not sent to community"), - self.tr(""""Your key wasn't sent in the community. -You can't request a membership.""")) - except NoPeerAvailable as e: - QMessageBox.critical(self, self.tr("Network error"), - self.tr("Couldn't connect to network : {0}").format(e), - QMessageBox.Ok) - except Exception as e: - QMessageBox.critical(self, "Error", - "{0}".format(e), - QMessageBox.Ok) + asyncio.async(self.account.send_membership(password, self.community, 'IN')) def send_membership_leaving(self): reply = QMessageBox.warning(self, self.tr("Warning"), @@ -197,20 +203,7 @@ The process to join back the community later will have to be done again.""") if self.password_asker.result() == QDialog.Rejected: return - try: - self.account.send_membership(password, self.community, 'OUT') - toast.display(self.tr("Membership"), self.tr("Success sending leaving demand")) - except ValueError as e: - QMessageBox.critical(self, self.tr("Leaving demand error"), - str(e)) - except NoPeerAvailable as e: - QMessageBox.critical(self, self.tr("Network error"), - self.tr("Couldn't connect to network : {0}").format(e), - QMessageBox.Ok) - except Exception as e: - QMessageBox.critical(self, self.tr("Error"), - "{0}".format(e), - QMessageBox.Ok) + asyncio.async(self.account.send_membership(password, self.community, 'OUT')) def publish_uid(self): reply = QMessageBox.warning(self, self.tr("Warning"), @@ -233,10 +226,10 @@ Publishing your UID can be canceled by Revoke UID.""") QMessageBox.critical(self, self.tr("Network error"), self.tr("Couldn't connect to network : {0}").format(e), QMessageBox.Ok) - except Exception as e: - QMessageBox.critical(self, self.tr("Error"), - "{0}".format(e), - QMessageBox.Ok) + # except Exception as e: + # QMessageBox.critical(self, self.tr("Error"), + # "{0}".format(e), + # QMessageBox.Ok) def revoke_uid(self): reply = QMessageBox.warning(self, self.tr("Warning"), @@ -248,21 +241,28 @@ Revoking your UID can only success if it is not already validated by the network if self.password_asker.result() == QDialog.Rejected: return - try: - self.account.revoke(password, self.community) - toast.display(self.tr("UID Revoking"), - self.tr("Success revoking your UID")) - except ValueError as e: - QMessageBox.critical(self, self.tr("Revoke UID error"), - str(e)) - except NoPeerAvailable as e: - QMessageBox.critical(self, self.tr("Network error"), - self.tr("Couldn't connect to network : {0}").format(e), - QMessageBox.Ok) - except Exception as e: - QMessageBox.critical(self, self.tr("Error"), - "{0}".format(e), - QMessageBox.Ok) + asyncio.async(self.account.revoke(password, self.community)) + + @asyncio.coroutine + def _execute_search_text(self, text): + response = yield from self.community.bma_access.future_request(qtbma.wot.Lookup, {'search': text}) + identities = [] + for identity_data in response['results']: + identity = yield from self.app.identities_registry.future_lookup(identity_data['pubkey'], self.community) + identities.append(identity) + + self_identity = self.account.identity(self.community) + try: + self_identity.inner_data_changed.disconnect(self.handle_account_identity_change) + self.community.inner_data_changed.disconnect(self.handle_community_change) + except TypeError as e: + if "disconnect() failed" in str(e): + pass + else: + raise + + self.edit_textsearch.clear() + self.refresh_identities(identities) def search_text(self): """ @@ -272,65 +272,82 @@ Revoking your UID can only success if it is not already validated by the network if len(text) < 2: return False - try: - response = self.community.request(bma.wot.Lookup, {'search': text}) - except Exception as e: - logging.debug('bma.wot.Lookup request error : ' + str(e)) - return False + else: + asyncio.async(self._execute_search_text(text)) - persons = [] - for identity in response['results']: - persons.append(Person.lookup(identity['pubkey'], self.community)) + @pyqtSlot(str) + def handle_community_change(self, origin): + logging.debug("Handle account community {0}".format(origin)) + if origin == qtbma.wot.Members: + self.search_members() - self.edit_textsearch.clear() - self.refresh(persons) + @pyqtSlot(str) + def handle_account_identity_change(self, origin): + logging.debug("Handle account identity change {0}".format(origin)) + if origin in (str(qtbma.wot.CertifiedBy), str(qtbma.wot.CertifiersOf)): + self.search_direct_connections() def search_members(self): """ Search members of community and display found members """ pubkeys = self.community.members_pubkeys() - persons = [] + identities = [] for p in pubkeys: - persons.append(Person.lookup(p, self.community)) + identities.append(self.app.identities_registry.lookup(p, self.community)) + + self_identity = self.account.identity(self.community) + + try: + self_identity.inner_data_changed.disconnect(self.handle_account_identity_change) + self.community.inner_data_changed.connect(self.handle_community_change) + except TypeError as e: + if "disconnect() failed" in str(e): + pass + else: + raise self.edit_textsearch.clear() - self.refresh(persons) + self.refresh_identities(identities) def search_direct_connections(self): """ Search members of community and display found members """ - self.refresh() - - def refresh(self, persons=None): - ''' + self_identity = self.account.identity(self.community) + try: + self.community.inner_data_changed.disconnect(self.handle_community_change) + self_identity.inner_data_changed.connect(self.handle_account_identity_change) + except TypeError as e: + if "disconnect() failed" in str(e): + logging.debug("Could not disconnect community") + else: + raise + + account_connections = [] + for p in self_identity.unique_valid_certifiers_of(self.community): + account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community)) + certifiers_of = [p for p in account_connections] + for p in self_identity.unique_valid_certified_by(self.community): + account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community)) + certified_by = [p for p in account_connections + if p.pubkey not in [i.pubkey for i in certifiers_of]] + identities = certifiers_of + certified_by + self.refresh_identities(identities) + + def refresh_identities(self, identities): + """ Refresh the table with specified identities. If no identities is passed, use the account connections. - ''' - if persons is None: - self_identity = Person.lookup(self.account.pubkey, self.community) - account_connections = [] - certifiers_of = [] - certified_by = [] - for p in self_identity.unique_valid_certifiers_of(self.community): - account_connections.append(Person.lookup(p['pubkey'], self.community)) - certifiers_of = [p for p in account_connections] - logging.debug(persons) - for p in self_identity.unique_valid_certified_by(self.community): - account_connections.append(Person.lookup(p['pubkey'], self.community)) - certified_by = [p for p in account_connections - if p.pubkey not in [i.pubkey for i in certifiers_of]] - persons = certifiers_of + certified_by - - self.table_identities.model().sourceModel().refresh_identities(persons) + """ + self.table_identities.model().sourceModel().refresh_identities(identities) self.table_identities.resizeColumnsToContents() def refresh_quality_buttons(self): try: - if self.account.published_uid(self.community): + if self.account.identity(self.community).published_uid(self.community): logging.debug("UID Published") - if self.account.member_of(self.community): + if self.account.identity(self.community).is_member(self.community): self.button_membership.setText(self.tr("Renew membership")) self.button_membership.show() self.button_publish_uid.hide() @@ -349,19 +366,7 @@ Revoking your UID can only success if it is not already validated by the network self.button_leaving.hide() self.button_publish_uid.show() self.button_revoke_uid.hide() - except PersonNotFoundError: + except LookupFailureError: self.button_membership.hide() self.button_leaving.hide() self.button_publish_uid.show() - - @pyqtSlot(str) - def refresh_person(self, pubkey): - logging.debug("Refresh person {0}".format(pubkey)) - if self is None: - logging.error("community_tab self is None in refresh_person. Watcher connected to a destroyed tab") - else: - if pubkey == self.account.pubkey: - self.refresh_quality_buttons() - - index = self.table_identities.model().sourceModel().person_index(pubkey) - self.table_identities.model().sourceModel().dataChanged.emit(index[0], index[1]) diff --git a/src/cutecoin/gui/contact.py b/src/cutecoin/gui/contact.py index 2f50c58e3a74cd2746762d8561adc18f0d69553b..8e26de9708940be7b1212853d6e42f004d327564 100644 --- a/src/cutecoin/gui/contact.py +++ b/src/cutecoin/gui/contact.py @@ -1,27 +1,27 @@ -''' +""" Created on 2 févr. 2014 @author: inso -''' +""" import re import logging from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QMessageBox -from ..core.person import Person +from ..core.registry import IdentitiesRegistry from ..tools.exceptions import ContactAlreadyExists from ..gen_resources.contact_uic import Ui_ConfigureContactDialog class ConfigureContactDialog(QDialog, Ui_ConfigureContactDialog): - ''' + """ classdocs - ''' + """ def __init__(self, account, parent=None, contact=None, index_edit=None): - ''' + """ Constructor - ''' + """ super().__init__() self.setupUi(self) self.account = account diff --git a/src/cutecoin/gui/currency_tab.py b/src/cutecoin/gui/currency_tab.py index 65075002e172f422846c77fd412176d179eaba95..e59049623b92b4c42dc3f694b743ab4f3b215148 100644 --- a/src/cutecoin/gui/currency_tab.py +++ b/src/cutecoin/gui/currency_tab.py @@ -1,8 +1,8 @@ -''' +""" Created on 2 févr. 2014 @author: inso -''' +""" import time import logging @@ -17,22 +17,22 @@ from .transactions_tab import TransactionsTabWidget from .network_tab import NetworkTabWidget from .informations_tab import InformationsTabWidget from . import toast +import asyncio from ..tools.exceptions import MembershipNotFoundError -from ..core.person import Person +from ..core.registry import IdentitiesRegistry class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): - ''' + """ classdocs - ''' + """ def __init__(self, app, community, password_asker, status_label): - ''' + """ Constructor - ''' + """ super().__init__() - self.setupUi(self) self.app = app self.community = community self.password_asker = password_asker @@ -44,6 +44,9 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): 'warning_certifications': self.tr("Warning : Your could miss certifications soon.") } + + super().setupUi(self) + self.tab_community = CommunityTabWidget(self.app, self.app.current_account, self.community, @@ -55,66 +58,39 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): self.community, self.password_asker) - self.tab_network = NetworkTabWidget(self.community) + self.tab_history = TransactionsTabWidget(self.app, + self.community, + self.password_asker, + self) + + self.tab_informations = InformationsTabWidget(self.app, + self.community) + + self.tab_network = NetworkTabWidget(self.app, + self.community) + + self.tabs_account.addTab(self.tab_wallets, + QIcon(':/icons/wallet_icon'), + self.tr("Wallets")) + + self.tabs_account.addTab(self.tab_history, + QIcon(':/icons/tx_icon'), + self.tr("Transactions")) + + self.tabs_account.addTab(self.tab_informations, + QIcon(':/icons/informations_icon'), + self.tr("Informations")) + + self.tabs_account.addTab(self.tab_community, + QIcon(':/icons/community_icon'), + self.tr("Community")) + + self.tabs_account.addTab(self.tab_network, + QIcon(":/icons/network_icon"), + self.tr("Network")) self.community.network.new_block_mined.connect(self.refresh_block) self.community.network.nodes_changed.connect(self.refresh_status) - bc_watcher = self.app.monitor.blockchain_watcher(self.community) - bc_watcher.error.connect(self.display_error) - bc_watcher.watching_stopped.connect(self.refresh_data) - bc_watcher.new_transfers.connect(self.notify_transfers) - - def refresh(self): - if self.app.current_account is None: - self.tabs_account.setEnabled(False) - else: - self.tabs_account.setEnabled(True) - - self.tab_wallets = WalletsTabWidget(self.app, - self.app.current_account, - self.community, - self.password_asker) - self.tabs_account.addTab(self.tab_wallets, - QIcon(':/icons/wallet_icon'), - self.tr("Wallets")) - - self.tab_history = TransactionsTabWidget(self.app, - self.community, - self.password_asker, - self) - self.tabs_account.addTab(self.tab_history, - QIcon(':/icons/tx_icon'), - self.tr("Transactions")) - - self.tab_community = CommunityTabWidget(self.app, - self.app.current_account, - self.community, - self.password_asker, - self) - self.tabs_account.addTab(self.tab_community, - QIcon(':/icons/community_icon'), - self.tr("Community")) - - self.tab_informations = InformationsTabWidget(self.app.current_account, - self.community) - self.tabs_account.addTab(self.tab_informations, - QIcon(':/icons/informations_icon'), - self.tr("Informations")) - - # fix bug refresh_nodes launch on destroyed NetworkTabWidget - logging.debug('Disconnect community.network.nodes_changed') - try: - self.community.network.nodes_changed.disconnect() - except TypeError: - logging.debug('No signals on community.network.nodes_changed') - - self.tab_network = NetworkTabWidget(self.community) - self.tabs_account.addTab(self.tab_network, - QIcon(":/icons/network_icon"), - self.tr("Network")) - self.tab_informations.refresh() - self.refresh_status() - self.refresh_wallets() @pyqtSlot(str) def display_error(self, error): @@ -124,14 +100,14 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): @pyqtSlot(int) def refresh_block(self, block_number): - ''' + """ When a new block is found, start handling data. @param: block_number: The number of the block mined - ''' + """ logging.debug("Refresh block") self.status_info.clear() try: - person = Person.lookup(self.app.current_account.pubkey, self.community) + person = self.app.identities_registry.lookup(self.app.current_account.pubkey, self.community) expiration_time = person.membership_expiration_time(self.community) sig_validity = self.community.parameters['sigValidity'] warning_expiration_time = int(sig_validity / 3) @@ -142,48 +118,50 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): days = int(expiration_time / 3600 / 24) if days > 0: self.status_info.append('membership_expire_soon') - toast.display(self.tr("Membership expiration"), + + if self.app.preferences['notifications']: + toast.display(self.tr("Membership expiration"), self.tr("<b>Warning : Membership expiration in {0} days</b>").format(days)) + certifiers_of = person.unique_valid_certifiers_of(self.community) if len(certifiers_of) < self.community.parameters['sigQty']: self.status_info.append('warning_certifications') - toast.display(self.tr("Certifications number"), - self.tr("<b>Warning : You are certified by only {0} persons, need {1}</b>").format(len(certifiers_of), - self.community.parameters['sigQty'])) + if self.app.preferences['notifications']: + toast.display(self.tr("Certifications number"), + self.tr("<b>Warning : You are certified by only {0} persons, need {1}</b>") + .format(len(certifiers_of), + self.community.parameters['sigQty'])) except MembershipNotFoundError as e: pass self.tab_history.start_progress() - self.app.monitor.blockchain_watcher(self.community).thread().start() - self.app.monitor.persons_watcher(self.community).thread().start() - self.refresh_status() + self.refresh_data() + + def refresh_wallets(self): + if self.tab_wallets: + self.tab_wallets.refresh() - @pyqtSlot() def refresh_data(self): - ''' + """ Refresh data when the blockchain watcher finished handling datas - ''' + """ if self.tab_wallets: self.tab_wallets.refresh() - if self.tab_history.table_history.model(): - self.tab_history.table_history.model().sourceModel().refresh_transfers() - self.tab_history.refresh_balance() - self.tab_history.stop_progress() self.refresh_status() @pyqtSlot() def refresh_status(self): - ''' + """ Refresh status bar - ''' + """ logging.debug("Refresh status") text = self.tr(" Block {0}").format(self.community.network.latest_block) - if self.community.network_quality() > 0.66: + if self.community.network.quality > 0.66: icon = '<img src=":/icons/connected" width="12" height="12"/>' - elif self.community.network_quality() > 0.33: + elif self.community.network.quality > 0.33: icon = '<img src=":/icons/weak_connect" width="12" height="12"/>' else: icon = '<img src=":/icons/disconnected" width="12" height="12"/>' @@ -193,26 +171,8 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): label_text += " - {0}".format(status_infotext) self.status_label.setText(label_text) - @pyqtSlot(list) - def notify_transfers(self, transfers_list): - transfers_txt = "" - amount = 0 - currency = self.community.name - for t in transfers_list: - amount += t.metadata['amount'] - - logging.debug(transfers_txt) - text = self.tr("Received {0} {1} from {2} transfers").format(amount, - currency, - len(transfers_list)) - text += transfers_txt - toast.display(self.tr("New transactions received"), text) - - def refresh_wallets(self): - if self.app.current_account: - self.tab_wallets.refresh() - def showEvent(self, event): + asyncio.async(self.community.network.discover_network()) self.refresh_status() def referential_changed(self): diff --git a/src/cutecoin/gui/import_account.py b/src/cutecoin/gui/import_account.py index dae6192c02afc00e3fddef31ab2e000ebedf5be4..bd3c0a52eea9d20f6fe0e5edb6059bf58a8713de 100644 --- a/src/cutecoin/gui/import_account.py +++ b/src/cutecoin/gui/import_account.py @@ -1,8 +1,8 @@ -''' +""" Created on 22 mai 2014 @author: inso -''' +""" import re from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QMessageBox, QFileDialog @@ -12,14 +12,14 @@ from cutecoin.gen_resources.import_account_uic import Ui_ImportAccountDialog class ImportAccountDialog(QDialog, Ui_ImportAccountDialog): - ''' + """ classdocs - ''' + """ def __init__(self, app, parent=None): - ''' + """ Constructor - ''' + """ super().__init__() self.setupUi(self) self.app = app diff --git a/src/cutecoin/gui/informations_tab.py b/src/cutecoin/gui/informations_tab.py index d6e3661557752428b70c293d1d5909c00b9de576..163306776dbc6bea73e8677e7512fc0de85d2dad 100644 --- a/src/cutecoin/gui/informations_tab.py +++ b/src/cutecoin/gui/informations_tab.py @@ -16,17 +16,26 @@ class InformationsTabWidget(QWidget, Ui_InformationsTabWidget): classdocs """ - def __init__(self, account, community): + def __init__(self, app, community): """ - Constructor + Constructor of the InformationsTabWidget + + :param app: cutecoin.core.Application + :param community: cutecoin.core.Community + :return: """ super().__init__() - self.setupUi(self) + self.app = app self.community = community - self.account = account + self.community.inner_data_changed.connect(self.refresh) + self.setupUi(self) self.refresh() + @property + def account(self): + return self.app.current_account + def refresh(self): #  try to request money parameters try: @@ -37,38 +46,85 @@ class InformationsTabWidget(QWidget, Ui_InformationsTabWidget): #  try to request money variables from last ud block try: - block = self.community.get_ud_block() + block_ud = self.community.get_ud_block() except Exception as e: logging.debug('community get_ud_block error : ' + str(e)) return False try: - block_t_minus_1 = self.community.get_ud_block(1) + block_ud_minus_1 = self.community.get_ud_block(1) except Exception as e: logging.debug('community get_ud_block error : ' + str(e)) return False - if block: - ud = self.get_referential_diff_value(block['dividend']) + if block_ud: + ud = self.get_referential_diff_value(block_ud['dividend']) # if referential type is quantitative... if self.account.ref_type() == 'q': # display int values # use the float type of 64bits, to avoid display a 32bit signed integer... localized_ud = QLocale().toString(float(ud), 'f', 0) - localized_mass_per_member = QLocale().toString( - float(self.get_referential_diff_value(block['monetaryMass'] / block['membersCount'])), 'f', 0 + # display int values + localized_ud_plus_1 = QLocale().toString( + float( + self.get_referential_diff_value( + self.community.computed_dividend + ) + ), + 'f', + 0 ) - localized_monetary_mass = QLocale().toString( - float(self.get_referential_diff_value(block['monetaryMass'])), 'f', 0 + localized_mass = QLocale().toString( + float(self.get_referential_diff_value(block_ud['monetaryMass'])), 'f', 0 ) + if block_ud_minus_1: + localized_mass_minus_1_per_member = QLocale().toString( + float( + self.get_referential_diff_value(float(0) if block_ud['membersCount'] == 0 else + block_ud_minus_1['monetaryMass'] / block_ud['membersCount'] + ) + ), 'f', 0 + ) + localized_mass_minus_1 = QLocale().toString( + float(self.get_referential_diff_value(block_ud_minus_1['monetaryMass'])), 'f', 0 + ) + else: + localized_mass_minus_1_per_member = QLocale().toString( + float(0), 'f', 0 + ) + localized_mass_minus_1 = QLocale().toString( + float(0), 'f', 0 + ) else: # display float values - localized_ud = QLocale().toString(ud, 'f', 6) - localized_mass_per_member = QLocale().toString( - self.get_referential_diff_value(block['monetaryMass'] / block['membersCount']), 'f', 6 + localized_ud = QLocale().toString(ud, 'f', self.app.preferences['digits_after_comma']) + # display float values + localized_ud_plus_1 = QLocale().toString( + float( + self.get_referential_diff_value( + self.community.computed_dividend + ) + ), + 'f', + self.app.preferences['digits_after_comma'] ) - localized_monetary_mass = QLocale().toString( - self.get_referential_diff_value(block['monetaryMass']), 'f', 6 + localized_mass = QLocale().toString( + float(self.get_referential_diff_value(block_ud['monetaryMass'])), 'f', self.app.preferences['digits_after_comma'] ) + if block_ud_minus_1: + localized_mass_minus_1_per_member = QLocale().toString( + self.get_referential_diff_value(float(0) if block_ud['membersCount'] == 0 else + block_ud_minus_1['monetaryMass'] / block_ud['membersCount']), 'f', self.app.preferences['digits_after_comma'] + ) + localized_mass_minus_1 = QLocale().toString( + self.get_referential_diff_value(block_ud_minus_1['monetaryMass']), 'f', self.app.preferences['digits_after_comma'] + ) + else: + localized_mass_minus_1_per_member = QLocale().toString( + float(0), 'f', self.app.preferences['digits_after_comma'] + ) + localized_mass_minus_1 = QLocale().toString( + float(0), 'f', self.app.preferences['digits_after_comma'] + ) # set infos in label self.label_general.setText( @@ -80,25 +136,34 @@ class InformationsTabWidget(QWidget, Ui_InformationsTabWidget): <tr><td align="right"><b>{:}</b></td><td>{:} {:}</td></tr> <tr><td align="right"><b>{:2.2%} / {:} days</b></td><td>{:}</td></tr> <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> + <tr><td align="right"><b>{:}</b></td><td>{:}</td></tr> </table> """).format( localized_ud, self.tr('Universal Dividend UD(t) in'), self.get_referential_diff_name(), - localized_monetary_mass, - self.tr('Monetary Mass M(t) in'), + localized_mass_minus_1, + self.tr('Monetary Mass M(t-1) in'), self.get_referential_diff_name(), - block['membersCount'], + block_ud['membersCount'], self.tr('Members N(t)'), - localized_mass_per_member, - self.tr('Monetary Mass per member M(t)/N(t) in'), + localized_mass_minus_1_per_member, + self.tr('Monetary Mass per member M(t-1)/N(t) in'), self.get_referential_diff_name(), - block['dividend'] / (block_t_minus_1['monetaryMass'] / block_t_minus_1['membersCount']), + float(0) if block_ud_minus_1['membersCount'] == 0 else + block_ud['dividend'] / (block_ud_minus_1['monetaryMass'] / block_ud_minus_1['membersCount']), + params['dt'] / 86400, - self.tr('Actual growth c = UD(t)/[M(t-1)/N(t-1)]'), + self.tr('Actual growth c = UD(t)/[M(t-1)/N(t)]'), QLocale.toString( QLocale(), - QDateTime.fromTime_t(block['medianTime'] + params['dt']), + QDateTime.fromTime_t(block_ud['medianTime']), + QLocale.dateTimeFormat(QLocale(), QLocale.ShortFormat) + ), + self.tr('Last UD date and time (t)'), + QLocale.toString( + QLocale(), + QDateTime.fromTime_t(block_ud['medianTime'] + params['dt']), QLocale.dateTimeFormat(QLocale(), QLocale.ShortFormat) ), self.tr('Next UD date and time (t+1)') @@ -107,35 +172,7 @@ class InformationsTabWidget(QWidget, Ui_InformationsTabWidget): else: self.label_general.setText(self.tr('No Universal Dividend created yet.')) - if block: - # if referential type is quantitative... - if self.account.ref_type() == 'q': - # display int values - localized_ud_t1 = QLocale().toString( - float( - self.get_referential_diff_value( - math.ceil( - max(block['dividend'], params['c'] * block['monetaryMass'] / block['membersCount']) - ) - ) - ), - 'f', - 0 - ) - else: - # display float values - localized_ud_t1 = QLocale().toString( - float( - self.get_referential_diff_value( - math.ceil( - max(block['dividend'], params['c'] * block['monetaryMass'] / block['membersCount']) - ) - ) - ), - 'f', - 6 - ) - + if block_ud: # set infos in label self.label_rules.setText( self.tr(""" @@ -147,16 +184,16 @@ class InformationsTabWidget(QWidget, Ui_InformationsTabWidget): """).format( self.tr('{:2.0%} / {:} days').format(params['c'], params['dt'] / 86400), self.tr('Fundamental growth (c) / Delta time (dt)'), - self.tr('UD(t+1) = MAX { UD(t) ; c × M(t) / N(t) }'), + self.tr('UD(t+1) = MAX { UD(t) ; c × M(t) / N(t+1) }'), self.tr('Universal Dividend (formula)'), self.tr('{:} = MAX {{ {:} {:} ; {:2.0%} × {:} {:} / {:} }}').format( - localized_ud_t1, + localized_ud_plus_1, localized_ud, self.get_referential_diff_name(), params['c'], - localized_monetary_mass, + localized_mass, self.get_referential_diff_name(), - block['membersCount'] + block_ud['membersCount'] ), self.tr('Universal Dividend (computed)') ) diff --git a/src/cutecoin/gui/mainwindow.py b/src/cutecoin/gui/mainwindow.py index b44cafb29dfe137ed7877ba77c0302f1022c400b..d991775e06602eccfed4554371b5a9002887573f 100644 --- a/src/cutecoin/gui/mainwindow.py +++ b/src/cutecoin/gui/mainwindow.py @@ -1,8 +1,8 @@ -''' +""" Created on 1 févr. 2014 @author: inso -''' +""" from ..gen_resources.mainwindow_uic import Ui_MainWindow from ..gen_resources.about_uic import Ui_AboutPopup @@ -28,31 +28,10 @@ from . import toast import logging -class Loader(QObject): - def __init__(self, app): - super().__init__() - self.app = app - self.account_name = "" - - loaded = pyqtSignal() - connection_error = pyqtSignal(str) - - def set_account_name(self, name): - self.account_name = name - - @pyqtSlot() - def load(self): - if self.account_name != "": - account = self.app.get_account(self.account_name) - self.app.change_current_account(account) - - self.loaded.emit() - - class MainWindow(QMainWindow, Ui_MainWindow): - ''' + """ classdocs - ''' + """ def __init__(self, app): """ @@ -62,40 +41,28 @@ class MainWindow(QMainWindow, Ui_MainWindow): """ # Set up the user interface from Designer. super().__init__() - self.setupUi(self) - QApplication.setWindowIcon(QIcon(":/icons/cutecoin_logo")) self.app = app - logging.debug(app.thread()) - logging.debug(self.thread()) - self.password_asker = None self.initialized = False + self.password_asker = None + self.import_dialog = None - self.busybar = QProgressBar(self.statusbar) - self.busybar.setMinimum(0) - self.busybar.setMaximum(0) - self.busybar.setValue(-1) - self.statusbar.addWidget(self.busybar) - self.busybar.hide() - self.app.version_requested.connect(self.latest_version_requested) - self.app.get_last_version() + super().setupUi(self) - self.combo_referential = QComboBox(self) - self.combo_referential.setEnabled(False) - self.combo_referential.currentIndexChanged.connect(self.referential_changed) + QApplication.setWindowIcon(QIcon(":/icons/cutecoin_logo")) + + self.app.version_requested.connect(self.latest_version_requested) self.status_label = QLabel("", self) self.status_label.setTextFormat(Qt.RichText) + self.statusbar.addPermanentWidget(self.status_label, 1) self.label_time = QLabel("", self) - - self.statusbar.addPermanentWidget(self.status_label, 1) self.statusbar.addPermanentWidget(self.label_time) - self.statusbar.addPermanentWidget(self.combo_referential) - self.update_time() - self.loader = Loader(self.app) - self.loader.loaded.connect(self.loader_finished) - self.loader.connection_error.connect(self.display_error) + self.combo_referential = QComboBox(self) + self.combo_referential.setEnabled(False) + self.combo_referential.currentIndexChanged.connect(self.referential_changed) + self.statusbar.addPermanentWidget(self.combo_referential) self.homescreen = HomeScreenWidget(self.app) self.centralWidget().layout().addWidget(self.homescreen) @@ -104,17 +71,20 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.open_ucoin_info = lambda: QDesktopServices.openUrl(QUrl("http://ucoin.io/theoretical/")) self.homescreen.button_info.clicked.connect(self.open_ucoin_info) - self.import_dialog = None - self.export_dialog = None - - # TODO: There are too much refresh() calls on startup + def startup(self): + self.update_time() + self.app.get_last_version() + if self.app.preferences['maximized']: + self.showMaximized() + else: + self.show() self.refresh() def open_add_account_dialog(self): dialog = ProcessConfigureAccount(self.app, None) result = dialog.exec_() if result == QDialog.Accepted: - self.action_change_account(self.app.current_account.name) + self.action_change_account(self.app.current_account) @pyqtSlot(str) def display_error(self, error): @@ -155,28 +125,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.window().refresh_contacts() def action_change_account(self, account_name): - def loading_progressed(value, maximum): - logging.debug("Busybar : {:} : {:}".format(value, maximum)) - self.busybar.setValue(value) - self.busybar.setMaximum(maximum) - QApplication.processEvents() - - if self.app.current_account: - self.app.save_cache(self.app.current_account) - - self.app.current_account = None + self.app.change_current_account(self.app.get_account(account_name)) self.refresh() - QApplication.setOverrideCursor(Qt.BusyCursor) - self.app.loading_progressed.connect(loading_progressed) - self.busybar.setMinimum(0) - self.busybar.setMaximum(0) - self.busybar.setValue(-1) - self.busybar.show() - self.status_label.setText(self.tr("Loading account {0}").format(account_name)) - self.loader.set_account_name(account_name) - QTimer.singleShot(10, self.loader.load) - self.homescreen.button_new.hide() - self.homescreen.button_import.hide() @pyqtSlot() def loader_finished(self): @@ -189,7 +139,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): except: logging.debug("Disconnect of app failed") - self.app.monitor.start_network_watchers() QApplication.processEvents() def open_transfer_money_dialog(self): @@ -278,7 +227,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): .format(version=latest[1]) version_url = latest[2] - toast.display("Cutecoin", """<p>{version_info}</br> + if self.app.preferences['notifications']: + toast.display("Cutecoin", """<p>{version_info}</br> <a href={version_url}>Download link</a></p>""".format( version_info=version_info, version_url=version_url)) @@ -296,7 +246,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): tab_currency = CurrencyTabWidget(self.app, community, self.password_asker, self.status_label) - tab_currency.refresh() self.currencies_tabwidget.addTab(tab_currency, QIcon(":/icons/currency_icon"), community.name) @@ -325,11 +274,11 @@ class MainWindow(QMainWindow, Ui_MainWindow): delete_action.triggered.connect(self.delete_contact) def refresh(self): - ''' + """ Refresh main window When the selected account changes, all the widgets in the window have to be refreshed - ''' + """ logging.debug("Refresh started") self.refresh_accounts() @@ -379,16 +328,17 @@ class MainWindow(QMainWindow, Ui_MainWindow): def export_account(self): # Testable way of using a QFileDialog - self.export_dialog = QFileDialog(self) - self.export_dialog.setObjectName('ExportFileDialog') - self.export_dialog.setWindowTitle(self.tr("Export an account")) - self.export_dialog.setNameFilter(self.tr("All account files (*.acc)")) - self.export_dialog.setLabelText(QFileDialog.Accept, self.tr('Export')) - self.export_dialog.accepted.connect(self.export_account_accepted) - self.export_dialog.show() + export_dialog = QFileDialog(self) + export_dialog.setObjectName('ExportFileDialog') + export_dialog.setWindowTitle(self.tr("Export an account")) + export_dialog.setNameFilter(self.tr("All account files (*.acc)")) + export_dialog.setLabelText(QFileDialog.Accept, self.tr('Export')) + export_dialog.accepted.connect(self.export_account_accepted) + export_dialog.show() def export_account_accepted(self): - selected_file = self.export_dialog.selectedFiles() + export_dialog = self.sender() + selected_file = export_dialog.selectedFiles() if selected_file: if selected_file[0][-4:] == ".acc": path = selected_file[0] @@ -397,30 +347,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.app.export_account(path, self.app.current_account) def closeEvent(self, event): - if self.app.current_account: - self.app.save_cache(self.app.current_account) - self.app.save_persons() + self.app.stop() super().closeEvent(event) - def showEvent(self, event): - super().showEvent(event) - if not self.initialized: - # if default account in preferences... - if self.app.preferences['account'] != "": - logging.debug("Loading default account") - self.action_change_account(self.app.preferences['account']) - # no default account... - else: - # if at least one account exists, set it as default... - if len(self.app.accounts) > 0: - # capture names sorted alphabetically - names = list(self.app.accounts.keys()) - names.sort() - # set first name in list as default in preferences - self.app.preferences['account'] = names[0] - self.app.save_preferences(self.app.preferences) - # open it - logging.debug("No default account in preferences. Set %s as default account." % names[0]) - self.action_change_account(self.app.preferences['account']) - - self.initialized = True diff --git a/src/cutecoin/gui/member.py b/src/cutecoin/gui/member.py index 853195085ab25eefbe0d6a36e4e3ad6325d2feb9..1b89afa9adcde8b41583bcad016e1642314eaa6e 100644 --- a/src/cutecoin/gui/member.py +++ b/src/cutecoin/gui/member.py @@ -39,7 +39,7 @@ class MemberDialog(QDialog, Ui_DialogMember): # if selected member is not the account member... if person.pubkey != self.account.pubkey: # add path from selected member to account member - path = graph.get_shortest_path_between_members(person, self.account.get_person()) + path = graph.get_shortest_path_between_members(person, self.account.identity(self.community)) text = self.tr(""" <table cellpadding="5"> diff --git a/src/cutecoin/gui/network_tab.py b/src/cutecoin/gui/network_tab.py index bd97eda945c8d2e48e7f0d1c6fa2f923870b7237..73349efb34f90896c25bc1d5fe57977a0df3c19c 100644 --- a/src/cutecoin/gui/network_tab.py +++ b/src/cutecoin/gui/network_tab.py @@ -1,27 +1,38 @@ -''' +""" Created on 20 févr. 2015 @author: inso -''' +""" import logging -from PyQt5.QtGui import QCursor +import asyncio + +from PyQt5.QtGui import QCursor, QDesktopServices from PyQt5.QtWidgets import QWidget, QMenu, QAction -from PyQt5.QtCore import Qt, QModelIndex, pyqtSlot +from PyQt5.QtCore import Qt, QModelIndex, pyqtSlot, QUrl from ..models.network import NetworkTableModel, NetworkFilterProxyModel +from ..core.net.api import bma as qtbma from ..gen_resources.network_tab_uic import Ui_NetworkTabWidget class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): - ''' + """ classdocs - ''' + """ + + def __init__(self, app, community): + """ + Constructore of a network tab. - def __init__(self, community): - ''' - Constructor - ''' + :param cutecoin.core.Application app: The application + :param cutecoin.core.Community community: The community + :return: A new network tab. + :rtype: NetworkTabWidget + """ super().__init__() + self.app = app + self.community = community + self.setupUi(self) model = NetworkTableModel(community) proxy = NetworkFilterProxyModel(self) @@ -30,7 +41,6 @@ class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): self.table_network.sortByColumn(0, Qt.DescendingOrder) self.table_network.resizeColumnsToContents() community.network.nodes_changed.connect(self.refresh_nodes) - self.community = community @pyqtSlot() def refresh_nodes(self): @@ -38,28 +48,35 @@ class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): self.table_network.model().sourceModel().modelReset.emit() def node_context_menu(self, point): - index = self.table_network.indexAt(point) - model = self.table_network.model() - if index.row() < model.rowCount(QModelIndex()): - source_index = model.mapToSource(index) - is_root_col = model.sourceModel().columns_types.index('is_root') - is_root_index = model.sourceModel().index(source_index.row(), is_root_col) - is_root = model.sourceModel().data(is_root_index, Qt.DisplayRole) - - menu = QMenu() - if is_root: - unset_root = QAction(self.tr("Unset root node"), self) - unset_root.triggered.connect(self.unset_root_node) - unset_root.setData(self.community.network.root_node_index(source_index.row())) - if len(self.community.network.root_nodes) > 1: - menu.addAction(unset_root) - else: - set_root = QAction(self.tr("Set as root node"), self) - set_root.triggered.connect(self.set_root_node) - set_root.setData(self.community.network.nodes[source_index.row()]) - menu.addAction(set_root) - # Show the context menu. - menu.exec_(QCursor.pos()) + index = self.table_network.indexAt(point) + model = self.table_network.model() + if index.row() < model.rowCount(QModelIndex()): + source_index = model.mapToSource(index) + is_root_col = model.sourceModel().columns_types.index('is_root') + is_root_index = model.sourceModel().index(source_index.row(), is_root_col) + is_root = model.sourceModel().data(is_root_index, Qt.DisplayRole) + + menu = QMenu() + if is_root: + unset_root = QAction(self.tr("Unset root node"), self) + unset_root.triggered.connect(self.unset_root_node) + unset_root.setData(self.community.network.root_node_index(source_index.row())) + if len(self.community.network.root_nodes) > 1: + menu.addAction(unset_root) + else: + set_root = QAction(self.tr("Set as root node"), self) + set_root.triggered.connect(self.set_root_node) + set_root.setData(self.community.network.nodes[source_index.row()]) + menu.addAction(set_root) + + if self.app.preferences['expert_mode']: + open_in_browser = QAction(self.tr("Open in browser"), self) + open_in_browser.triggered.connect(self.open_node_in_browser) + open_in_browser.setData(self.community.network.nodes[source_index.row()]) + menu.addAction(open_in_browser) + + # Show the context menu. + menu.exec_(QCursor.pos()) @pyqtSlot() def set_root_node(self): @@ -71,5 +88,14 @@ class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): index = self.sender().data() self.community.network.remove_root_node(index) + @pyqtSlot() + def open_node_in_browser(self): + node = self.sender().data() + peering = qtbma.network.Peering(node.endpoint) + url = QUrl(peering.reverse_url("/peering")) + QDesktopServices.openUrl(url) + + def manual_nodes_refresh(self): + self.community.network.refresh_once() diff --git a/src/cutecoin/gui/password_asker.py b/src/cutecoin/gui/password_asker.py index 2009f8720f92613fed576275609968eddfba08b6..dcb65ddd1851dab1c0ae2cd21365f3a06a2979a8 100644 --- a/src/cutecoin/gui/password_asker.py +++ b/src/cutecoin/gui/password_asker.py @@ -1,8 +1,8 @@ -''' +""" Created on 24 dec. 2014 @author: inso -''' +""" import logging import re @@ -14,14 +14,14 @@ from ..gen_resources.password_asker_uic import Ui_PasswordAskerDialog class PasswordAskerDialog(QDialog, Ui_PasswordAskerDialog): - ''' + """ A dialog to get password. - ''' + """ def __init__(self, account): - ''' + """ Constructor - ''' + """ super().__init__() self.setupUi(self) self.account = account diff --git a/src/cutecoin/gui/preferences.py b/src/cutecoin/gui/preferences.py index 91f0026efef99365da97c6b115bc59a7aea559c1..902b5d624fdb16d45a6bc7dc6d7c4c4f6f08ff0f 100644 --- a/src/cutecoin/gui/preferences.py +++ b/src/cutecoin/gui/preferences.py @@ -1,28 +1,30 @@ -''' +""" Created on 11 mai 2015 @author: inso -''' +""" from PyQt5.QtCore import QCoreApplication from ..core.account import Account from . import toast from PyQt5.QtWidgets import QDialog +from PyQt5.QtGui import QIcon from ..gen_resources.preferences_uic import Ui_PreferencesDialog +import icons_rc class PreferencesDialog(QDialog, Ui_PreferencesDialog): - ''' + """ A dialog to get password. - ''' + """ def __init__(self, app): - ''' + """ Constructor - ''' + """ super().__init__() self.setupUi(self) self.app = app @@ -36,11 +38,24 @@ class PreferencesDialog(QDialog, Ui_PreferencesDialog): for lang in ('en_GB', 'fr_FR'): self.combo_language.addItem(lang) self.combo_language.setCurrentText(self.app.preferences['lang']) + self.checkbox_expertmode.setChecked(self.app.preferences['expert_mode']) + self.checkbox_maximize.setChecked(self.app.preferences['maximized']) + self.checkbox_notifications.setChecked(self.app.preferences['notifications']) + self.spinbox_digits_comma.setValue(self.app.preferences['digits_after_comma']) + self.spinbox_digits_comma.setMaximum(12) + self.spinbox_digits_comma.setMinimum(1) + self.button_app.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(0)) + self.button_display.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(1)) + self.button_network.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(2)) def accept(self): pref = {'account': self.combo_account.currentText(), 'lang': self.combo_language.currentText(), - 'ref': self.combo_referential.currentIndex()} + 'ref': self.combo_referential.currentIndex(), + 'expert_mode': self.checkbox_expertmode.isChecked(), + 'maximized': self.checkbox_maximize.isChecked(), + 'digits_after_comma': self.spinbox_digits_comma.value(), + 'notifications': self.checkbox_notifications.isChecked()} self.app.save_preferences(pref) toast.display(self.tr("Preferences"), self.tr("A restart is needed to apply your new preferences.")) diff --git a/src/cutecoin/gui/process_cfg_account.py b/src/cutecoin/gui/process_cfg_account.py index 50b1bc7271b5708a95131ee8a63d779084cf9212..5906db17f4bd003e747350dd19ddf6fcae45baef 100644 --- a/src/cutecoin/gui/process_cfg_account.py +++ b/src/cutecoin/gui/process_cfg_account.py @@ -1,8 +1,8 @@ -''' +""" Created on 6 mars 2014 @author: inso -''' +""" import logging import requests from ucoinpy.documents.peer import Peer @@ -24,9 +24,9 @@ class Step(): class StepPageInit(Step): - ''' + """ First step when adding a community - ''' + """ def __init__(self, config_dialog): super().__init__(config_dialog) @@ -59,9 +59,9 @@ class StepPageInit(Step): class StepPageKey(Step): - ''' + """ First step when adding a community - ''' + """ def __init__(self, config_dialog): super().__init__(config_dialog) @@ -107,9 +107,9 @@ class StepPageKey(Step): class StepPageCommunities(Step): - ''' + """ First step when adding a community - ''' + """ def __init__(self, config_dialog): super().__init__(config_dialog) @@ -140,14 +140,14 @@ class StepPageCommunities(Step): class ProcessConfigureAccount(QDialog, Ui_AccountConfigurationDialog): - ''' + """ classdocs - ''' + """ def __init__(self, app, account): - ''' + """ Constructor - ''' + """ # Set up the user interface from Designer. super().__init__() self.setupUi(self) @@ -174,7 +174,8 @@ class ProcessConfigureAccount(QDialog, Ui_AccountConfigurationDialog): def open_process_add_community(self): logging.debug("Opening configure community dialog") logging.debug(self.password_asker) - dialog = ProcessConfigureCommunity(self.account, None, + dialog = ProcessConfigureCommunity(self.app, + self.account, None, self.password_asker) dialog.accepted.connect(self.action_add_community) dialog.exec_() @@ -215,16 +216,7 @@ class ProcessConfigureAccount(QDialog, Ui_AccountConfigurationDialog): def open_process_edit_community(self, index): community = self.account.communities[index.row()] - try: - dialog = ProcessConfigureCommunity(self.account, community, self.password_asker) - except NoPeerAvailable as e: - QMessageBox.critical(self, self.tr("Error"), - str(e), QMessageBox.Ok) - return - except requests.exceptions.RequestException as e: - QMessageBox.critical(self, self.tr("Error"), - str(e), QMessageBox.Ok) - return + dialog = ProcessConfigureCommunity(self.app, self.account, community, self.password_asker) dialog.accepted.connect(self.action_edit_community) dialog.exec_() diff --git a/src/cutecoin/gui/process_cfg_community.py b/src/cutecoin/gui/process_cfg_community.py index 037334f8acfab09e1214f20504f962098842733b..993b6e57eeffc5d5871b76ec6ec35d02052840cb 100644 --- a/src/cutecoin/gui/process_cfg_community.py +++ b/src/cutecoin/gui/process_cfg_community.py @@ -1,25 +1,22 @@ -''' +""" Created on 8 mars 2014 @author: inso -''' +""" import logging -import requests +import asyncio -from ucoinpy.api import bma -from ucoinpy.api.bma import ConnectionHandler -from ucoinpy.documents.peer import Peer - -from PyQt5.QtWidgets import QDialog, QMenu, QMessageBox +from PyQt5.QtWidgets import QDialog, QMenu, QMessageBox, QApplication from PyQt5.QtGui import QCursor +from PyQt5.QtCore import pyqtSlot from ..gen_resources.community_cfg_uic import Ui_CommunityConfigurationDialog from ..models.peering import PeeringTreeModel -from ..core.community import Community -from ..core.person import Person -from ..core.net.node import Node -from ..tools.exceptions import PersonNotFoundError, NoPeerAvailable +from ..core import Community +from ..core.registry import Identity +from ..core.net import Node +from . import toast class Step(): @@ -30,43 +27,52 @@ class Step(): class StepPageInit(Step): - ''' + """ First step when adding a community - ''' + """ def __init__(self, config_dialog): super().__init__(config_dialog) self.node = None logging.debug("Init") + self.config_dialog.button_next.setEnabled(False) + self.config_dialog.button_checknode.clicked.connect(self.check_node) - def is_valid(self): + @asyncio.coroutine + def coroutine_check_node(self): server = self.config_dialog.lineedit_server.text() port = self.config_dialog.spinbox_port.value() logging.debug("Is valid ? ") - try: - self.node = Node.from_address(None, server, port) - except Exception as e: - QMessageBox.critical(self.config_dialog, ":(", - str(e), - QMessageBox.Ok) + self.node = yield from Node.from_address(self.config_dialog.app.network_manager, None, server, port) + if self.node: + self.config_dialog.button_next.setEnabled(True) + self.config_dialog.button_check_node.setText("Ok !") + else: + self.config_dialog.button_next.setEnabled(False) + self.config_dialog.button_check_node.setText("Could not connect.") - return True + @pyqtSlot() + def check_node(self): + asyncio.async(self.coroutine_check_node()) + + def is_valid(self): + return self.node is not None def process_next(self): - ''' + """ We create the community - ''' + """ account = self.config_dialog.account logging.debug("Account : {0}".format(account)) - self.config_dialog.community = Community.create(self.node) + self.config_dialog.community = Community.create(self.config_dialog.app.network_manager, self.node) def display_page(self): self.config_dialog.button_previous.setEnabled(False) class StepPageAddpeers(Step): - ''' + """ The step where the user add peers - ''' + """ def __init__(self, config_dialog): super().__init__(config_dialog) @@ -79,10 +85,7 @@ class StepPageAddpeers(Step): def display_page(self): # We add already known peers to the displayed list self.config_dialog.nodes = self.config_dialog.community.network.root_nodes - try: - tree_model = PeeringTreeModel(self.config_dialog.community) - except requests.exceptions.RequestException: - raise + tree_model = PeeringTreeModel(self.config_dialog.community) self.config_dialog.tree_peers.setModel(tree_model) self.config_dialog.button_previous.setEnabled(False) @@ -90,16 +93,22 @@ class StepPageAddpeers(Step): class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog): - ''' + """ Dialog to configure or add a community - ''' + """ - def __init__(self, account, community, password_asker, default_node=None): - ''' + def __init__(self, app, account, community, password_asker): + """ Constructor - ''' + + :param cutecoin.core.Application app: The application + :param cutecoin.core.Account account: The configured account + :param cutecoin.core.Community community: The configured community + :param cutecoin.gui.password_asker.Password_Asker password_asker: The password asker + """ super().__init__() self.setupUi(self) + self.app = app self.community = community self.account = account self.password_asker = password_asker @@ -124,21 +133,13 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog): def next(self): if self.step.next_step is not None: if self.step.is_valid(): - try: - self.step.process_next() - self.step = self.step.next_step - next_index = self.stacked_pages.currentIndex() + 1 - self.stacked_pages.setCurrentIndex(next_index) - self.step.display_page() - except NoPeerAvailable: - return - except requests.exceptions.RequestException as e: - QMessageBox.critical(self.config_dialog, ":(", - str(e), - QMessageBox.Ok) - return + self.step.process_next() + self.step = self.step.next_step + next_index = self.stacked_pages.currentIndex() + 1 + self.stacked_pages.setCurrentIndex(next_index) + self.step.display_page() else: - self.accept() + asyncio.async(self.final()) def previous(self): if self.step.previous_step is not None: @@ -147,25 +148,29 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog): self.stacked_pages.setCurrentIndex(previous_index) self.step.display_page() - def add_node(self): - ''' + @asyncio.coroutine + def start_add_node(self): + """ Add node slot - ''' + """ server = self.lineedit_add_address.text() port = self.spinbox_add_port.value() try: - node = Node.from_address(self.community.currency, server, port) + node = yield from Node.from_address(self.app.network_manager, self.community.currency, server, port) self.community.add_node(node) except Exception as e: QMessageBox.critical(self, self.tr("Error"), str(e)) self.tree_peers.setModel(PeeringTreeModel(self.community)) + def add_node(self): + asyncio.async(self.start_add_node()) + def remove_node(self): - ''' + """ Remove node slot - ''' + """ logging.debug("Remove node") index = self.sender().data() self.community.remove_node(index) @@ -191,10 +196,37 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog): action.setEnabled(False) menu.exec_(QCursor.pos()) - def accept(self): - try: - Person.lookup(self.account.pubkey, self.community, cached=False) - except PersonNotFoundError as e: + def selfcert_sent(self, pubkey, currency): + if self.app.preferences['notifications']: + toast.display(self.tr("UID Publishing"), + self.tr("Success publishing your UID").format(pubkey, currency)) + else: + QMessageBox.information(self, self.tr("UID Publishing"), + self.tr("Success publishing your UID").format(pubkey, currency)) + self.account.certification_broadcasted.disconnect() + self.account.broadcast_error.disconnect(self.handle_error) + QApplication.restoreOverrideCursor() + self.add_community_and_close() + + @pyqtSlot(int, str) + def handle_error(self, error_code, text): + if self.app.preferences['notifications']: + toast.display(self.tr("Error"), self.tr("{0} : {1}".format(error_code, text))) + else: + QMessageBox.critical(self, self.tr("Error"), self.tr("{0} : {1}".format(error_code, text))) + self.account.certification_broadcasted.disconnect() + self.account.broadcast_error.disconnect(self.handle_error) + QApplication.restoreOverrideCursor() + + def add_community_and_close(self): + if self.community not in self.account.communities: + self.account.add_community(self.community) + self.accept() + + @asyncio.coroutine + def final(self): + identity = yield from self.app.identities_registry.future_lookup(self.account.pubkey, self.community) + if identity.status == Identity.NOT_FOUND: reply = QMessageBox.question(self, self.tr("Pubkey not found"), self.tr("""The public key of your account wasn't found in the community. :\n {0}\n @@ -203,20 +235,10 @@ Would you like to publish the key ?""").format(self.account.pubkey)) password = self.password_asker.exec_() if self.password_asker.result() == QDialog.Rejected: return - try: - self.account.send_selfcert(password, self.community) - except ValueError as e: - QMessageBox.critical(self, self.tr("Pubkey publishing error"), - e.message) - except NoPeerAvailable as e: - QMessageBox.critical(self, self.tr("Network error"), - self.tr("Couldn't connect to network : {0}").format(e), - QMessageBox.Ok) - except Exception as e: - QMessageBox.critical(self, self.tr("Error"), - "{0}".format(e), - QMessageBox.Ok) - - if self.community not in self.account.communities: - self.account.add_community(self.community) - super().accept() + self.account.selfcert_broadcasted.connect(self.handle_broadcast) + self.account.broadcast_error.connect(self.handle_error) + asyncio.async(self.account.send_selfcert(password, self.community)) + else: + self.add_community_and_close() + else: + self.add_community_and_close() diff --git a/src/cutecoin/gui/toast.py b/src/cutecoin/gui/toast.py index 52fddab8a7e2cc0d868553a1bfe281c21e55095f..afd30d7776087dab85ee0b9420ffd8eba5ae2927 100644 --- a/src/cutecoin/gui/toast.py +++ b/src/cutecoin/gui/toast.py @@ -1,8 +1,8 @@ -''' +""" Created on 1 mai 2015 @author: inso -''' +""" import sys, time import logging from PyQt5.QtCore import Qt, QThread @@ -17,7 +17,6 @@ def display(title, msg): logging.debug("NOTIFY DISPLAY") if sys.platform == "linux": import notify2 - import dbus if not notify2.is_initted(): logging.debug("Initialising notify2") notify2.init("cutecoin") diff --git a/src/cutecoin/gui/transactions_tab.py b/src/cutecoin/gui/transactions_tab.py index 4d6b382e5dc3e09eb5d71e15a5e2474460f4a132..302c8fbc1f0a13b78391699d40cf4a4f457a9570 100644 --- a/src/cutecoin/gui/transactions_tab.py +++ b/src/cutecoin/gui/transactions_tab.py @@ -1,13 +1,14 @@ from PyQt5.QtWidgets import QWidget, QAbstractItemView, QHeaderView, QDialog, \ QMenu, QAction, QApplication, QMessageBox -from PyQt5.QtCore import Qt, QDateTime, QTime, QModelIndex, QLocale, QCoreApplication +from PyQt5.QtCore import Qt, QDateTime, QTime, QModelIndex, QLocale, QCoreApplication, pyqtSlot from PyQt5.QtGui import QCursor from ..gen_resources.transactions_tab_uic import Ui_transactionsTabWidget from ..models.txhistory import HistoryTableModel, TxFilterProxyModel from ..core.transfer import Transfer from ..core.wallet import Wallet -from ..core.person import Person +from ..core.registry import Identity from .transfer import TransferMoneyDialog +from . import toast import logging @@ -35,11 +36,13 @@ class TransactionsTabWidget(QWidget, Ui_transactionsTabWidget): self.password_asker = password_asker self.currency_tab = currency_tab self.progressbar.hide() + self.community.inner_data_changed.connect(self.refresh_minimum_maximum) self.refresh() - def refresh(self): + def refresh_minimum_maximum(self): + block = self.community.get_block(1) minimum_datetime = QDateTime() - minimum_datetime.setTime_t(self.community.get_block(1).mediantime) + minimum_datetime.setTime_t(block['medianTime']) minimum_datetime.setTime(QTime(0, 0)) self.date_from.setMinimumDateTime(minimum_datetime) @@ -51,10 +54,13 @@ class TransactionsTabWidget(QWidget, Ui_transactionsTabWidget): self.date_to.setDateTime(tomorrow_datetime) self.date_to.setMaximumDateTime(tomorrow_datetime) + def refresh(self): + #TODO: Use resetmodel instead of destroy/create + self.refresh_minimum_maximum() ts_from = self.date_from.dateTime().toTime_t() ts_to = self.date_to.dateTime().toTime_t() - model = HistoryTableModel(self.app.current_account, self.community) + model = HistoryTableModel(self.app, self.community) proxy = TxFilterProxyModel(ts_from, ts_to) proxy.setSourceModel(model) proxy.setDynamicSortFilter(True) @@ -72,14 +78,26 @@ class TransactionsTabWidget(QWidget, Ui_transactionsTabWidget): def progressing(value, maximum): self.progressbar.setValue(value) self.progressbar.setMaximum(maximum) - + self.app.current_account.loading_progressed.connect(progressing) + self.app.current_account.loading_finished.connect(self.stop_progress) + self.app.current_account.refresh_transactions(self.community) self.progressbar.show() - blockchain_watcher = self.app.monitor.blockchain_watcher(self.community) - blockchain_watcher.loading_progressed.connect(progressing) - def stop_progress(self): + @pyqtSlot(list) + def stop_progress(self, received_list): + amount = 0 + for r in received_list: + amount += r.metadata['amount'] self.progressbar.hide() - self.table_history.resizeColumnsToContents() + if len(received_list) > 0: + text = self.tr("Received {0} {1} from {2} transfers").format(amount, + self.community.currency, + len(received_list)) + if self.app.preferences['notifications']: + toast.display(self.tr("New transactions received"), text) + + self.table_history.model().sourceModel().refresh_transfers() + self.table_history.resizeColumnsToContents() def refresh_balance(self): # if referential is "units" @@ -94,8 +112,9 @@ class TransactionsTabWidget(QWidget, Ui_transactionsTabWidget): proxy = self.table_history.model() balance = proxy.deposits - proxy.payments - localized_deposits = QLocale().toString( - self.app.current_account.units_to_diff_ref(proxy.deposits, self.community)) + ref = self.app.current_account.units_to_diff_ref + deposits = ref(proxy.deposits, self.community) + localized_deposits = QLocale().toString(deposits) localized_payments = QLocale().toString( self.app.current_account.units_to_diff_ref(proxy.payments, self.community)) localized_balance = QLocale().toString( @@ -126,9 +145,9 @@ class TransactionsTabWidget(QWidget, Ui_transactionsTabWidget): state_data = model.sourceModel().data(state_index, Qt.DisplayRole) pubkey_col = model.sourceModel().columns_types.index('pubkey') - person_index = model.sourceModel().index(source_index.row(), + identity_index = model.sourceModel().index(source_index.row(), pubkey_col) - person = model.sourceModel().data(person_index, Qt.DisplayRole) + identity = model.sourceModel().data(identity_index, Qt.DisplayRole) transfer = model.sourceModel().transfers[source_index.row()] if state_data == Transfer.REFUSED or state_data == Transfer.TO_SEND: send_back = QAction(self.tr("Send again"), self) @@ -141,31 +160,31 @@ class TransactionsTabWidget(QWidget, Ui_transactionsTabWidget): cancel.setData(transfer) menu.addAction(cancel) else: - if isinstance(person, Person): + if isinstance(identity, Identity): informations = QAction(self.tr("Informations"), self) informations.triggered.connect(self.currency_tab.tab_community.menu_informations) - informations.setData(person) + informations.setData(identity) menu.addAction(informations) add_as_contact = QAction(self.tr("Add as contact"), self) add_as_contact.triggered.connect(self.currency_tab.tab_community.menu_add_as_contact) - add_as_contact.setData(person) + add_as_contact.setData(identity) menu.addAction(add_as_contact) send_money = QAction(self.tr("Send money"), self) send_money.triggered.connect(self.currency_tab.tab_community.menu_send_money) - send_money.setData(person) + send_money.setData(identity) menu.addAction(send_money) - if isinstance(person, Person): + if isinstance(identity, identity): view_wot = QAction(self.tr("View in Web of Trust"), self) view_wot.triggered.connect(self.currency_tab.tab_community.view_wot) - view_wot.setData(person) + view_wot.setData(identity) menu.addAction(view_wot) copy_pubkey = QAction(self.tr("Copy pubkey to clipboard"), self) copy_pubkey.triggered.connect(self.copy_pubkey_to_clipboard) - copy_pubkey.setData(person) + copy_pubkey.setData(identity) menu.addAction(copy_pubkey) # Show the context menu. @@ -176,7 +195,7 @@ class TransactionsTabWidget(QWidget, Ui_transactionsTabWidget): clipboard = QApplication.clipboard() if data.__class__ is Wallet: clipboard.setText(data.pubkey) - elif data.__class__ is Person: + elif data.__class__ is Identity: clipboard.setText(data.pubkey) elif data.__class__ is str: clipboard.setText(data) diff --git a/src/cutecoin/gui/transfer.py b/src/cutecoin/gui/transfer.py index 070b4e2607dff70530334950a9a315cb6e7159b9..d514c90022ec3e949144ddc37ed2f12d8f72ec20 100644 --- a/src/cutecoin/gui/transfer.py +++ b/src/cutecoin/gui/transfer.py @@ -1,27 +1,31 @@ -''' +""" Created on 2 févr. 2014 @author: inso -''' +""" from PyQt5.QtWidgets import QDialog, QMessageBox, QApplication -from PyQt5.QtCore import QRegExp, Qt, QLocale +from PyQt5.QtCore import QRegExp, Qt, QLocale, pyqtSlot from PyQt5.QtGui import QRegExpValidator -from ..tools.exceptions import NotEnoughMoneyError, NoPeerAvailable from ..gen_resources.transfer_uic import Ui_TransferMoneyDialog from . import toast +import asyncio class TransferMoneyDialog(QDialog, Ui_TransferMoneyDialog): - ''' + """ classdocs - ''' + """ - def __init__(self, sender, password_asker): - ''' + def __init__(self, app, sender, password_asker): + """ Constructor - ''' + :param cutecoin.core.Application app: The application + :param cutecoin.core.Account sender: The sender + :param cutecoin.gui.password_asker.Password_Asker password_asker: The password asker + :return: + """ super().__init__() self.setupUi(self) self.account = sender @@ -30,7 +34,7 @@ class TransferMoneyDialog(QDialog, Ui_TransferMoneyDialog): self.wallet = None self.community = self.account.communities[0] self.wallet = self.account.wallets[0] - self.dividend = self.community.dividend + self.dividend = self.community.dividend() regexp = QRegExp('^([ a-zA-Z0-9-_:/;*?\[\]\(\)\\\?!^+=@&~#{}|<>%.]{0,255})$') validator = QRegExpValidator(regexp) @@ -70,40 +74,36 @@ class TransferMoneyDialog(QDialog, Ui_TransferMoneyDialog): if self.password_asker.result() == QDialog.Rejected: return - try: - QApplication.setOverrideCursor(Qt.WaitCursor) - QApplication.processEvents() - self.wallet.send_money(self.account.salt, password, self.community, - recipient, amount, comment) - toast.display(self.tr("Money transfer"), - self.tr("Success transfering {0} {1} to {2}").format(amount, - self.community.currency, - recipient)) - except ValueError as e: - QMessageBox.critical(self, self.tr("Money transfer"), - self.tr("Something wrong happened : {0}").format(e), - QMessageBox.Ok) - return - except NotEnoughMoneyError as e: - QMessageBox.warning(self, self.tr("Money transfer"), - self.tr("""This transaction could not be sent on this block -Please try again later""")) - except NoPeerAvailable as e: - QMessageBox.critical(self, self.tr("Money transfer"), - self.tr("Couldn't connect to network : {0}").format(e), - QMessageBox.Ok) - return - except Exception as e: - QMessageBox.critical(self, self.tr("Error"), - "{0}".format(str(e)), - QMessageBox.Ok) - return - finally: - QApplication.restoreOverrideCursor() - QApplication.processEvents() - + QApplication.setOverrideCursor(Qt.WaitCursor) + QApplication.processEvents() + self.wallet.transfer_broadcasted.connect(self.money_sent) + self.wallet.broadcast_error.connect(self.handle_error) + asyncio.async(self.wallet.send_money(self.account.salt, password, self.community, + recipient, amount, comment)) + + @pyqtSlot(str) + def money_sent(self, receiver_uid): + if self.app.preferences['notifications']: + toast.display(self.tr("Transfer"), + self.tr("Success sending money to {0}").format(receiver_uid)) + else: + QMessageBox.information(self, self.tr("Transfer"), + self.tr("Success sending money to {0}").format(receiver_uid)) + self.wallet.transfer_broadcasted.disconnect() + self.wallet.broadcast_error.disconnect(self.handle_error) + QApplication.restoreOverrideCursor() super().accept() + @pyqtSlot(int, str) + def handle_error(self, error_code, text): + if self.app.preferences['notifications']: + toast.display(self.tr("Error"), self.tr("{0} : {1}".format(error_code, text))) + else: + QMessageBox.critical(self, self.tr("Error"), self.tr("{0} : {1}".format(error_code, text))) + self.wallet.transfer_broadcasted.disconnect() + self.wallet.broadcast_error.disconnect(self.handle_error) + QApplication.restoreOverrideCursor() + def amount_changed(self): amount = self.spinbox_amount.value() relative = amount / self.dividend @@ -120,7 +120,7 @@ Please try again later""")) def change_current_community(self, index): self.community = self.account.communities[index] - self.dividend = self.community.dividend + self.dividend = self.community.dividend() amount = self.wallet.value(self.community) ref_amount = self.account.units_to_ref(amount, self.community) ref_name = self.account.ref_name(self.community.currency) diff --git a/src/cutecoin/gui/wallets_tab.py b/src/cutecoin/gui/wallets_tab.py index 22cdc57472ee852dcde3b2f9bbac79f1f48ca34f..b9136e9e9be7fed427d39cd2180196c3ecdab34c 100644 --- a/src/cutecoin/gui/wallets_tab.py +++ b/src/cutecoin/gui/wallets_tab.py @@ -1,14 +1,14 @@ -''' +""" Created on 15 févr. 2015 @author: inso -''' +""" import logging from PyQt5.QtWidgets import QWidget, QMenu, QAction, QApplication, QDialog from PyQt5.QtCore import QDateTime, QModelIndex, Qt, QLocale from PyQt5.QtGui import QCursor -from ..core.person import Person +from ..core.registry import Identity from ..core.wallet import Wallet from ..gui.password_asker import PasswordAskerDialog from ..models.wallets import WalletsTableModel, WalletsFilterProxyModel @@ -18,9 +18,9 @@ from ..gen_resources.wallets_tab_uic import Ui_WalletsTab class WalletsTabWidget(QWidget, Ui_WalletsTab): - ''' + """ classdocs - ''' + """ def __init__(self, app, account, community, password_asker): """ @@ -36,25 +36,39 @@ class WalletsTabWidget(QWidget, Ui_WalletsTab): self.account = account self.community = community self.password_asker = password_asker + self.setup_connections() - self.refresh() + def setup_connections(self): + self.account.inner_data_changed.connect(self.refresh_informations_frame) + self.community.inner_data_changed.connect(self.refresh_informations_frame) def refresh(self): + self.refresh_informations_frame() + self.refresh_wallets() + + def refresh_wallets(self): + #TODO: Using reset model instead of destroy/create + wallets_model = WalletsTableModel(self.app, self.community) + proxy_model = WalletsFilterProxyModel() + proxy_model.setSourceModel(wallets_model) + wallets_model.dataChanged.connect(self.wallet_changed) + self.table_wallets.setModel(proxy_model) + self.table_wallets.resizeColumnsToContents() + + def refresh_informations_frame(self): parameters = self.community.parameters - last_renewal = "" - expiration = "" try: - person = Person.lookup(self.account.pubkey, self.community) - membership = person.membership(self.community) + identity = self.account.identity(self.community) + membership = identity.membership(self.community) renew_block = membership['blockNumber'] - last_renewal = self.community.get_block(renew_block).mediantime + last_renewal = self.community.get_block(renew_block)['medianTime'] expiration = last_renewal + parameters['sigValidity'] except MembershipNotFoundError: last_renewal = None expiration = None - certified = person.unique_valid_certified_by(self.community) - certifiers = person.unique_valid_certifiers_of(self.community) + certified = identity.unique_valid_certified_by(self.community) + certifiers = identity.unique_valid_certifiers_of(self.community) if last_renewal and expiration: date_renewal = QLocale.toString( QLocale(), @@ -109,9 +123,12 @@ class WalletsTabWidget(QWidget, Ui_WalletsTab): localized_maximum = QLocale().toString(float(self.get_referential_value(maximum)), 'f', 0) else: # display float values - localized_amount = QLocale().toString(float(self.get_referential_value(amount)), 'f', 6) - localized_minimum = QLocale().toString(float(self.get_referential_value(0)), 'f', 6) - localized_maximum = QLocale().toString(float(self.get_referential_value(maximum)), 'f', 6) + localized_amount = QLocale().toString(float(self.get_referential_value(amount)), 'f', + self.app.preferences['digits_after_comma']) + localized_minimum = QLocale().toString(float(self.get_referential_value(0)), 'f', + self.app.preferences['digits_after_comma']) + localized_maximum = QLocale().toString(float(self.get_referential_value(maximum)), 'f', + self.app.preferences['digits_after_comma']) # set infos in label self.label_balance.setText( @@ -135,13 +152,6 @@ class WalletsTabWidget(QWidget, Ui_WalletsTab): ) ) - wallets_model = WalletsTableModel(self.account, self.community) - proxy_model = WalletsFilterProxyModel() - proxy_model.setSourceModel(wallets_model) - wallets_model.dataChanged.connect(self.wallet_changed) - self.table_wallets.setModel(proxy_model) - self.table_wallets.resizeColumnsToContents() - def get_referential_value(self, value): return self.account.units_to_ref(value, self.community) @@ -231,7 +241,7 @@ class WalletsTabWidget(QWidget, Ui_WalletsTab): clipboard = QApplication.clipboard() if data.__class__ is Wallet: clipboard.setText(data.pubkey) - elif data.__class__ is Person: + elif data.__class__ is Identity: clipboard.setText(data.pubkey) elif data.__class__ is str: clipboard.setText(data) diff --git a/src/cutecoin/gui/wot_tab.py b/src/cutecoin/gui/wot_tab.py index 4a9d5ddaa41d5b8c5b47bfa75abce725aead8ac3..7fe44de8f0a4c5220cb0e2b37f7d219e0c80c7e8 100644 --- a/src/cutecoin/gui/wot_tab.py +++ b/src/cutecoin/gui/wot_tab.py @@ -6,8 +6,6 @@ from PyQt5.QtWidgets import QWidget, QComboBox from PyQt5.QtCore import pyqtSlot from ..gen_resources.wot_tab_uic import Ui_WotTabWidget from cutecoin.gui.views.wot import NODE_STATUS_HIGHLIGHTED, NODE_STATUS_SELECTED, NODE_STATUS_OUT, ARC_STATUS_STRONG, ARC_STATUS_WEAK -from ucoinpy.api import bma -from cutecoin.core.person import Person class WotTabWidget(QWidget, Ui_WotTabWidget): @@ -32,64 +30,79 @@ class WotTabWidget(QWidget, Ui_WotTabWidget): self.comboBoxSearch.setInsertPolicy(QComboBox.NoInsert) # add scene events - self.graphicsView.scene().node_clicked.connect(self.draw_graph) + self.graphicsView.scene().node_clicked.connect(self.handle_node_click) self.graphicsView.scene().node_signed.connect(self.sign_node) self.graphicsView.scene().node_transaction.connect(self.send_money_to_node) self.graphicsView.scene().node_contact.connect(self.add_node_as_contact) self.graphicsView.scene().node_member.connect(self.identity_informations) - app.monitor.persons_watcher(community).person_changed.connect(self.handle_person_change) self.account = account self.community = community self.password_asker = password_asker + self.app = app # nodes list for menu from search self.nodes = list() # create node metadata from account - self._current_metadata = {'text': self.account.name, 'id': self.account.pubkey} - self.refresh() + self._current_identity = None + self.draw_graph(self.account.identity(self.community)) + + @pyqtSlot(dict) + def handle_node_click(self, metadata): + self.draw_graph(self.app.identities_registry.from_metadata(metadata)) - def draw_graph(self, metadata): + def draw_graph(self, identity): """ Draw community graph centered on the identity - :param dict metadata: Graph node metadata of the identity + :param cutecoin.core.registry.Identity identity: Graph node identity """ - logging.debug("Draw graph - " + metadata['text']) - self._current_metadata = metadata + logging.debug("Draw graph - " + identity.uid) - # create Person from node metadata - person = Person.from_metadata(metadata) - person_account = Person.from_metadata({'text': self.account.name, - 'id': self.account.pubkey}) - certifier_list = person.certifiers_of(self.community) - certified_list = person.certified_by(self.community) + identity_account = self.account.identity(self.community) + + #Disconnect old identity + try: + if self._current_identity and self._current_identity != identity: + self._current_identity.inner_data_changed.disconnect(self.handle_identity_change) + except TypeError as e: + if "disconnect()" in str(e): + logging.debug("Disconnect of old identity failed.") + + #Connect new identity + if self._current_identity != identity: + self._current_identity = identity + identity.inner_data_changed.connect(self.handle_identity_change) + + # create Identity from node metadata + certifier_list = identity.certifiers_of(self.community) + certified_list = identity.certified_by(self.community) # create empty graph instance graph = Graph(self.community) # add wallet node node_status = 0 - if person.pubkey == person_account.pubkey: + if identity == identity_account: node_status += NODE_STATUS_HIGHLIGHTED - if person.is_member(self.community) is False: + if identity.is_member(self.community) is False: node_status += NODE_STATUS_OUT node_status += NODE_STATUS_SELECTED - graph.add_person(person, node_status) + graph.add_identity(identity, node_status) # populate graph with certifiers-of - graph.add_certifier_list(certifier_list, person, person_account) + graph.add_certifier_list(certifier_list, identity, identity_account) # populate graph with certified-by - graph.add_certified_list(certified_list, person, person_account) + graph.add_certified_list(certified_list, identity, identity_account) # draw graph in qt scene self.graphicsView.scene().update_wot(graph.get()) # if selected member is not the account member... - if person.pubkey != person_account.pubkey: + if identity.pubkey != identity_account.pubkey: # add path from selected member to account member - path = graph.get_shortest_path_between_members(person, person_account) + path = graph.get_shortest_path_between_members(identity, identity_account) if path: self.graphicsView.scene().update_path(path) @@ -97,21 +110,19 @@ class WotTabWidget(QWidget, Ui_WotTabWidget): """ Reset graph scene to wallet identity """ - metadata = {'text': self.account.name, 'id': self.account.pubkey} self.draw_graph( - metadata + self.account.identity(self.community) ) def refresh(self): """ Refresh graph scene to current metadata """ - self.draw_graph(self._current_metadata) + self.draw_graph(self._current_identity) - @pyqtSlot(str) - def handle_person_change(self, pubkey): - if pubkey == self._current_metadata['id']: - self.refresh() + @pyqtSlot() + def handle_identity_change(self): + self.refresh() def search(self): """ @@ -122,7 +133,7 @@ class WotTabWidget(QWidget, Ui_WotTabWidget): if len(text) < 2: return False try: - response = self.community.request(bma.wot.Lookup, {'search': text}) + response = self.community.simple_request(bma.wot.Lookup, {'search': text}) except Exception as e: logging.debug('bma.wot.Lookup request error : ' + str(e)) return False @@ -149,20 +160,20 @@ class WotTabWidget(QWidget, Ui_WotTabWidget): node = self.nodes[index] metadata = {'id': node['pubkey'], 'text': node['uid']} self.draw_graph( - metadata + self.app.identities_registry.from_metadata(metadata) ) def identity_informations(self, metadata): - person = Person.from_metadata(metadata) - self.parent.identity_informations(person) + identity = self.app.identities_registry.from_metadata(metadata) + self.parent.identity_informations(identity) def sign_node(self, metadata): - person = Person.from_metadata(metadata) - self.parent.certify_identity(person) + identity = self.app.identities_registry.from_metadata(metadata) + self.parent.certify_identity(identity) def send_money_to_node(self, metadata): - person = Person.from_metadata(metadata) - self.parent.send_money_to_identity(person) + identity = self.app.identities_registry.from_metadata(metadata) + self.parent.send_money_to_identity(identity) def add_node_as_contact(self, metadata): # check if contact already exists... diff --git a/src/cutecoin/main.py b/src/cutecoin/main.py index f1a102a13d3e8e80f790954efe251c7a70f4dfda..3f17978b75be8d0e19ffc97fa8e205386abdb974 100755 --- a/src/cutecoin/main.py +++ b/src/cutecoin/main.py @@ -5,9 +5,10 @@ Created on 1 févr. 2014 """ import signal import sys -import os import logging +import asyncio +from quamash import QEventLoop from PyQt5.QtWidgets import QApplication from cutecoin.gui.mainwindow import MainWindow from cutecoin.core.app import Application @@ -17,8 +18,13 @@ if __name__ == '__main__': signal.signal(signal.SIGINT, signal.SIG_DFL) cutecoin = QApplication(sys.argv) - app = Application(sys.argv, cutecoin) - window = MainWindow(app) - window.showMaximized() - sys.exit(cutecoin.exec_()) - pass + loop = QEventLoop(cutecoin) + asyncio.set_event_loop(loop) + print("Debug enabled : {0}".format(loop.get_debug())) + + with loop: + app = Application.startup(sys.argv, cutecoin, loop) + window = MainWindow(app) + window.startup() + loop.run_forever() + sys.exit() diff --git a/src/cutecoin/models/communities.py b/src/cutecoin/models/communities.py index 2e1479b5de3da5939c4829066f25f8c7ff4ad911..d782ad461a4bcf380af6bbb6dbff24a3f624089d 100644 --- a/src/cutecoin/models/communities.py +++ b/src/cutecoin/models/communities.py @@ -1,22 +1,22 @@ -''' +""" Created on 5 févr. 2014 @author: inso -''' +""" from PyQt5.QtCore import QAbstractListModel, Qt class CommunitiesListModel(QAbstractListModel): - ''' + """ A Qt abstract item model to display communities in a tree - ''' + """ def __init__(self, account, parent=None): - ''' + """ Constructor - ''' + """ super(CommunitiesListModel, self).__init__(parent) self.communities = account.communities diff --git a/src/cutecoin/models/community.py b/src/cutecoin/models/community.py index 61a239b902c28a67a99101cff5eefdeeb3914094..16babb17f50b415a760af1d68a148f18bcd8ab4d 100644 --- a/src/cutecoin/models/community.py +++ b/src/cutecoin/models/community.py @@ -1,8 +1,8 @@ -''' +""" Created on 5 fevr. 2014 @author: inso -''' +""" class CommunityItemModel(object): diff --git a/src/cutecoin/models/identities.py b/src/cutecoin/models/identities.py index aa0692ea5fd66c8064106cb576b8e52747be4880..7e32c77ce057d733d79efe9859aaf0daabd3742f 100644 --- a/src/cutecoin/models/identities.py +++ b/src/cutecoin/models/identities.py @@ -1,11 +1,10 @@ -''' +""" Created on 5 févr. 2014 @author: inso -''' +""" -from ucoinpy.api import bma -from ..core.person import Person +from ..core.net.api import bma as qtbma from ..tools.exceptions import NoPeerAvailable, MembershipNotFoundError from PyQt5.QtCore import QAbstractTableModel, QSortFilterProxyModel, Qt, \ QDateTime, QModelIndex, QLocale @@ -70,14 +69,14 @@ class IdentitiesFilterProxyModel(QSortFilterProxyModel): class IdentitiesTableModel(QAbstractTableModel): - ''' + """ A Qt abstract item model to display communities in a tree - ''' + """ def __init__(self, community, parent=None): - ''' + """ Constructor - ''' + """ super().__init__(parent) self.community = community self.columns_titles = { @@ -87,38 +86,76 @@ class IdentitiesTableModel(QAbstractTableModel): 'expiration': self.tr('Expiration')} self.columns_ids = ('uid', 'pubkey', 'renewed', 'expiration') self.identities_data = [] + self._identities = [] + self._refresh_slots = [] @property def pubkeys(self): - ''' + """ Get pubkeys of displayed identities - ''' + """ return [i[1] for i in self.identities_data] - def identity_data(self, person): - parameters = self.community.parameters + def identity_data(self, identity): try: - join_block = person.membership(self.community)['blockNumber'] - try: - join_date = self.community.get_block(join_block).mediantime - expiration_date = join_date + parameters['sigValidity'] - except NoPeerAvailable: - join_date = None - expiration_date = None + join_date = identity.get_join_date(self.community) + expiration_date = identity.get_expiration_date(self.community) except MembershipNotFoundError: join_date = None expiration_date = None - return (person.uid, person.pubkey, join_date, expiration_date) + return (identity.uid, identity.pubkey, join_date, expiration_date) + + def refresh_identities(self, identities): + """ + Change the identities to display + + :param cutecoin.core.registry.IdentitiesRegistry identities: The new identities to display + """ + logging.debug("Refresh {0} identities".format(len(identities))) + + # We disconnect identities from their local slots + for (index, identity) in enumerate(self._identities): + identity.inner_data_changed.disconnect(self._refresh_slots[index]) - def refresh_identities(self, persons): - logging.debug("Refresh {0} identities".format(len(persons))) self.identities_data = [] + self._identities = [] + self._refresh_slots = [] self.beginResetModel() - for person in persons: - self.identities_data.append(self.identity_data(person)) + for identity in identities: + logging.debug(identity) + + # Connection + refresh_slot = lambda req, identity=identity: self.refresh_identity(req, identity) + identity.inner_data_changed.connect(refresh_slot) + + self._identities.append(identity) + self._refresh_slots.append(refresh_slot) + self.identities_data.append(self.identity_data(identity)) self.endResetModel() + def refresh_identity(self, request, identity): + """ + Refresh an identity when its inner_data changed + :param cutecoin.core.registry.Identity identity: The refreshed identity + """ + logging.debug("Refresh {0} because of {1}".format(identity, request)) + try: + index = self._identities.index(identity) + self.identities_data[index] = self.identity_data(identity) + if request == str(qtbma.wot.Lookup): + model_index_0 = self.createIndex(index, self.columns_ids.index('uid')) + model_index_max = self.createIndex(index, self.columns_ids.index('pubkey')) + self.dataChanged.emit(model_index_0, model_index_max) + elif request in (str(qtbma.blockchain.Membership), + str(qtbma.blockchain.Block), + str(qtbma.blockchain.Parameters)): + model_index_0 = self.createIndex(index, self.columns_ids.index('renewed')) + model_index_max = self.createIndex(index, self.columns_ids.index('expiration')) + self.dataChanged.emit(model_index_0, model_index_max) + except ValueError: + logging.debug("Identity {0} is not in list : {1}".format(identity, self.identities_data)) + def rowCount(self, parent): return len(self.identities_data) @@ -136,7 +173,7 @@ class IdentitiesTableModel(QAbstractTableModel): col = index.column() return self.identities_data[row][col] - def person_index(self, pubkey): + def identity_index(self, pubkey): try: row = self.pubkeys.index(pubkey) index_start = self.index(row, 0) diff --git a/src/cutecoin/models/network.py b/src/cutecoin/models/network.py index 4c43f07266bb30461b23ccdb5385b7289d478b5e..6056a9feeb6d16b072d8d05e9e25f6cc2aaaefc5 100644 --- a/src/cutecoin/models/network.py +++ b/src/cutecoin/models/network.py @@ -1,15 +1,17 @@ -''' +""" Created on 5 févr. 2014 @author: inso -''' +""" import logging -from ..tools.exceptions import NoPeerAvailable -from ..core.net.node import Node + from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant, QSortFilterProxyModel from PyQt5.QtGui import QColor, QFont +from ..tools.exceptions import NoPeerAvailable +from cutecoin.core.net.node import Node + class NetworkFilterProxyModel(QSortFilterProxyModel): def __init__(self, parent=None): @@ -81,14 +83,14 @@ class NetworkFilterProxyModel(QSortFilterProxyModel): class NetworkTableModel(QAbstractTableModel): - ''' + """ A Qt abstract item model to display - ''' + """ def __init__(self, community, parent=None): - ''' + """ Constructor - ''' + """ super().__init__(parent) self.community = community self.columns_types = ( diff --git a/src/cutecoin/models/peering.py b/src/cutecoin/models/peering.py index 313c3c581210e52e426e33ede66d582b0629561a..4b2e48c2b4a5a4c2f2041d5eb9836a81751d6b56 100644 --- a/src/cutecoin/models/peering.py +++ b/src/cutecoin/models/peering.py @@ -1,13 +1,10 @@ -''' +""" Created on 5 févr. 2014 @author: inso -''' +""" -from ucoinpy.api import bma -from ucoinpy.documents.peer import BMAEndpoint, Peer from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt -from requests.exceptions import Timeout import logging @@ -87,14 +84,14 @@ class NodeItem(object): class PeeringTreeModel(QAbstractItemModel): - ''' + """ A Qt abstract item model to display nodes of a community - ''' + """ def __init__(self, community): - ''' + """ Constructor - ''' + """ super().__init__(None) self.nodes = community._network.root_nodes self.root_item = RootItem(community.currency) diff --git a/src/cutecoin/models/txhistory.py b/src/cutecoin/models/txhistory.py index bebd9e13ee13dab3e2f0df2f77f69e21fbcd7357..a23f9decd123b4c87abaedceda369ca13ec89e43 100644 --- a/src/cutecoin/models/txhistory.py +++ b/src/cutecoin/models/txhistory.py @@ -1,8 +1,8 @@ -''' +""" Created on 5 févr. 2014 @author: inso -''' +""" import datetime import logging @@ -17,12 +17,16 @@ class TxFilterProxyModel(QSortFilterProxyModel): def __init__(self, ts_from, ts_to, parent=None): super().__init__(parent) self.community = None - self.account = None + self.app = None self.ts_from = ts_from self.ts_to = ts_to self.payments = 0 self.deposits = 0 + @property + def account(self): + return self.app.current_account + def set_period(self, ts_from, ts_to): """ Filter table by given timestamps @@ -66,7 +70,7 @@ class TxFilterProxyModel(QSortFilterProxyModel): def setSourceModel(self, sourceModel): self.community = sourceModel.community - self.account = sourceModel.account + self.app = sourceModel.app super().setSourceModel(sourceModel) def lessThan(self, left, right): @@ -115,7 +119,7 @@ class TxFilterProxyModel(QSortFilterProxyModel): return QLocale().toString(float(amount_ref), 'f', 0) else: # display float values - return QLocale().toString(amount_ref, 'f', 6) + return QLocale().toString(amount_ref, 'f', self.app.preferences['digits_after_comma']) if role == Qt.FontRole: font = QFont() @@ -150,16 +154,16 @@ class TxFilterProxyModel(QSortFilterProxyModel): class HistoryTableModel(QAbstractTableModel): - ''' + """ A Qt abstract item model to display communities in a tree - ''' + """ - def __init__(self, account, community, parent=None): - ''' + def __init__(self, app, community, parent=None): + """ Constructor - ''' + """ super().__init__(parent) - self.account = account + self.app = app self.community = community self.account.referential self.transfers_data = [] @@ -187,15 +191,19 @@ class HistoryTableModel(QAbstractTableModel): 'Pubkey' ) + @property + def account(self): + return self.app.current_account + @property def transfers(self): - return self.account.transfers(self.community) + return self.account.transfers(self.community) + self.account.dividends(self.community) def data_received(self, transfer): amount = transfer.metadata['amount'] comment = "" - if transfer.txdoc: - comment = transfer.txdoc.comment + if transfer.metadata['comment'] != "": + comment = transfer.metadata['comment'] if transfer.metadata['issuer_uid'] != "": sender = transfer.metadata['issuer_uid'] else: @@ -211,14 +219,13 @@ class HistoryTableModel(QAbstractTableModel): def data_sent(self, transfer): amount = transfer.metadata['amount'] comment = "" - if transfer.txdoc: - comment = transfer.txdoc.comment + if transfer.metadata['comment'] != "": + comment = transfer.metadata['comment'] if transfer.metadata['receiver_uid'] != "": receiver = transfer.metadata['receiver_uid'] else: receiver = "pub:{0}".format(transfer.metadata['receiver'][:5]) - date_ts = transfer.metadata['time'] txid = transfer.metadata['txid'] @@ -226,18 +233,30 @@ class HistoryTableModel(QAbstractTableModel): "", comment, transfer.state, txid, transfer.metadata['receiver']) + def data_dividend(self, dividend): + amount = dividend['amount'] + comment = "" + receiver = self.account.name + date_ts = dividend['time'] + id = dividend['id'] + return (date_ts, receiver, amount, + "", "", Transfer.VALIDATED, id, + self.account.pubkey) + def refresh_transfers(self): self.beginResetModel() self.transfers_data = [] for transfer in self.transfers: if type(transfer) is Received: self.transfers_data.append(self.data_received(transfer)) - else: + elif type(transfer) is Transfer: self.transfers_data.append(self.data_sent(transfer)) + elif type(transfer) is dict: + self.transfers_data.append(self.data_dividend(transfer)) self.endResetModel() def rowCount(self, parent): - return len(self.transfers) + return len(self.transfers_data) def columnCount(self, parent): return len(self.columns_types) @@ -263,7 +282,7 @@ class HistoryTableModel(QAbstractTableModel): return self.transfers_data[row][col] if role == Qt.ToolTipRole and col == 0: - return self.transfers[row].metadata['time'] + return self.transfers_data[self.columns_types.index('date')] def flags(self, index): return Qt.ItemIsSelectable | Qt.ItemIsEnabled diff --git a/src/cutecoin/models/wallets.py b/src/cutecoin/models/wallets.py index 56a59423e11120ae8ce78b82906d6b7c24938899..8e78251579e23c9dbe0c56baf77fd632915638dc 100644 --- a/src/cutecoin/models/wallets.py +++ b/src/cutecoin/models/wallets.py @@ -1,24 +1,28 @@ -''' +""" Created on 8 févr. 2014 @author: inso -''' - -from PyQt5.QtCore import QAbstractTableModel, QSortFilterProxyModel, Qt, QLocale +""" +import asyncio +from PyQt5.QtCore import QAbstractTableModel, QSortFilterProxyModel, Qt, QLocale, pyqtSlot class WalletsFilterProxyModel(QSortFilterProxyModel): def __init__(self, parent=None): super().__init__(parent) self.community = None - self.account = None + self.app = None + + @property + def account(self): + return self.app.current_account def columnCount(self, parent): return self.sourceModel().columnCount(None) def setSourceModel(self, source_model): self.community = source_model.community - self.account = source_model.account + self.app = source_model.app super().setSourceModel(source_model) def lessThan(self, left, right): @@ -45,7 +49,8 @@ class WalletsFilterProxyModel(QSortFilterProxyModel): return QLocale().toString(float(amount_ref), 'f', 0) else: # display float values - return QLocale().toString(amount_ref, 'f', 6) + return QLocale().toString(amount_ref, 'f', + self.app.preferences['digits_after_comma']) if role == Qt.TextAlignmentRole: if source_index.column() == self.sourceModel().columns_types.index('amount'): @@ -56,26 +61,56 @@ class WalletsFilterProxyModel(QSortFilterProxyModel): class WalletsTableModel(QAbstractTableModel): - ''' + """ A Qt list model to display wallets and edit their names - ''' + """ + + def __init__(self, app, community, parent=None): + """ - def __init__(self, account, community, parent=None): - ''' - Constructor - ''' + :param list of cutecoin.core.wallet.Wallet wallets: The list of wallets to display + :param cutecoin.core.community.Community community: The community to show + :param PyQt5.QtCore.QObject parent: The parent widget + :return: The model + :rtype: WalletsTableModel + """ super().__init__(parent) - self.account = account + self.app = app + self.account.wallets_changed.connect(self.refresh_account_wallets) + self.community = community self.columns_headers = (self.tr('Name'), self.tr('Amount'), self.tr('Pubkey')) self.columns_types = ('name', 'amount', 'pubkey') + @property + def account(self): + return self.app.current_account + @property def wallets(self): return self.account.wallets + @pyqtSlot() + def refresh_account_wallets(self): + """ + Change the current wallets, reconnect the slots + """ + self.beginResetModel() + for w in self.account.wallets: + w.inner_data_changed.connect(lambda: self.refresh_wallet(w)) + self.endResetModel() + + def refresh_wallet(self, wallet): + """ + Refresh the specified wallet value + :param cutecoin.core.wallet.Wallet wallet: The wallet to refresh + """ + index = self.account.wallets.index(wallet) + if index > 0: + self.dataChanged.emit(index, index) + def rowCount(self, parent): return len(self.wallets) diff --git a/src/cutecoin/tests/__init__.py b/src/cutecoin/tests/__init__.py index 8b137891791fe96927ad78e64b0aad7bded08bdc..1d65e88026c473b057c001647a460228f8415d7c 100644 --- a/src/cutecoin/tests/__init__.py +++ b/src/cutecoin/tests/__init__.py @@ -1 +1 @@ - +from .qapp import get_application \ No newline at end of file diff --git a/src/cutecoin/tests/main_window/__init__.py b/src/cutecoin/tests/main_window/__init__.py index 8b137891791fe96927ad78e64b0aad7bded08bdc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/src/cutecoin/tests/main_window/__init__.py +++ b/src/cutecoin/tests/main_window/__init__.py @@ -1 +0,0 @@ - diff --git a/src/cutecoin/tests/main_window/test_main_window_dialogs.py b/src/cutecoin/tests/main_window/test_main_window_dialogs.py index 7cfc3b895ba99a083801a3c4e6c010e23281aed6..db842cfde92a8afcb3b19602468524c0842a73af 100644 --- a/src/cutecoin/tests/main_window/test_main_window_dialogs.py +++ b/src/cutecoin/tests/main_window/test_main_window_dialogs.py @@ -1,80 +1,93 @@ -import sys import unittest -import gc -import PyQt5 -from PyQt5.QtWidgets import QApplication, QMenu +import asyncio +import quamash +from PyQt5.QtWidgets import QDialog, QFileDialog from PyQt5.QtCore import QLocale, QTimer +from PyQt5.QtNetwork import QNetworkAccessManager from cutecoin.gui.mainwindow import MainWindow from cutecoin.core.app import Application +from cutecoin.tests import get_application + +from cutecoin.tests.stubs.core.registry import IdentitiesRegistry # Qapplication cause a core dumped when re-run in setup # set it as global var -qapplication = QApplication(sys.argv) - class MainWindowDialogsTest(unittest.TestCase): def setUp(self): QLocale.setDefault(QLocale("en_GB")) -# self.application = Application(sys.argv, qapplication) -# self.main_window = MainWindow(self.application) -# -# def test_action_about(self): -# # select menu -# self.main_window.actionAbout.trigger() -# widgets = qapplication.topLevelWidgets() -# for widget in widgets: -# if isinstance(widget, PyQt5.QtWidgets.QDialog): -# self.assertEqual(widget.objectName(), 'AboutPopup') -# self.assertEqual(widget.isVisible(), True) -# widget.close() -# break -# -# def test_action_add_account(self): -# # asynchronous test, cause dialog is waiting user response -# QTimer.singleShot(0, self._async_test_action_add_account) -# # select menu -# self.main_window.action_add_account.trigger() -# -# def _async_test_action_add_account(self): -# widgets = qapplication.topLevelWidgets() -# for widget in widgets: -# if isinstance(widget, PyQt5.QtWidgets.QDialog): -# self.assertEqual(widget.objectName(), 'AccountConfigurationDialog') -# self.assertEqual(widget.isVisible(), True) -# widget.close() -# break -# # -# # # fixme: require a app.current_account fixture -# # # def test_action_configure_account(self): -# # # # asynchronous test, cause dialog is waiting user response -# # # QTimer.singleShot(0, self._async_test_action_configure_account) -# # # # select about menu -# # # self.main_window.action_configure_parameters.trigger() -# # # -# # # def _async_test_action_configure_account(self): -# # # widgets = qapplication.topLevelWidgets() -# # # for widget in widgets: -# # # if isinstance(widget, PyQt5.QtWidgets.QDialog): -# # # self.assertEqual(widget.objectName(), 'AccountConfigurationDialog') -# # # self.assertEqual(widget.isVisible(), True) -# # # widget.close() -# # # break -# # -# -# def test_action_export_account(self): -# # select menu -# self.main_window.action_export.trigger() -# -# widgets = qapplication.topLevelWidgets() -# for widget in widgets: -# if isinstance(widget, PyQt5.QtWidgets.QFileDialog): -# self.assertEqual(widget.objectName(), 'ExportFileDialog') -# self.assertTrue(widget.isVisible()) -# widget.close() -# break + self.qapplication = get_application() + self.lp = quamash.QEventLoop(self.qapplication) + asyncio.set_event_loop(self.lp) + network_manager = QNetworkAccessManager() + + self.application = Application(self.qapplication, self.lp, network_manager, IdentitiesRegistry()) + self.main_window = MainWindow(self.application) + + def tearDown(self): + # delete all top widgets from main QApplication + try: + self.lp.close() + finally: + asyncio.set_event_loop(None) + + def test_action_about(self): + #select menu + self.main_window.actionAbout.trigger() + widgets = self.qapplication.topLevelWidgets() + for widget in widgets: + if isinstance(widget, QDialog): + if widget.isVisible(): + self.assertEqual('AboutPopup', widget.objectName()) + widget.close() + break + + def test_action_add_account(self): + #asynchronous test, cause dialog is waiting user response + QTimer.singleShot(1, self._async_test_action_add_account) + #select menu + self.main_window.action_add_account.trigger() + + def _async_test_action_add_account(self): + widgets = self.qapplication.topLevelWidgets() + for widget in widgets: + if isinstance(widget, QDialog): + if widget.isVisible(): + try: + self.assertEqual('AccountConfigurationDialog', widget.objectName()) + break + finally: + widget.close() + + # fixme: require a app.current_account fixture + # def test_action_configure_account(self): + # # asynchronous test, cause dialog is waiting user response + # QTimer.singleShot(0, self._async_test_action_configure_account) + # # select about menu + # self.main_window.action_configure_parameters.trigger() + # + # def _async_test_action_configure_account(self): + # widgets = qapplication.topLevelWidgets() + # for widget in widgets: + # if isinstance(widget, PyQt5.QtWidgets.QDialog): + # self.assertEqual(widget.objectName(), 'AccountConfigurationDialog') + # self.assertEqual(widget.isVisible(), True) + # widget.close() + # break + # + def test_action_export_account(self): + #select menu + self.main_window.action_export.trigger() - def test_ignoreme(self): - return + widgets = self.qapplication.topLevelWidgets() + for widget in widgets: + if isinstance(widget, QFileDialog): + if widget.isVisible(): + try: + self.assertEqual('ExportFileDialog', widget.objectName()) + break + finally: + widget.close() if __name__ == '__main__': unittest.main() diff --git a/src/cutecoin/tests/main_window/test_main_window_menus.py b/src/cutecoin/tests/main_window/test_main_window_menus.py index 02e6360914e18845892422ff0c142a36844071c5..95c3e6e12607d05d2523681b8970342baaca1de9 100644 --- a/src/cutecoin/tests/main_window/test_main_window_menus.py +++ b/src/cutecoin/tests/main_window/test_main_window_menus.py @@ -1,70 +1,57 @@ -# -*- coding: utf-8 -*- - import sys import unittest -import gc -import PyQt5 -from PyQt5.QtWidgets import QApplication, QMenu +import os +import asyncio +import quamash +from PyQt5.QtWidgets import QMenu from PyQt5.QtCore import QLocale from cutecoin.gui.mainwindow import MainWindow from cutecoin.core.app import Application - -# Qapplication cause a core dumped when re-run in setup -# set it as global var -qapplication = QApplication(sys.argv) - +from cutecoin.tests import get_application class MainWindowMenusTest(unittest.TestCase): def setUp(self): + self.qapplication = get_application() QLocale.setDefault(QLocale("en_GB")) -# self.application = Application(sys.argv, qapplication) -# self.main_window = MainWindow(self.application) -# -# def tearDown(self): -# # delete all top widgets from main QApplication -# lw = qapplication.topLevelWidgets() -# for w in lw: -# del w -# gc.collect() -# -# def test_menubar(self): -# children = self.main_window.menubar.children() -# menus = [] -# """:type: list[QMenu]""" -# for child in children: -# if isinstance(child, QMenu): -# menus.append(child) -# self.assertEqual(len(menus), 3) -# self.assertEqual(menus[0].objectName(), 'menu_account') -# self.assertEqual(menus[1].objectName(), 'menu_contacts') -# self.assertEqual(menus[2].objectName(), 'menu_actions') -# -# def test_menu_account(self): -# actions = self.main_window.menu_account.actions() -# """:type: list[QAction]""" -# self.assertEqual(len(actions), 10) -# self.assertEqual(actions[0].objectName(), 'action_add_account') -# self.assertEqual(actions[2].objectName(), 'action_configure_parameters') -# self.assertEqual(actions[3].objectName(), 'action_set_as_default') -# self.assertEqual(actions[5].objectName(), 'action_export') -# self.assertEqual(actions[6].objectName(), 'action_import') -# self.assertEqual(actions[8].objectName(), 'actionAbout') -# self.assertEqual(actions[9].objectName(), 'action_quit') -# -# def test_menu_contacts(self): -# actions = self.main_window.menu_contacts.actions() -# """:type: list[QAction]""" -# self.assertEqual(len(actions), 3) -# self.assertEqual(actions[1].objectName(), 'action_add_a_contact') -# -# def test_menu_actions(self): -# actions = self.main_window.menu_actions.actions() -# """:type: list[QAction]""" -# self.assertEqual(len(actions), 2) -# self.assertEqual(actions[0].objectName(), 'actionTransfer_money') -# self.assertEqual(actions[1].objectName(), 'actionCertification') - def test_ignoreme(self): - return + self.lp = quamash.QEventLoop(self.qapplication) + asyncio.set_event_loop(self.lp) + + self.application = Application(self.qapplication, self.lp, None, None) + self.main_window = MainWindow(self.application) + + def tearDown(self): + try: + self.lp.close() + finally: + asyncio.set_event_loop(None) + + def test_menubar(self): + children = self.main_window.menubar.children() + menus = [] + """:type: list[QMenu]""" + for child in children: + if isinstance(child, QMenu): + menus.append(child) + self.assertEqual(len(menus), 3) + self.assertEqual(menus[0].objectName(), 'menu_file') + self.assertEqual(menus[1].objectName(), 'menu_account') + self.assertEqual(menus[2].objectName(), 'menu_help') + + def test_menu_account(self): + actions = self.main_window.menu_account.actions() + """:type: list[QAction]""" + self.assertEqual('action_configure_parameters', actions[1].objectName()) + self.assertEqual('action_add_account', actions[2].objectName()) + self.assertEqual('actionCertification', actions[4].objectName()) + self.assertEqual('actionTransfer_money', actions[5].objectName()) + self.assertEqual('action_add_a_contact', actions[7].objectName()) + self.assertEqual(9, len(actions)) + + def test_menu_actions(self): + actions = self.main_window.menu_help.actions() + """:type: list[QAction]""" + self.assertEqual(len(actions), 1) + self.assertEqual(actions[0].objectName(), 'actionAbout') if __name__ == '__main__': unittest.main() diff --git a/src/cutecoin/tests/qapp.py b/src/cutecoin/tests/qapp.py new file mode 100644 index 0000000000000000000000000000000000000000..d3e1a8f657a8d8a72ca386ba3b2e8e7313d44fbb --- /dev/null +++ b/src/cutecoin/tests/qapp.py @@ -0,0 +1,14 @@ + +_application_ = [] + +def get_application(): + """Get the singleton QApplication""" + from PyQt5.QtWidgets import QApplication + if not len(_application_): + application = QApplication.instance() + if not application: + import sys + application = QApplication(sys.argv) + _application_.append( application ) + return _application_[0] + diff --git a/src/cutecoin/tests/stubs/__init__.py b/src/cutecoin/tests/stubs/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/cutecoin/tests/stubs/core/__init__.py b/src/cutecoin/tests/stubs/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/cutecoin/tests/stubs/core/net/__init__.py b/src/cutecoin/tests/stubs/core/net/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8779e25f1cc4a979a682c0b5fb9e2833540fe6df --- /dev/null +++ b/src/cutecoin/tests/stubs/core/net/__init__.py @@ -0,0 +1 @@ +from .network import Network \ No newline at end of file diff --git a/src/cutecoin/tests/stubs/core/net/network.py b/src/cutecoin/tests/stubs/core/net/network.py new file mode 100644 index 0000000000000000000000000000000000000000..3d2de537d84529bd64056c7d2b25094d8c08be87 --- /dev/null +++ b/src/cutecoin/tests/stubs/core/net/network.py @@ -0,0 +1,98 @@ +""" +Created on 24 févr. 2015 + +@author: inso +""" +import asyncio + +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject + + +class Network(QObject): + """ + A network is managing nodes polling and crawling of a + given community. + """ + nodes_changed = pyqtSignal() + new_block_mined = pyqtSignal(int) + + def __init__(self, network_manager, currency, nodes): + """ + Constructor of a network + + :param str currency: The currency name of the community + :param list nodes: The root nodes of the network + """ + super().__init__() + self.currency = currency + self.network_manager = network_manager + + @classmethod + def create(cls, network_manager, node): + nodes = [node] + network = cls(network_manager, node.currency, nodes) + return network + + def merge_with_json(self, json_data): + pass + + @classmethod + def from_json(cls, network_manager, currency, json_data): + nodes = [] + network = cls(network_manager, currency, nodes) + return network + + def jsonify(self): + data = [] + return data + + @property + def quality(self): + return 0.33 + + def stop_coroutines(self): + pass + + def continue_crawling(self): + return False + + @property + def synced_nodes(self): + return self.nodes + + @property + def online_nodes(self): + return self.nodes + + @property + def nodes(self): + """ + Get all knew nodes. + """ + return self._nodes + + @property + def root_nodes(self): + return self._root_nodes + + @property + def latest_block(self): + return 20000 + + def add_node(self, node): + pass + + def add_root_node(self, node): + pass + + def remove_root_node(self, index):pass + + def is_root_node(self, node): + return True + + def root_node_index(self, index): + return self.nodes[0] + + @asyncio.coroutine + def discover_network(self): + pass diff --git a/src/cutecoin/tests/stubs/core/registry/__init__.py b/src/cutecoin/tests/stubs/core/registry/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4ab046a19ff274331c3bc1ffd25e16bd19985478 --- /dev/null +++ b/src/cutecoin/tests/stubs/core/registry/__init__.py @@ -0,0 +1,2 @@ +from .identities import IdentitiesRegistry +from .identity import Identity \ No newline at end of file diff --git a/src/cutecoin/tests/stubs/core/registry/identities.py b/src/cutecoin/tests/stubs/core/registry/identities.py new file mode 100644 index 0000000000000000000000000000000000000000..e06ecb447f7cd9574c109773994a81b4ca0b2fd0 --- /dev/null +++ b/src/cutecoin/tests/stubs/core/registry/identities.py @@ -0,0 +1,29 @@ +from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QTimer +from cutecoin.core.net.api import bma as qtbma +from .identity import Identity + +import asyncio + + +class IdentitiesRegistry: + def __init__(self, instances={}): + pass + + def load_json(self, json_data): + pass + + def jsonify(self): + return {'registry': []} + + def lookup(self, pubkey, community): + identity = Identity.empty(pubkey) + return identity + + @asyncio.coroutine + def future_lookup(self, pubkey, community): + identity = Identity.empty(pubkey) + yield from asyncio.sleep(1) + return identity + + def from_metadata(self, metadata): + return Identity() diff --git a/src/cutecoin/tests/stubs/core/registry/identity.py b/src/cutecoin/tests/stubs/core/registry/identity.py new file mode 100644 index 0000000000000000000000000000000000000000..7c0e3a5c4b796facded48ce0e372bb3ed24341b8 --- /dev/null +++ b/src/cutecoin/tests/stubs/core/registry/identity.py @@ -0,0 +1,114 @@ +""" +Created on 11 févr. 2014 + +@author: inso +""" + +import logging +import time +import asyncio + +from ucoinpy.documents.certification import SelfCertification +from cutecoin.tools.exceptions import Error, NoPeerAvailable,\ + MembershipNotFoundError +from cutecoin.core.net.api import bma as qtbma +from cutecoin.core.net.api.bma import PROTOCOL_VERSION +from PyQt5.QtCore import QObject, pyqtSignal + + +class Identity(QObject): + """ + A person with a uid and a pubkey + """ + FOUND = 1 + NOT_FOUND = 0 + + inner_data_changed = pyqtSignal(str) + + def __init__(self, uid, pubkey, status): + """ + Initializing a person object. + + :param str uid: The person uid, also known as its uid on the network + :param str pubkey: The person pubkey + :param int status: The local status of the identity + """ + super().__init__() + assert(status in (Identity.FOUND, Identity.NOT_FOUND)) + self.uid = uid + self.pubkey = pubkey + self.status = status + + @classmethod + def empty(cls, pubkey): + return cls("", pubkey, Identity.NOT_FOUND) + + @classmethod + def from_metadata(cls, metadata): + return cls(metadata["text"], metadata["id"], Identity.NOT_FOUND) + + @classmethod + 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_data['pubkey'] + uid = json_data['uid'] + status = json_data['status'] + + return cls(uid, pubkey, status) + + @asyncio.coroutine + def selfcert(self, community): + yield from asyncio.sleep(1) + return None + + def get_join_date(self, community): + return time.time() + 100000000 + + def get_expiration_date(self, community): + return time.time() + 1000000000 + + def membership(self, community): + raise MembershipNotFoundError() + + def published_uid(self, community): + return False + + def is_member(self, community): + return False + + def certifiers_of(self, community): + return list() + + def unique_valid_certifiers_of(self, community): + return list() + + def certified_by(self, community): + return list() + + def unique_valid_certified_by(self, community): + return list() + + def membership_expiration_time(self, community): + current_time = time.time() + return current_time+1000000 + + def jsonify(self): + """ + Get the community as dict in json format. + :return: The community as a dict in json format + """ + data = {'uid': self.uid, + 'pubkey': self.pubkey, + 'status': self.status} + return data + + def __str__(self): + status_str = ("NOT_FOUND", "FOUND") + return "{0} - {1} - {2}".format(self.uid, + self.pubkey, + status_str[self.status]) \ No newline at end of file diff --git a/src/cutecoin/tools/exceptions.py b/src/cutecoin/tools/exceptions.py index f394b397109d777550893881fd698817120ec199..8821d1c8ed90662e22ae4c5f57bc47a6b78581f2 100644 --- a/src/cutecoin/tools/exceptions.py +++ b/src/cutecoin/tools/exceptions.py @@ -1,16 +1,16 @@ -''' +""" Created on 9 févr. 2014 @author: inso -''' +""" class Error(Exception): def __init__(self, message): - ''' + """ Constructor - ''' + """ self.message = "Error : " + message def __str__(self): @@ -19,44 +19,44 @@ class Error(Exception): class NotMemberOfCommunityError(Error): - ''' + """ Exception raised when adding a community the account is not a member of - ''' + """ def __init__(self, account, community): - ''' + """ Constructor - ''' + """ super() \ .__init__(account + " is not a member of " + community) -class PersonNotFoundError(Error): +class LookupFailureError(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__( "Person looked by {0} in {1} not found ".format(value, community)) 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 + @@ -67,29 +67,29 @@ class MembershipNotFoundError(Error): class AlgorithmNotImplemented(Error): - ''' + """ Exception raised when a coin uses an algorithm not known - ''' + """ def __init__(self, algo_name): - ''' + """ Constructor - ''' + """ super() \ .__init__("Algorithm " + algo_name + " not implemented.") class KeyAlreadyUsed(Error): - ''' + """ Exception raised trying to add an account using a key already used for another account. - ''' + """ def __init__(self, new_account, keyid, found_account): - ''' + """ Constructor - ''' + """ super() .__init__( """Cannot add account {0} : the key {1} is already used by {2}""".format(new_account, @@ -100,15 +100,15 @@ the key {1} is already used by {2}""".format(new_account, class NameAlreadyExists(Error): - ''' + """ Exception raised trying to add an account using a key already used for another account. - ''' + """ def __init__(self, account_name): - ''' + """ Constructor - ''' + """ super() .__init__( "Cannot add account " + account_name + @@ -117,30 +117,30 @@ class NameAlreadyExists(Error): class BadAccountFile(Error): - ''' + """ Exception raised trying to add an account using a key already used for another account. - ''' + """ def __init__(self, path): - ''' + """ Constructor - ''' + """ super() .__init__( "File " + path + " is not an account file") class NotEnoughMoneyError(Error): - ''' + """ Exception raised trying to add an account using a key already used for another account. - ''' + """ def __init__(self, available, currency, nb_inputs, requested): - ''' + """ Constructor - ''' + """ super() .__init__( "Only {0} {1} available in {2} sources, needs {3}" .format(available, @@ -150,41 +150,41 @@ class NotEnoughMoneyError(Error): class NoPeerAvailable(Error): - ''' + """ Exception raised when a community doesn't have any peer available. - ''' + """ def __init__(self, currency, peers): - ''' + """ Constructor - ''' + """ super() .__init__( "No peer answered in {0} community ({1} peers available)" .format(currency, peers)) class InvalidNodeCurrency(Error): - ''' + """ Exception raised when a node doesn't use the intended currency - ''' + """ def __init__(self, currency, node_currency): - ''' + """ Constructor - ''' + """ super() .__init__( "Node is working for {0} currency, but should be {1}" .format(node_currency, currency)) class ContactAlreadyExists(Error): - ''' + """ Exception raised when a community doesn't have any peer available. - ''' + """ def __init__(self, new_contact, already_contact): - ''' + """ Constructor - ''' + """ super() .__init__( "Cannot add {0}, he/she has the same pubkey as {1} contact" .format(new_contact, already_contact))