diff --git a/.gitignore b/.gitignore index 3c3629e647f5ddf82548912e337bea9826b434af..97008e5b868ddbfa6767109cebdf00d7868ee8aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +yarn.lock \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..091b8c160afb1760f82d7dfaa4ca1c374a81a049 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,33 @@ +stages: + - github-sync + - deploy + +push_to_github: + stage: github-sync + variables: + GIT_STRATEGY: none + tags: + - github + script: + - rm -rf ./* + - rm -rf .git + - git clone --mirror $CI_REPOSITORY_URL . + - git remote add github $GITHUB_URL_AND_KEY + - git config --global user.email "contact@duniter.org" + - git config --global user.name "Duniter" + # Job would fail if we don't remove refs about pull requests + - bash -c "cat packed-refs | grep -v 'refs/pull' > packed-refs-new; echo 'Removed pull refs.'" + - mv packed-refs-new packed-refs + - bash -c "git push --force --mirror github 2>&1 | grep -v duniter-gitlab; echo $?" + +publish: + stage: deploy + image: node:6.12-alpine + tags: + - nodejs + only: + - tags + - triggers + script: + - echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}'>.npmrc + - npm publish \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..5f8c823f02f8246a92338f638282888f6697885f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Currency-Monit", + "program": "${workspaceRoot}/run.js", + "args": ["currency-monit"] + } + ] +} \ No newline at end of file diff --git a/lg/about_en.txt b/lg/about_en.txt new file mode 100644 index 0000000000000000000000000000000000000000..615596ca85d8b0f76bb61e1f128b2f11cfde405d --- /dev/null +++ b/lg/about_en.txt @@ -0,0 +1,9 @@ +LG en +VERSION Version +AUTHOR Author +CONTRIBUTORS Others Contributors +LICENSE license +GIT_REPOSITORY git repository +IF_YOU_WANT If you want you can +DISABLE_HELP disable help +DONATE You can support the development of this module by a gift in Ğ1 \ No newline at end of file diff --git a/lg/about_fr.txt b/lg/about_fr.txt new file mode 100644 index 0000000000000000000000000000000000000000..de67524f20240dc70391630f858f0ce06893f7b3 --- /dev/null +++ b/lg/about_fr.txt @@ -0,0 +1,9 @@ +LG fr +VERSION Version +AUTHOR Auteur +CONTRIBUTORS Autres contributeurs +LICENSE licence +GIT_REPOSITORY dépôt git +IF_YOU_WANT Si vous le souhaitez vous pouvez +DISABLE_HELP désactiver l'aide +DONATE Vous pouvez soutenir le développement de ce module par un don en Ğ1 \ No newline at end of file diff --git a/lg/blockCount_en.txt b/lg/blockCount_en.txt index fef7489111f66bba719ebcb5e5fb4a6c9e08ac28..0b4c69051b82ef71af69167872ea971d21f4f437 100755 --- a/lg/blockCount_en.txt +++ b/lg/blockCount_en.txt @@ -1 +1,13 @@ -SUBMIT_BUTTON submit \ No newline at end of file +SUBMIT_BUTTON submit +WRITTEN_BLOCKS Written blocks +BLOCKCHAIN blockchain +SINCE_BECOME_MEMBER since become member +MEAN_NONCE Mean nonce +RANGE members have written blocks in the range +BEGIN Begin +END End +NB_BLOCKS Number of written Blocks +PERCENT_OF_WRITTEN_BLOCKS Percent of written Blocks +DETAIL_BY_NODE detail by node +SIGNIFICANT_LIMIT significant limit +PERCENT_OF_BLOCKS % of blocks \ No newline at end of file diff --git a/lg/blockCount_fr.txt b/lg/blockCount_fr.txt index 5ed9c4264d6b35bc0503f19338397fdb00f4ae95..1f5aaef14b425ad20c141a6ee001a5f6c0ff3bf3 100755 --- a/lg/blockCount_fr.txt +++ b/lg/blockCount_fr.txt @@ -1 +1,13 @@ -SUBMIT_BUTTON recharger \ No newline at end of file +SUBMIT_BUTTON recharger +WRITTEN_BLOCKS blocs écrits +BLOCKCHAIN blockchain +SINCE_BECOME_MEMBER par chaque membre depuis qu'il est devenu membre +MEAN_NONCE Nonce moyen +RANGE membres ayant écrit des blocs sur la période +BEGIN Début +END Fin +NB_BLOCKS Nombre de blocs écrits +PERCENT_OF_WRITTEN_BLOCKS % de blocs écrits +DETAIL_BY_NODE detail par noeud +SIGNIFICANT_LIMIT noeud significatif à partir de +PERCENT_OF_BLOCKS % de blocs \ No newline at end of file diff --git a/lg/gaussianWotQuality_en.txt b/lg/gaussianWotQuality_en.txt new file mode 100644 index 0000000000000000000000000000000000000000..d006671f1a47aaad43a3a5c1eb82d4c7d9a06c12 --- /dev/null +++ b/lg/gaussianWotQuality_en.txt @@ -0,0 +1,7 @@ +SUBMIT_BUTTON submit +DISTRIBUTION_QUALITY gaussian distribution of quality members +IF_NO_SENTRIES If the concept of referring members didn't exist +NEXT_YN Feign the following Y[n] landing +QUALITY quality +PERCENT_REACHED % of referring members reached +NB_REACHED number of referring members reached \ No newline at end of file diff --git a/lg/gaussianWotQuality_fr.txt b/lg/gaussianWotQuality_fr.txt new file mode 100644 index 0000000000000000000000000000000000000000..ef3af141bffe963987ace30a05b6ba7dd49c93ba --- /dev/null +++ b/lg/gaussianWotQuality_fr.txt @@ -0,0 +1,7 @@ +SUBMIT_BUTTON recharger +DISTRIBUTION_QUALITY distribution gaussienne de la qualité des membres +IF_NO_SENTRIES si le concept de membre référent n'existait pas +NEXT_YN Simuler le palier Y[n] suivant +QUALITY qualité +PERCENT_REACHED % de nombres référents atteints +NB_REACHED nombre de membres référents atteints \ No newline at end of file diff --git a/lg/membersCount_en.txt b/lg/membersCount_en.txt index ffe695c55960acfcf91692439409a16e363e4433..6284013e0b9f5a944c4b46b12bedf59ca636ca60 100755 --- a/lg/membersCount_en.txt +++ b/lg/membersCount_en.txt @@ -17,4 +17,7 @@ Afficher l'évolution de la difficulté commune du réseau (preuve de travail) MEMBERS_COUNT members SENTRIES_COUNT referring* members ISSUERS_COUNT Members co-writers blockchain -POW_MIN Common difficulty network (proof of work) \ No newline at end of file +POW_MIN Common difficulty network (proof of work) +MAX max +POINTS points +REGULATE_BY_ADAPTING regulate by adapting \ No newline at end of file diff --git a/lg/membersCount_fr.txt b/lg/membersCount_fr.txt index 1c0a036c1e69c5327cfaf806c7243f2326cf46e1..5605b8e8a30a4d4cfa02d0f7f468920a6dce714f 100755 --- a/lg/membersCount_fr.txt +++ b/lg/membersCount_fr.txt @@ -16,4 +16,7 @@ SHOW_POW_MIN Afficher l'évolution de la difficulté commune du réseau (preuve MEMBERS_COUNT Membres SENTRIES_COUNT Membres référents* ISSUERS_COUNT Membres co-écrivains de la blockchain Ğ1 -POW_MIN Difficulté commune du réseau (preuve de travail) \ No newline at end of file +POW_MIN Difficulté commune du réseau (preuve de travail) +MAX max +POINTS points +REGULATE_BY_ADAPTING réguler en adaptant \ No newline at end of file diff --git a/lg/members_en.txt b/lg/members_en.txt index b29593eff33df15a945d8300e7be69bc91a3bcc6..84d2f38affce5880c8eaf082d95872f130c96fc6 100755 --- a/lg/members_en.txt +++ b/lg/members_en.txt @@ -30,7 +30,7 @@ PROPORTION_OF_EXIST_PATH Percent of pairs directed for which there is a path of MEAN_LENGTH_PATH mean length shortest path WOT_TENSION_FACTOR Wot tension factor* DATA_AT Data at -meanMembersReachedByMembersInSingleExtCert Mean members/sentries reached in single member/sentry certification +meanMembersReachedByMembers Mean members/sentries reached in single member/sentry certification SENTRIES_REACHED sentries reached MEMBERS_REACHED members reached SENTRY_CERT sentry certification @@ -40,6 +40,8 @@ PROPORTION_MEMBERS_WITH_QUALITY_UPPER_1 Proportion of members with an upper qual CURRENT_BLOCKCHAIN_TIME Current blockchain time TABLE_TITLE Members that will expire in less than DAYS days +OF_WHICH of which +ARE_REFERRING_MEMBERS are referrings COL_UID uid COL_IDTY_WRITTEN_TIME identity written time COL_LAST_RENEWAL last renewal @@ -60,5 +62,9 @@ EMITTED emitted WRITTEN written INVALID_BLOCKSTAMP invalid blockstamp CERT_AVAILABLE available -OVERALL Overall +MEMBERS members +NEXT_YN Feign the following Y[n] landing +MEMBER_FILTER search a member +EXPIRE_TIME expiration datetime +RANDOM_LIST Draw lots MEMBERS members \ No newline at end of file diff --git a/lg/members_fr.txt b/lg/members_fr.txt index d84826be44e77c190401262569a633082aa0a35a..5a13228d6573952715dd1a83e33b72894b36d739 100755 --- a/lg/members_fr.txt +++ b/lg/members_fr.txt @@ -30,7 +30,7 @@ PROPORTION_OF_EXIST_PATH Proportion de couples orientés pour lesquels il existe MEAN_LENGTH_PATH Longueur moyenne d'un plus court chemin WOT_TENSION_FACTOR Facteur de tension de la toile* DATA_AT Données au -meanMembersReachedByMembersInSingleExtCert Taux moyen de membres joiniables via une seule certification +meanMembersReachedByMembers Taux moyen de membres joiniables via une seule certification MEMBER_CERT certification d'un membre MEMBERS_REACHED membres joiniables SENTRY_CERT certification d'un membre référent @@ -40,6 +40,8 @@ PROPORTION_MEMBERS_WITH_QUALITY_UPPER_1 Proportion de membre avec une qualité > CURRENT_BLOCKCHAIN_TIME Temps Blockchain actuel TABLE_TITLE Membres dont le statut de membre va expirer dans moins de DAYS jours +OF_WHICH dont +ARE_REFERRING_MEMBERS sont référents COL_UID uid COL_IDTY_WRITTEN_TIME obtention statut membre COL_LAST_RENEWAL dernier renouvellement @@ -60,5 +62,9 @@ EMITTED émise WRITTEN écrite INVALID_BLOCKSTAMP blockstamp incorrect CERT_AVAILABLE disponible -OVERALL Total +MEMBERS membres +NEXT_YN Simuler le palier Y[n] suivant +MEMBER_FILTER rechercher un membre +EXPIRE_TIME date et heure d'expiration +RANDOM_LIST Tirer au sort MEMBERS membres \ No newline at end of file diff --git a/lg/menu_en.txt b/lg/menu_en.txt index 876b5a55881ff15b122f7f1e0ef1710db6858c96..4adc1d3601c310b39c37e4e253a3ef2101b36310 100755 --- a/lg/menu_en.txt +++ b/lg/menu_en.txt @@ -2,7 +2,8 @@ LG en WILL_MEMBERS willMembers MEMBERS members MEMBERS_COUNT membersCount +WOTEX wotex +GAUSSIAN_WOT_QUALITY gaussianWotQuality BLOCK_COUNT blockCount MONETARY_MASS monetaryMass -WOTEX wotex ABOUT about \ No newline at end of file diff --git a/lg/menu_fr.txt b/lg/menu_fr.txt index 244a325cd9fae537084711876ec28f395504c64e..f0ca603d23741e016034ae22f3c257d825ec8e7d 100755 --- a/lg/menu_fr.txt +++ b/lg/menu_fr.txt @@ -2,7 +2,8 @@ LG fr WILL_MEMBERS futurs membres MEMBERS listes des membres MEMBERS_COUNT nombre de membres +WOTEX wotex +GAUSSIAN_WOT_QUALITY qualité toile BLOCK_COUNT nombre de blocs MONETARY_MASS masse monétaire -WOTEX wotex ABOUT a propos \ No newline at end of file diff --git a/lg/monetaryMass_en.txt b/lg/monetaryMass_en.txt index fef7489111f66bba719ebcb5e5fb4a6c9e08ac28..090024dbb3d7f63611e7d4c51e6c406946c2f957 100755 --- a/lg/monetaryMass_en.txt +++ b/lg/monetaryMass_en.txt @@ -1 +1,22 @@ -SUBMIT_BUTTON submit \ No newline at end of file +SUBMIT_BUTTON submit +DESC1 The currency will be full when the money supply by member will be worth +UD UD +FULL_CURRENCY_FORMULA because 1/c * dtReeval/dt = +CURRENTLY Currently +AND_WE_HAVE and we have +DESC2 members. Thus in full currency we would have a total money supply of +MEMBER member +BEGIN Begin +END End +QUANTITATIVE quantitative +RELATIVE relative +PERCENT_OF_FULL_CURRENCY percent of full currency +MASS_BY_MEMBERS mass by members +TOTAL_MASS total mass +LOGARITHMIC logarithmic +LINEAR linear +MONETARY_MASS monetaryMass +IN_PERCENT_OF_FULL_CURRENCY in % of full currency +PERCENT_VARIATION_MONETARY_MASS % of variation of the monetary mass +BY_MEMBERS by members +IN_THE_RANGE in the range \ No newline at end of file diff --git a/lg/monetaryMass_fr.txt b/lg/monetaryMass_fr.txt index 5ed9c4264d6b35bc0503f19338397fdb00f4ae95..2e1331f1aded6c27c6255b3992bdb2f6a5734a26 100755 --- a/lg/monetaryMass_fr.txt +++ b/lg/monetaryMass_fr.txt @@ -1 +1,23 @@ -SUBMIT_BUTTON recharger \ No newline at end of file +SUBMIT_BUTTON recharger +DESC1 La monnaie sera pleine lorsque la masse monétaire par membre atteindra +The currency will be full when the money supply by member will be worth +UD DU +FULL_CURRENCY_FORMULA car 1/c * dtReeval/dt +CURRENTLY Actuellement +AND_WE_HAVE et nous sommes +DESC2 membres. Donc si la monnaie était pleine nous aurions une masse totale de +MEMBER membre +BEGIN Début +END Fin +QUANTITATIVE quantitatif +RELATIVE relatif +PERCENT_OF_FULL_CURRENCY pourcentage de monnaie pleine +MASS_BY_MEMBERS masse par membre +TOTAL_MASS masse totale +LOGARITHMIC logarithmique +LINEAR linéaire +MONETARY_MASS masse monetaire +IN_PERCENT_OF_FULL_CURRENCY en % de monnaie pleine +PERCENT_VARIATION_MONETARY_MASS % de variation de la masse monétaire +BY_MEMBER par membre +IN_THE_RANGE dans la période \ No newline at end of file diff --git a/lg/willMembers_en.txt b/lg/willMembers_en.txt index 47d0f60971b02dd7ffdae69dddcd133c1911a52f..d66105996bdac8ba75fbb9acb8c2ed85c6b8ab34 100755 --- a/lg/willMembers_en.txt +++ b/lg/willMembers_en.txt @@ -30,25 +30,28 @@ MEAN_SENTRIES_REACHED mean sentries reached MEAN_QUALITY_CERTS Quality averages of the set of certifications MEAN_MEMBERS_REACHED mean members reached IDENTITY Identity -MEMBERSHIP_CASE membership case +MEMBERSHIP_CASE_FULL complete case ? +DISTANCE_RULE distance COL_4 List of received certifications (recent -> old) COL_4_WITH_AVAIlABILITY_SORT List of received certifications (by availability time) +PUBKEY_PART pubkey (first 16 characters) +EMITTE emitted EMITTED_ON emitted on -AT_BLOCK at block -EXPIRE_ON expire on +EXPIRE_TIME expiration datetime KO KO OK OK -MEMBERSHIP_ASKED Membership asked +MEMBERSHIP_NOT_ASKED Membership not asked YES yes NO no -QUALITY_CERTIFIERS quality certifiers +QUALITY_CERTIFIERS package quality DISTANCE Distance -CERTIFIERS_COUNT certifiers count +CERTS certifications QUALITY quality SIG_PERIOD_OK available IDTY_REVOKED [identity revoked] INVALID_BLOCKSTAMP invalid blockstamp LAST_TR1 total LAST_TR2 identities -LICENSE licence GPL-3.0 -SRC source code +ON_WOTEX on wotex +IDTY_FILTER filter identities +MISS it's missing diff --git a/lg/willMembers_fr.txt b/lg/willMembers_fr.txt index 589ccb14926c58e66b690a8386e429ddb250cbac..09d3fbe18bbd51bed1b7be110cb9bad3d015460d 100755 --- a/lg/willMembers_fr.txt +++ b/lg/willMembers_fr.txt @@ -30,25 +30,28 @@ MEAN_SENTRIES_REACHED Taux moyen de membres référents joiniables MEAN_QUALITY_CERTS Qualité moyenne par groupe de certifications reçues MEAN_MEMBERS_REACHED Taux moyen de membres joiniables IDENTITY Identité -MEMBERSHIP_CASE dossier d'adhésion +MEMBERSHIP_CASE_FULL dossier complet ? +DISTANCE_RULE distance COL_4 liste des certifications reçues (récentes -> anciennes) COL_4_WITH_AVAIlABILITY_SORT liste des certifications reçues (par date de disponibilité) +PUBKEY_PART clé publique (16 premiers caractères) +EMITTED émise EMITTED_ON émise le -AT_BLOCK au bloc -EXPIRE_ON expire le +EXPIRE_TIME date et heure d'expiration KO KO OK OK -MEMBERSHIP_ASKED Adhésion demandée +MEMBERSHIP_NOT_ASKED Adhésion non demandée YES oui NO non -QUALITY_CERTIFIERS qualité certificateurs +QUALITY_CERTIFIERS qualité dossier DISTANCE Distance -CERTIFIERS_COUNT Nombre de certificateurs +CERTS certifications QUALITY qualité SIG_PERIOD_OK disponible IDTY_REVOKED [identité revoquée] INVALID_BLOCKSTAMP blockstamp incorrect LAST_TR1 total LAST_TR2 identités -LICENSE licence GPL-3.0 -SRC code source +ON_WOTEX sur wotex +IDTY_FILTER filtrer les identités +MISS il manque diff --git a/lib/constants.js b/lib/constants.js index d97be3bd6a649d7076c3831954cbd5d44d91937e..557f407e58822165deba4714cc1dc614974fbe9f 100755 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,10 +1,18 @@ "use strict"; module.exports = { - USE_WOTB6: true, + DEFAULT_LANGUAGE: "fr", MIN_WILLMEMBERS_UPDATE_FREQ: 180, MIN_MEMBERS_UPDATE_FREQ: 180, STEP_COUNT_MIN: 4, STEP_COUNT_MAX: 150, - MIN_CACHE_UPDATE_FREQ: 150 // 2 min 30 (150 sec) + MIN_CACHE_UPDATE_FREQ: 150, // 2 min 30 (150 sec) + MIN_WOT_QUALITY_CACHE_UPDATE_FREQ: 300, + QUALITY_CACHE_ACTION : { + GET_QUALITY: 0, + GET_MEANS: 1, + INIT: 2, + GET_SENTRIES_COUNT: 3, + GET_D_SEN: 3 + } }; \ No newline at end of file diff --git a/lib/main.js b/lib/main.js index 064c8bba5ab7b3eb3addff154cdc2f037d473ea9..0877d4da2b411e1f4e10b970921ebf724d7be61e 100755 --- a/lib/main.js +++ b/lib/main.js @@ -1,14 +1,28 @@ "use strict"; const co = require('co'); +const os = require('os'); +const fs = require('fs'); const webserver = require(__dirname + '/webserver.js'); -//const duniter = require(__dirname + '/duniter.js'); +const timestampToDatetime = require(__dirname + '/timestampToDatetime.js'); /**************************** * Main algorithm */ module.exports = (duniterServer, host, port, appParente, program) => co(function *() { + + // Get local timezone offset + var x = new Date(); + var offset = -x.getTimezoneOffset(); + //timestampToDatetime(1000000, true, offset); + + // Get monitDatasPath + const mdb = program.mdb || "duniter_default"; + const monitDatasPath = os.homedir() + "/.config/duniter/" + mdb + "/currency-monit/"; + + // Create monitDatasPath + if (!fs.existsSync(monitDatasPath)) { fs.mkdirSync(monitDatasPath) } // Define cache var cache = { @@ -34,7 +48,7 @@ module.exports = (duniterServer, host, port, appParente, program) => co(function console.log("module currency-monit started"); // Specialized node's UI - let httpServer = webserver(host, port, appParente, duniterServer, cache); + let httpServer = webserver(host, port, appParente, duniterServer, monitDatasPath, offset, cache); yield httpServer.openConnection(); }) diff --git a/lib/randomInt.js b/lib/randomInt.js new file mode 100644 index 0000000000000000000000000000000000000000..62d607c3d8c926bed68868dcdb8a7a43d14668d8 --- /dev/null +++ b/lib/randomInt.js @@ -0,0 +1,3 @@ +module.exports = function randomInt (low, high) { + return Math.floor(Math.random() * (high - low) + low); +} diff --git a/lib/timestampToDatetime.js b/lib/timestampToDatetime.js index b44f46505f749d496837239fa1355785bd00cfbb..5b86854aef88cc4a7f5642aed7dab831f7068fd3 100755 --- a/lib/timestampToDatetime.js +++ b/lib/timestampToDatetime.js @@ -1,4 +1,14 @@ -module.exports = function timestampToDatetime(timestamp, onlyDate = false) { +// cache offset +var offset = 0; + +module.exports = function timestampToDatetime(timestamp, onlyDate = false, offset_ = 0) { + if (offset_ != 0) { + offset = offset_; + } + + // Apply offset + timestamp += offset*60; // offset is in minutes + // Convertir le timestamp en datetime let tmptimestampExpireCertif = new Date(timestamp*1000);//tmpQueryGetTimeWrittenCert[0].medianTime)*1000); let tmptimestampExpireCertifDay = tmptimestampExpireCertif.getDate(); diff --git a/lib/updateCache.js b/lib/updateCache.js index b57f720ce503624c69f674efa778011b2d16ee54..0f2fd076d5dd81a9f7de3d1a8ef9dfa5de6aa897 100755 --- a/lib/updateCache.js +++ b/lib/updateCache.js @@ -38,68 +38,25 @@ module.exports = (req, res, next) => co(function *() { let checkBlock = yield duniterServer.dal.peerDAL.query('SELECT `hash` FROM block WHERE `fork`=0 AND `number`='+(cache.blockchain[cache.blockchain.length-1].number)+' LIMIT 1 '); if (cache.blockchain.length > 0 && cache.blockchain[cache.blockchain.length-1].hash != checkBlock[0].hash && upgradeCache) { - /*// unstack loop - while (cache.blockchain.length > 0 && cache.blockchain[cache.blockchain.length-1].hash != checkBlock[0].hash) - { - // unstack cache.blockchain - cache.blockchain.pop(); - - // unstack block - checkBlock = yield duniterServer.dal.peerDAL.query('SELECT `hash` FROM block WHERE `fork`=0 AND `number`='+(cache.blockchain.length-1)+' LIMIT 1 '); + // reinitialize cache + cache.lastUptime = 0; + cache.lockMembersCount = false; + cache.beginBlock = null; + cache.currentBlockNumber = 0; + cache.currentBlockTime = 0; + cache.currentSentries = 0; + cache.endBlock = null; + cache.step = null; + cache.stepUnit = null; + cache.stepTime = null; + cache.onlyDate = null; + cache.Yn = 0; + cache.pubkeys = new Array(); + cache.pub_index = new Array(); + cache.blockchain = new Array(); - console.log("unstack block #%s", cache.blockchain.length-1); - } - - let cacheTime = 0; - if (cache.blockchain.length > 0 ) - { - cacheTime = cache.blockchain[cache.blockchain.length-1].medianTime; - } - else - {*/ - // reinitialize cache - cache.lastUptime = 0; - cache.lockMembersCount = false; - cache.beginBlock = null; - cache.currentBlockNumber = 0; - cache.currentBlockTime = 0; - cache.currentSentries = 0; - cache.endBlock = null; - cache.step = null; - cache.stepUnit = null; - cache.stepTime = null; - cache.onlyDate = null; - cache.Yn = 0; - cache.pubkeys = new Array(); - cache.pub_index = new Array(); - cache.blockchain = new Array(); - - // reinitialize bdd - reinitBdd = true; - /*} - - // unstak pubkeys joins during the fork (no-member pubkeys has considered join when then received currency for the first time) - while (cache.pubkeys.length > 0 && cache.pubkeys[cache.pubkeys.length-1].join >= cache.blockchain.length) { cache.pubkeys.pop(); cache.pub_index.pop(); } - - // unstak transactions and certifications written during the fork - for (let m=0;cache.pubkeys.length;m++) - { - // unstak inputs - while (cache.pubkeys[m].inputsTime.length > 0 && cache.pubkeys[m].inputsTime[cache.pubkeys[m].inputsTime.length-1] > cacheTime) - { - cache.pubkeys[m].inputsTime.pop(); - cache.pubkeys[m].inputsAmount.pop(); - } - // unstak outputs - while (cache.pubkeys[m].outputsTime.length > 0 && cache.pubkeys[m].outputsTime[cache.pubkeys[m].outputsTime.length-1] > cacheTime) - { - cache.pubkeys[m].outputsTime.pop(); - cache.pubkeys[m].outputsAmount.pop(); - } - // unstak certs - while (cache.pubkeys[m].writtenCerts.length > 0 && cache.pubkeys[m].writtenCerts[cache.pubkeys[m].writtenCerts.length-1] > cache.blockchain.length) { cache.pubkeys[m].writtenCerts.pop(); } - while (cache.pubkeys[m].receivedCerts.length > 0 && cache.pubkeys[m].receivedCerts[cache.pubkeys[m].receivedCerts.length-1] > cache.blockchain.length) { cache.pubkeys[m].receivedCerts.pop(); } - }*/ + // reinitialize bdd + reinitBdd = true; } } @@ -113,12 +70,12 @@ module.exports = (req, res, next) => co(function *() { { switch (req.query.stepUnit) { - case "blocks": unitTime = 3600; cache.onlyDate = false; cache.stepUnit = "blocks"; break; - case "hours": unitTime = 3600; cache.onlyDate = false; cache.stepUnit = "hours"; break; - case "days": unitTime = 86400; cache.onlyDate = true; cache.stepUnit = "days"; break; - case "weeks": unitTime = 604800; cache.onlyDate = true; cache.stepUnit = "weeks"; break; - case "months": unitTime = 18144000; cache.onlyDate = true; cache.stepUnit = "months"; break; - case "years": unitTime = 31557600; cache.onlyDate = true; cache.stepUnit = "years"; break; + case "blocks": unitTime = 3600; cache.onlyDate = false; cache.stepUnit = "blocks"; break; + case "hours": unitTime = 3600; cache.onlyDate = false; cache.stepUnit = "hours"; break; + case "days": unitTime = 86400; cache.onlyDate = true; cache.stepUnit = "days"; break; + case "weeks": unitTime = 604800; cache.onlyDate = true; cache.stepUnit = "weeks"; break; + case "months": unitTime = 18144000; cache.onlyDate = true; cache.stepUnit = "months"; break; + case "years": unitTime = 31557600; cache.onlyDate = true; cache.stepUnit = "years"; break; } } // Default values @@ -140,7 +97,7 @@ module.exports = (req, res, next) => co(function *() { { cache.endBlock = yield duniterServer.dal.peerDAL.query('SELECT `hash`,`medianTime`,`number`,`membersCount` FROM block WHERE `fork`=0 ORDER BY `medianTime` DESC LIMIT 1 '); } - } + } // fix begin value if ( typeof(req.query.begin) == 'undefined' || req.query.begin < 0 ) @@ -150,21 +107,43 @@ module.exports = (req, res, next) => co(function *() { let beginTime = cache.endBlock[0].medianTime-(parseInt(cache.step)*unitTime*constants.STEP_COUNT_MIN); cache.beginBlock = yield duniterServer.dal.peerDAL.query('SELECT `medianTime`,`number` FROM block WHERE `fork`=0 AND `medianTime` >= \''+beginTime+'\' ORDER BY `medianTime` ASC LIMIT 1 '); } - else { cache.beginBlock = yield duniterServer.dal.peerDAL.query('SELECT `medianTime`,`number` FROM block WHERE `fork`=0 AND `number`='+req.query.begin+' LIMIT 1 '); } - - // Apply STEP_COUNT_MAX and calculate stepTime - if ( Math.ceil((cache.endBlock[0].medianTime-cache.beginBlock[0].medianTime)/(cache.step*unitTime)) > constants.STEP_COUNT_MAX ) + else { cache.beginBlock = yield duniterServer.dal.peerDAL.query('SELECT `medianTime`,`number` FROM block WHERE `fork`=0 AND `number`='+req.query.begin+' LIMIT 1 '); } + + // Define nbMaxPoints and adaptMaxPoints + if ( typeof(req.query.nbMaxPoints) != 'undefined' && req.query.nbMaxPoints > 0 ) { + cache.nbMaxPoints = req.query.nbMaxPoints; + } else { + cache.nbMaxPoints = constants.STEP_COUNT_MAX; + } + if ( typeof(req.query.adaptMaxPoints) != 'undefined' && (req.query.adaptMaxPoints == "step" || req.query.adaptMaxPoints == "end")) { + cache.adaptMaxPoints = req.query.adaptMaxPoints; + } else { + cache.adaptMaxPoints = "begin"; + } + + // Apply nbMaxPoints and adaptMaxPoints + if (cache.adaptMaxPoints == "begin") { + if ( Math.ceil((cache.endBlock[0].medianTime-cache.beginBlock[0].medianTime)/(cache.step*unitTime)) > cache.nbMaxPoints ) + { + let newBeginTime = cache.endBlock[0].medianTime-cache.step*cache.nbMaxPoints*unitTime; + cache.beginBlock = yield duniterServer.dal.peerDAL.query('SELECT `medianTime`,`number` FROM block WHERE `fork`=0 AND `medianTime` >= \''+newBeginTime+'\' ORDER BY `medianTime` ASC LIMIT 1 '); + } + } else if (cache.adaptMaxPoints == "step") { cache.step = Math.ceil((cache.endBlock[0].medianTime-cache.beginBlock[0].medianTime)/(constants.STEP_COUNT_MAX*unitTime)); + } else { + let newEndTime = cache.beginBlock[0].medianTime+cache.step*cache.nbMaxPoints*unitTime; + cache.endBlock = yield duniterServer.dal.peerDAL.query('SELECT `medianTime`,`number` FROM block WHERE `fork`=0 AND `medianTime` <= \''+newEndTime+'\' ORDER BY `medianTime` DESC LIMIT 1 '); } + + // Calculate stepTime cache.stepTime = parseInt(cache.step)*unitTime; // if new blocks and MIN_CACHE_UPDATE_FREQ pass, update cache if ( parseInt(cache.endBlock[0].number) >= cache.currentBlockNumber && Math.floor(Date.now() / 1000) > (cache.lastUptime + constants.MIN_CACHE_UPDATE_FREQ)) { // let previousCacheTime = (cache.blockchain.length > 0) ? cache.blockchain[cache.blockchain.length-1].medianTime:0; - var newBlocks = yield duniterServer.dal.peerDAL.query( - 'SELECT `hash`,`membersCount`,`medianTime`,`number`,`certifications`,`joiners`,`actives`,`revoked` FROM block WHERE `fork`=0 AND `medianTime` > '+cache.currentBlockTime+' AND `medianTime` <= '+cache.endBlock[0].medianTime+' ORDER BY `medianTime` ASC'); + var newBlocks = yield duniterServer.dal.peerDAL.query('SELECT `hash`,`membersCount`,`medianTime`,`number`,`certifications`,`joiners`,`actives`,`revoked` FROM block WHERE `fork`=0 AND `medianTime` > '+cache.currentBlockTime+' AND `medianTime` <= '+cache.endBlock[0].medianTime+' ORDER BY `medianTime` ASC'); // Initialise newJoiners let newJoiners = new Array(); @@ -206,162 +185,11 @@ module.exports = (req, res, next) => co(function *() { { for (let m=0;m<cache.pubkeys.length;m++) { - if (cache.pubkeys[m].writtenCerts.length >= cache.Yn && cache.pubkeys[m].receivedCerts.length >= minReceivedCerts && cache.pubkeys[m].writtenCerts.length < newYn && cache.pubkeys[m].receivedCerts.length < newYn) { newSentries -= 1; } + if (cache.pubkeys[m].writtenCerts.length >= cache.Yn && cache.pubkeys[m].receivedCerts.length >= minReceivedCerts && (cache.pubkeys[m].writtenCerts.length < newYn || cache.pubkeys[m].receivedCerts.length < newYn)) { newSentries -= 1; } } minReceivedCerts = (newYn<conf.sigQty) ? conf.sigQty:newYn; // recalculate minReceivedCerts } cache.Yn = newYn; - - /* - // add dividend to members balance - if (parseInt(newBlocks[b].dividend) > 0) - { - for (let k=0;k<cache.pubkeys.length;k++) - { - if ( cache.pubkeys[k].receivedCerts != null && cache.pubkeys[k].receivedCerts.length >= sigQty) - { cache.pubkeys[k].balance += parseInt(newBlocks[b].dividend); } - } - } - - // parse and push transactions - transactions = JSON.parse(newBlocks[b].transactions); - for (let t=0;t<transactions.length;t++) - { - // Calculate inputsSum - let inputsSum = 0; - for (let i=0;i<transactions[t].inputs.length;i++) - { - //console.log("(b, t, i) = %s, %s, %s", b, t, i); - let input = transactions[t].inputs[i].split(":"); - inputsSum += (parseInt(input[0])*Math.pow(10, parseInt(input[1]))); - } - - // get issuer - let issuer = transactions[t].issuers[0]; - - // Push issuer inputs - if (typeof(cache.pub_index[issuer]) == 'undefined') - { - cache.pubkeys.push({ - join: cache.blockchain.length+1, - pub: issuer, - balance: inputsSum, - balanceWithOthers: new Array(), - pubkeyBalanceWithOthers: new Array(), - inputsTime: new Array(parseInt(newBlocks[b].medianTime)), - inputsAmount: new Array(inputsSum), - outputsTime: new Array(), - outputsAmount: new Array(), - writtenCerts: null, // tab of blocks number (only for members pubkeys) - receivedCerts: null // tab of blocks number (only for members pubkeys) - }); - cache.pub_index[issuer] = cache.pubkeys.length-1; - //console.log("w : issuer first : cache.pub_index[%s] = %s", issuer, cache.pub_index[issuer]); // DEBUG - } - else - { - let pubkeyId = cache.pub_index[issuer]; - if (cache.pubkeys[pubkeyId].inputsTime[cache.pubkeys[pubkeyId].inputsTime.length-1] == parseInt(newBlocks[b].medianTime)) - { - cache.pubkeys[pubkeyId].inputsAmount[cache.pubkeys[pubkeyId].inputsAmount.length-1] += inputsSum; - cache.pubkeys[pubkeyId].balance -= inputsSum; - } - else - { - //console.log("cache.pubkeys[%s] = %s", pubkeyId, cache.pubkeys[pubkeyId].pub); // DEBUG - cache.pubkeys[pubkeyId].inputsTime.push(parseInt(newBlocks[b].medianTime)); - cache.pubkeys[pubkeyId].inputsAmount.push(inputsSum); - cache.pubkeys[pubkeyId].balance -= inputsSum; - } - } - - // split and push outputs - for (let o=0;o<transactions[t].outputs.length;o++) - { - let output = transactions[t].outputs[o].split(":"); - let ouputAmout = parseInt(output[0])*Math.pow(10, parseInt(output[1])); - - // get receiver pubkey - let pubkey = output[2].substr(4, output[2].length-5); - - if (typeof(cache.pub_index[pubkey]) == 'undefined') - { - cache.pubkeys.push({ - join: cache.blockchain.length+1, - pub: pubkey, - balance: ouputAmout, - balanceWithOthers: new Array(), - pubkeyBalanceWithOthers: new Array(), - inputsTime: new Array(), - inputsAmount: new Array(), - outputsTime: new Array(), - outputsAmount: new Array(), - writtenCerts: null, // tab of blocks number (only for members pubkeys) - receivedCerts: null // tab of blocks number (only for members pubkeys) - }); - cache.pub_index[pubkey] = cache.pubkeys.length-1; - cache.pubkeys[cache.pubkeys.length-1].outputsTime.push(parseInt(newBlocks[b].medianTime)); - cache.pubkeys[cache.pubkeys.length-1].outputsAmount.push(ouputAmout); - } - else - { - let pubkeyId = cache.pub_index[pubkey]; - cache.pubkeys[pubkeyId].balance += ouputAmout; - if (pubkey == issuer) - { - cache.pubkeys[pubkeyId].inputsAmount[cache.pubkeys[pubkeyId].inputsAmount.length-1] -= ouputAmout; - ouputAmout = 0; - } - else - { - if (cache.pubkeys[pubkeyId].outputsTime[cache.pubkeys[pubkeyId].outputsTime.length-1] == parseInt(newBlocks[b].medianTime)) - { - cache.pubkeys[pubkeyId].outputsAmount[cache.pubkeys[pubkeyId].outputsAmount.length-1] += ouputAmout; - } - else - { - cache.pubkeys[pubkeyId].outputsTime.push(parseInt(newBlocks[b].medianTime)); - cache.pubkeys[pubkeyId].outputsAmount.push(ouputAmout); - } - } - } - - // Push balanceWithOthers - let pubkeyId = cache.pub_index[pubkey]; - if ( typeof(cache.pubkeys[cache.pub_index[issuer]].pubkeyBalanceWithOthers[pubkey]) != 'undefined') - { - let idBalanceKey = cache.pubkeys[cache.pub_index[issuer]].pubkeyBalanceWithOthers[pubkey]; - cache.pubkeys[cache.pub_index[issuer]].balanceWithOthers[idBalanceKey] -= ouputAmout; - } - else if ( typeof(cache.pubkeys[pubkeyId].pubkeyBalanceWithOthers[issuer]) != 'undefined') - { - let idBalanceKey = cache.pubkeys[pubkeyId].pubkeyBalanceWithOthers[issuer]; - cache.pubkeys[pubkeyId].balanceWithOthers[idBalanceKey] += ouputAmout; - } - else - { - cache.pubkeys[pubkeyId].balanceWithOthers.push(ouputAmout); - cache.pubkeys[pubkeyId].pubkeyBalanceWithOthers[issuer] = cache.pubkeys[pubkeyId].balanceWithOthers.length-1; - } - - // If receiver pubkey balance <= 1,00, destroy money - if ( cache.pubkeys[cache.pub_index[pubkey]].balance < 100) - { - cache.destroyAmount += cache.pubkeys[cache.pub_index[pubkey]].balance; - cache.pubkeys[cache.pub_index[pubkey]].outputsAmount[cache.pubkeys[cache.pub_index[pubkey]].outputsAmount.length-1] -= cache.pubkeys[cache.pub_index[pubkey]].balance; - cache.pubkeys[cache.pub_index[pubkey]].balance = 0; - } - } - - // If issuer pubkey balance <= 1,00, destroy money - if ( cache.pubkeys[cache.pub_index[issuer]].balance < 100) - { - cache.destroyAmount += cache.pubkeys[cache.pub_index[issuer]].balance; - cache.pubkeys[cache.pub_index[issuer]].inputsAmount[cache.pubkeys[cache.pub_index[issuer]].inputsAmount.length-1] += cache.pubkeys[cache.pub_index[issuer]].balance; - cache.pubkeys[cache.pub_index[issuer]].balance = 0; - } - } - */ // parse and split revoked revoked = JSON.parse(newBlocks[b].revoked); @@ -370,32 +198,6 @@ module.exports = (req, res, next) => co(function *() { revoked[r] = revoked[r].split(":"); delIdtys.push(revoked[r][0]); let tmpPubIndex = cache.pub_index[revoked[r][0]]; - /*// Suppr receivers of writtenCerts - for (let wc=0;wc<cache.pubkeys[tmpPubIndex].writtenCerts.length;wc++) - { - let tmpPubIndex2 = cache.pub_index[cache.pubkeys[tmpPubIndex].writtenCerts[wc][1]]; - for (let wc2=0;wc2<cache.pubkeys[tmpPubIndex2].receivedCerts.length;wc2++) - { - if ( cache.pubkeys[tmpPubIndex2].receivedCerts[wc2][1] == revoked[r][0] ) - { - cache.pubkeys[tmpPubIndex2].receivedCerts.splice(wc2,1); - wc2 = cache.pubkeys[tmpPubIndex2].receivedCerts.length; - } - } - } - // Suppr issuers of receivedCerts - for (let rc=0;rc<cache.pubkeys[tmpPubIndex].receivedCerts.length;rc++) - { - let tmpPubIndex2 = cache.pub_index[cache.pubkeys[tmpPubIndex].receivedCerts[rc][1]]; - for (let rc2=0;rc2<cache.pubkeys[tmpPubIndex2].writtenCerts.length;rc2++) - { - if ( cache.pubkeys[tmpPubIndex2].writtenCerts[rc2][1] == revoked[r][0] ) - { - cache.pubkeys[tmpPubIndex2].writtenCerts.splice(rc2,1); - rc2 = cache.pubkeys[tmpPubIndex2].writtenCerts.length; - } - } - }*/ //cache.pubkeys.splice(tmpPubIndex,1); } diff --git a/lib/webserver.js b/lib/webserver.js index f8dfb79cefa6da0223fb923d7ade2568522f216a..567b5acc4343c27b04e1a06c14f8a3c120aa05a6 100755 --- a/lib/webserver.js +++ b/lib/webserver.js @@ -13,7 +13,7 @@ const bodyParser = require('body-parser'); const routes = require(__dirname + '/../routes'); const tpl = require(__dirname + '/tplit.js'); -module.exports = (host, port, appParente, duniterServer, cache) => { +module.exports = (host, port, appParente, duniterServer, monitDatasPath, offset, cache) => { var app = express(); @@ -31,13 +31,31 @@ module.exports = (host, port, appParente, duniterServer, cache) => { app.set('view engine', 'html') // register the template engine app.locals.duniterServer = duniterServer + app.locals.monitDatasPath = monitDatasPath app.locals.currencyName = duniterServer.conf.currency + app.locals.offset = offset app.locals.cache = cache app.locals.HTML_HEAD = fs.readFileSync(__dirname + '/../views/HEAD.html', 'utf-8') app.locals.HTML_TOR_HEAD = fs.readFileSync(__dirname + '/../views/TOR_HEAD.html', 'utf-8') app.use( routes ) + + /*************************************** + * CSV des membres calculants + ***************************************/ + app.get('/csvCalculatorsRank', function(req, res) { + let files = fs.readdirSync(monitDatasPath + '/calculators_rank/') + let maxTimestamp = 0 + for (let file of files) { + let fileTimestamp = parseInt(file.split('_')[2]) + if (fileTimestamp > maxTimestamp) { + maxTimestamp = fileTimestamp + } + } + var file = monitDatasPath + '/calculators_rank/calculators_rank_' + maxTimestamp + '.csv'; + res.download(file); // Set disposition and send it. + }); // Si l'on ne dispose pas d'un serveur web parent, lancer notre propre serveur web if ( appParente == null ) diff --git a/package.json b/package.json index 2e9df9aede7c3bb7ed69a0eb53213b1d6a538d03..79fb8dcd3616c8b259dfcffa90ef6cb460e8fb98 100755 --- a/package.json +++ b/package.json @@ -1,29 +1,24 @@ { "name": "duniter-currency-monit", - "version": "0.3.7", + "version": "0.4.11", "main": "index.js", "license": "AGPLv3", "dependencies": { - "body-parser": "1.15.1", - "co": "^4.6.0", - "express": "4.13.4", + "body-parser": "1.17.1", + "co": "4.6.0", + "express": "4.15.2", "fs-extra": "^3.0.1", "http": "0.0.0", - "morgan": "1.7.0", - "node-pre-gyp": "^0.6.34", + "morgan": "1.8.1", + "node-pre-gyp": "0.6.34", "q": "1.5.0", - "request": "^2.81.0", - "request-promise": "4.2.0", - "wotb": "^0.6.2", + "request": "2.81.0", + "request-promise": "4.2.0" }, "devDependencies": { - "duniter": "1.3.14", - "duniter-bma": "1.3.x", - "duniter-crawler": "1.3.x", - "duniter-keypair": "1.3.x", - "duniter-prover": "1.3.x" + "duniter": "1.6.x" }, "peerDependencies": { - "duniter": "1.3.14" + "duniter": "1.6.x" } } diff --git a/readme.md b/readme.md index 0ff25800dbd2de88ad8dbe4170b0bfc265a4e701..91ee838f004844367e4d56848c3bd72a9bde151c 100644 --- a/readme.md +++ b/readme.md @@ -1,14 +1,35 @@ -# Currency-Monit +# Currency-Monit Module -Requires Node.js v6 +Requires Duniter 1.5.4 or higher ## Installation - git clone https://github.com/librelois/duniter-currency-monit.git - cd duniter-currency-monit - npm install - node index.js config --autoconf - node index.js sync g1.duniter.org 10901 - node index.js currency-monit +**Warning: only tar.gz format works ! Don't use zip format.** -Then, visit http://localhost:10501. +### If you use web-ui, install it very easy: + +1. go to localhost:9220/#/main/settings/modules +and at the foot of page enter: +```bash +duniter-currency-monit@0.4.5 +``` + +then clik to button INSTALL THIS MODULE + +2. restart your duniter node + +### If you don't use web-ui: + +1. download and uncompress archive at the location of your choice +2. plug the plugin to your duniter node: + +```bash +duniter plug duniter-currency-monit@0.4.5 +``` + +3. Stop your duniter node and restart it in the following method: +```bash +duniter currency-monit [host] [port] +``` + +then visit `host:port` (default is `localhost:10500`) \ No newline at end of file diff --git a/release.sh b/release.sh new file mode 100755 index 0000000000000000000000000000000000000000..12e1be47d934aabdef17c2600bcaec9cdd87c85a --- /dev/null +++ b/release.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +current=`grep -P "version\": \"\d+.\d+.\d+(\w*)" package.json | grep -oP "\d+.\d+.\d+(\w*)"` +echo "Current version: $current" + +if [[ $1 =~ ^[0-9]+.[0-9]+.[0-9]+((a|b)[0-9]+)?$ ]]; then + # Change the version in package.json and test file + sed -i "s/version\": \"$current/version\": \"$1/g" package.json + sed -i "s/$current/$1/g" views/about.html + + # Commit + git reset HEAD + git add package.json views/about.html + git commit -m "v$1" + git tag "v$1" +else + echo "Wrong version format" +fi diff --git a/routes/blockCount.js b/routes/blockCount.js index 097c5cf06b490b0b08c05aa7de3c3a00b0185c2d..80316026e81de396b59a71a69343c6fee20d1b73 100755 --- a/routes/blockCount.js +++ b/routes/blockCount.js @@ -1,8 +1,10 @@ "use strict"; const co = require('co') +const fs = require('fs') const timestampToDatetime = require(__dirname + '/../lib/timestampToDatetime') const colorScale = require(__dirname + '/../lib/colorScale') +const getLang = require(__dirname + '/../lib/getLang') // Garder l'index des blocs en mémoire vive var blockchain = []; @@ -11,7 +13,7 @@ var previousBlockchainTime= 0; module.exports = (req, res, next) => co(function *() { - var { duniterServer } = req.app.locals + var { duniterServer, monitDatasPath } = req.app.locals try { // get GET parameters @@ -22,6 +24,9 @@ module.exports = (req, res, next) => co(function *() { var data = req.query.data || 'nbBlocks'; var perNode = (req.query.perNode == 'yes') ? 'yes':'no'; var significantPercent = req.query.significantPercent || 3; + + // get lg file + const LANG = getLang(`${__dirname}/../lg/blockCount_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`); // detect fork if ( blockchain.length > 0 ) @@ -78,17 +83,17 @@ module.exports = (req, res, next) => co(function *() { tabBlockMembers.push ({ uid: idtys[i].uid, pubkey: idtys[i].pub, - becomeMember: (tmpBecomeMember[0] > begin) ? tmpBecomeMember[0]:begin, - coreCount: 0, - blockCount: 0, + becomeMember: (tmpBecomeMember[0] > begin) ? tmpBecomeMember[0]:begin, + coreCount: 0, + blockCount: 0, data: 0 }); // initialize tabBlockCountPerNode and tabDataPerNode if (perNode == 'yes') { - tabCoreCountPerNode.push(new Array()); - tabBlockCountPerNode.push(new Array()); - tabDataPerNode.push(new Array()); + tabCoreCountPerNode.push(new Array()); + tabBlockCountPerNode.push(new Array()); + tabDataPerNode.push(new Array()); } } @@ -97,40 +102,77 @@ module.exports = (req, res, next) => co(function *() { { for (let m=0;m<tabBlockMembers.length;m++) { - for (let n=0;n<9;n++) - { - tabCoreCountPerNode[m].push(0); - tabBlockCountPerNode[m].push(0); - if (data == 'meanNonce') { tabDataPerNode[m].push(0); } - } + for (let n=0;n<9;n++) + { + tabCoreCountPerNode[m].push(0); + tabBlockCountPerNode[m].push(0); + if (data == 'meanNonce') { tabDataPerNode[m].push(0); } + } } } + + // Open a write stream into a new calculators_rank file + let pathToCalculatorsRankFile = monitDatasPath + 'calculators_rank/' + let calculatorsRankFilename = 'calculators_rank_' + Date.now() + '.csv' + if (!fs.existsSync(pathToCalculatorsRankFile)) { fs.mkdirSync(pathToCalculatorsRankFile) } + var ws = fs.createWriteStream(pathToCalculatorsRankFile + calculatorsRankFilename, { + flags: 'a', + encoding: 'utf8', + fd: null, + mode: 0o666, + autoClose: true + }); // Calculate the sum of blocks and their nonce and number of core + let calculatorsCount = 1; for (let b=begin;b<blockchain.length;b++) { for (let m=0;m<tabBlockMembers.length;m++) { if (tabBlockMembers[m].pubkey == blockchain[b].issuer) { - tabBlockMembers[m].blockCount++; - let nonce = parseInt((blockchain[b].nonce).toString().substr(3)); - if (data == 'meanNonce') { tabBlockMembers[m].data += nonce; } - - let idCore = parseInt((blockchain[b].nonce).toString().substr(1, 2)); - tabBlockMembers[m].coreCount = (tabBlockMembers[m].coreCount < idCore) ? idCore:tabBlockMembers[m].coreCount; - - let idNode = parseInt((blockchain[b].nonce).toString().substr(0, 1)); - if (perNode == 'yes') - { - maxIdNode = (idNode > maxIdNode) ? idNode:maxIdNode; - tabCoreCountPerNode[m][idNode-1] = (tabCoreCountPerNode[m][idNode-1] < idCore) ? idCore:tabCoreCountPerNode[m][idNode-1]; - tabBlockCountPerNode[m][idNode-1]++; - if (data == 'meanNonce') { tabDataPerNode[m][idNode-1] += nonce; } - } + if (tabBlockMembers[m].blockCount == 0) { + //console.log("%s, %s, #%s, %s", calculatorsCount, tabBlockMembers[m].uid, b, timestampToDatetime(blockchain[b].medianTime)); + ws.write(calculatorsCount + ', ' + tabBlockMembers[m].uid + ', #' + b + ', ' + timestampToDatetime(blockchain[b].medianTime) + '\r\n'); + calculatorsCount++; + } + + tabBlockMembers[m].blockCount++; + let nonce = parseInt((blockchain[b].nonce).toString().substr(3)); + if (data == 'meanNonce') { tabBlockMembers[m].data += nonce; } + + let idCore = parseInt((blockchain[b].nonce).toString().substr(1, 2)); + tabBlockMembers[m].coreCount = (tabBlockMembers[m].coreCount < idCore) ? idCore:tabBlockMembers[m].coreCount; + + let idNode = parseInt((blockchain[b].nonce).toString().substr(0, 1)); + if (perNode == 'yes') + { + maxIdNode = (idNode > maxIdNode) ? idNode:maxIdNode; + tabCoreCountPerNode[m][idNode-1] = (tabCoreCountPerNode[m][idNode-1] < idCore) ? idCore:tabCoreCountPerNode[m][idNode-1]; + tabBlockCountPerNode[m][idNode-1]++; + if (data == 'meanNonce') { tabDataPerNode[m][idNode-1] += nonce; } + } } } } + + // Close write stream into calculators_rank file + ws.end(); + + // Remove oldest calculators_rank file + let files = fs.readdirSync(pathToCalculatorsRankFile); + if (files.length > 10) { + let minTimestamp = parseInt(Date.now()) + for (let file of files) { + let fileTimestamp = parseInt(file.split('_')[2]) + if (fileTimestamp < minTimestamp) { + minTimestamp = fileTimestamp; + } + } + fs.unlink(pathToCalculatorsRankFile + 'calculators_rank_' + minTimestamp + '.csv',function(err){ + if(err) return console.log(err); + }); + } // Delete non-significant nodes // A node is considered as significant if its blockCount represents more than 3 % of the total member blockCount @@ -140,24 +182,24 @@ module.exports = (req, res, next) => co(function *() { for (let m=0;m<tabBlockMembers.length;m++) { let significantLimit = parseInt(tabBlockMembers[m].blockCount * significantPercent / 100); - for (let n=0;n<maxIdNode;n++) - { - if (tabBlockCountPerNode[m][n] <= significantLimit) - { - tabBlockMembers[m].blockCount -= tabBlockCountPerNode[m][n]; - tabCoreCountPerNode[m][n] = 0; - tabBlockCountPerNode[m][n] = 0; - if (data == 'meanNonce') - { - tabBlockMembers[m].data -= tabDataPerNode[m][n]; - tabDataPerNode[m][n] = 0; - } - } - else if (tabBlockCountPerNode[m][n] > 0) + for (let n=0;n<maxIdNode;n++) { - maxSignificantIdNode = ((n+1) > maxSignificantIdNode) ? (n+1):maxSignificantIdNode; - } - } + if (tabBlockCountPerNode[m][n] <= significantLimit) + { + tabBlockMembers[m].blockCount -= tabBlockCountPerNode[m][n]; + tabCoreCountPerNode[m][n] = 0; + tabBlockCountPerNode[m][n] = 0; + if (data == 'meanNonce') + { + tabBlockMembers[m].data -= tabDataPerNode[m][n]; + tabDataPerNode[m][n] = 0; + } + } + else if (tabBlockCountPerNode[m][n] > 0) + { + maxSignificantIdNode = ((n+1) > maxSignificantIdNode) ? (n+1):maxSignificantIdNode; + } + } } } @@ -166,38 +208,38 @@ module.exports = (req, res, next) => co(function *() { { if (data == 'nbBlocks') { - tabBlockMembers[m].data = tabBlockMembers[m].blockCount; - if (perNode == 'yes') { - for (let n=0;n<maxSignificantIdNode;n++) { tabDataPerNode[m].push(tabBlockCountPerNode[m][n]); } - } + tabBlockMembers[m].data = tabBlockMembers[m].blockCount; + if (perNode == 'yes') { + for (let n=0;n<maxSignificantIdNode;n++) { tabDataPerNode[m].push(tabBlockCountPerNode[m][n]); } + } } else if (data == 'writtenPercent') { - tabBlockMembers[m].data = parseFloat( ((tabBlockMembers[m].blockCount * 100) / (blockchain.length-begin)).toFixed(2) ); - if (perNode == 'yes') { - for (let n=0;n<maxSignificantIdNode;n++) { - tabDataPerNode[m].push( parseFloat( ((tabBlockCountPerNode[m][n] * 100) / (blockchain.length-begin)).toFixed(2) ) ); - } - } + tabBlockMembers[m].data = parseFloat( ((tabBlockMembers[m].blockCount * 100) / (blockchain.length-begin)).toFixed(2) ); + if (perNode == 'yes') { + for (let n=0;n<maxSignificantIdNode;n++) { + tabDataPerNode[m].push( parseFloat( ((tabBlockCountPerNode[m][n] * 100) / (blockchain.length-begin)).toFixed(2) ) ); + } + } } else if (data == 'meanNonce' && tabBlockMembers[m].blockCount > 0) { - tabBlockMembers[m].data = parseInt( (tabBlockMembers[m].data / (tabBlockMembers[m].blockCount)).toFixed(0) ); - if (perNode == 'yes') { - for (let n=0;n<maxSignificantIdNode;n++) { - tabDataPerNode[m][n] = parseInt( (tabDataPerNode[m][n] / (tabBlockCountPerNode[m][n])).toFixed(0) ); - } - } + tabBlockMembers[m].data = parseInt( (tabBlockMembers[m].data / (tabBlockMembers[m].blockCount)).toFixed(0) ); + if (perNode == 'yes') { + for (let n=0;n<maxSignificantIdNode;n++) { + tabDataPerNode[m][n] = parseInt( (tabDataPerNode[m][n] / (tabBlockCountPerNode[m][n])).toFixed(0) ); + } + } } else if (data == 'writtenPercentSinceBecomeMember') { - let nbBlockwithThisMember = (tabBlockMembers[m].becomeMember > begin) ? (blockchain.length-tabBlockMembers[m].becomeMember) : (blockchain.length-begin); - tabBlockMembers[m].data = parseFloat( ((tabBlockMembers[m].blockCount * 100) / nbBlockwithThisMember).toFixed(2) ); - if (perNode == 'yes') { - for (let n=0;n<maxSignificantIdNode;n++) { - tabDataPerNode[m].push( parseFloat( ((tabBlockCountPerNode[m][n] * 100) / nbBlockwithThisMember).toFixed(2) ) ); - } - } + let nbBlockwithThisMember = (tabBlockMembers[m].becomeMember > begin) ? (blockchain.length-tabBlockMembers[m].becomeMember) : (blockchain.length-begin); + tabBlockMembers[m].data = parseFloat( ((tabBlockMembers[m].blockCount * 100) / nbBlockwithThisMember).toFixed(2) ); + if (perNode == 'yes') { + for (let n=0;n<maxSignificantIdNode;n++) { + tabDataPerNode[m].push( parseFloat( ((tabBlockCountPerNode[m][n] * 100) / nbBlockwithThisMember).toFixed(2) ) ); + } + } } } @@ -227,22 +269,22 @@ module.exports = (req, res, next) => co(function *() { tabBlockMembersSort.push({ uid: tabBlockMembers[idMax].uid, pubkey: tabBlockMembers[idMax].pub, - becomeMember: tabBlockMembers[idMax].becomeMember, - coreCount: tabBlockMembers[m].coreCount, - coreCountPerNode: (perNode == 'yes') ? tabCoreCountPerNode[idMax]:null, - blockCount: tabBlockMembers[idMax].blockCount, - blockCountPerNode: (perNode == 'yes') ? tabBlockCountPerNode[idMax]:null, - data: tabBlockMembers[idMax].data, - dataPerNode: (perNode == 'yes') ? tabDataPerNode[idMax]:null + becomeMember: tabBlockMembers[idMax].becomeMember, + coreCount: tabBlockMembers[m].coreCount, + coreCountPerNode: (perNode == 'yes') ? tabCoreCountPerNode[idMax]:null, + blockCount: tabBlockMembers[idMax].blockCount, + blockCountPerNode: (perNode == 'yes') ? tabBlockCountPerNode[idMax]:null, + data: tabBlockMembers[idMax].data, + dataPerNode: (perNode == 'yes') ? tabDataPerNode[idMax]:null }); tabExcluded.push(tabBlockMembers[idMax].uid); } //define dataLabel - var dataLabel = '#Written blocks'; - if (data == 'writtenPercent') { dataLabel = "\% blockchain"; } - else if (data == 'writtenPercentSinceBecomeMember') { dataLabel = "\% blockchain (since become member)"; } - else if (data == 'meanNonce') { dataLabel = '#Mean nonce'; } + var dataLabel = '#'+LANG['WRITTEN_BLOCKS']; + if (data == 'writtenPercent') { dataLabel = "\% "+LANG['BLOCKCHAIN']; } + else if (data == 'writtenPercentSinceBecomeMember') { dataLabel = "\% "+LANG['BLOCKCHAIN']+" ("+LANG['SINCE_BECOME_MEMBER']+")"; } + else if (data == 'meanNonce') { dataLabel = '#'+LANG['MEAN_NONCE']; } // Si le client demande la réponse au format JSON, le faire if (format == 'JSON') @@ -260,98 +302,98 @@ module.exports = (req, res, next) => co(function *() { for (let n=0;n<maxIdNode;n++) { tabDataXperNode.push(new Array()); } for (let m=0;m<tabBlockMembersSort.length;m++) { - if (tabBlockMembersSort[m].data > 0) - { - if (perNode == 'yes') - { - tabLabels.push(tabBlockMembersSort[m].uid+"("); - for (let n=0;n<maxSignificantIdNode;n++) - { - tabDataXperNode[n].push(tabBlockMembersSort[m].dataPerNode[n]); - if (tabBlockMembersSort[m].coreCountPerNode[n] > 0) { tabLabels[tabLabels.length-1] += tabBlockMembersSort[m].coreCountPerNode[n]+"c,"; } - } - tabLabels[tabLabels.length-1] = tabLabels[tabLabels.length-1].substr(0, tabLabels[tabLabels.length-1].length-1); - tabLabels[tabLabels.length-1] += ")"; - } - else - { - tabLabels.push(tabBlockMembersSort[m].uid/*+"("+tabBlockMembersSort[m].coreCount+"c)"*/); - tabDataX.push(tabBlockMembersSort[m].data); - } - nbMembers++; - } + if (tabBlockMembersSort[m].data > 0) + { + if (perNode == 'yes') + { + tabLabels.push(tabBlockMembersSort[m].uid+"("); + for (let n=0;n<maxSignificantIdNode;n++) + { + tabDataXperNode[n].push(tabBlockMembersSort[m].dataPerNode[n]); + if (tabBlockMembersSort[m].coreCountPerNode[n] > 0) { tabLabels[tabLabels.length-1] += tabBlockMembersSort[m].coreCountPerNode[n]+"c,"; } + } + tabLabels[tabLabels.length-1] = tabLabels[tabLabels.length-1].substr(0, tabLabels[tabLabels.length-1].length-1); + tabLabels[tabLabels.length-1] += ")"; + } + else + { + tabLabels.push(tabBlockMembersSort[m].uid); + tabDataX.push(tabBlockMembersSort[m].data); + } + nbMembers++; + } } var datasets = [ [] ]; if (perNode == 'yes') { - for (let n=0;n<maxSignificantIdNode;n++) - { - datasets.push({ - label: dataLabel, - data: tabDataXperNode[n], - backgroundColor: colorScale(nbMembers, 0.5), - borderWidth: 0, - hoverBackgroundColor: colorScale(nbMembers, 0.2) - }); - } + for (let n=0;n<maxSignificantIdNode;n++) + { + datasets.push({ + label: dataLabel, + data: tabDataXperNode[n], + backgroundColor: colorScale(nbMembers, 0.5), + borderWidth: 0, + hoverBackgroundColor: colorScale(nbMembers, 0.2) + }); + } } else { - datasets = [{ - label: dataLabel, - data: tabDataX, - backgroundColor: colorScale(nbMembers, 0.5), - borderColor: colorScale(nbMembers, 1.0), - borderWidth: 1, - hoverBackgroundColor: colorScale(nbMembers, 0.2), - hoverBorderColor: colorScale(nbMembers, 0.2) + datasets = [{ + label: dataLabel, + data: tabDataX, + backgroundColor: colorScale(nbMembers, 0.5), + borderColor: colorScale(nbMembers, 1.0), + borderWidth: 1, + hoverBackgroundColor: colorScale(nbMembers, 0.2), + hoverBorderColor: colorScale(nbMembers, 0.2) }]; } res.locals = { - host: req.headers.host.toString(), - tabBlockMembersSort, - begin, - end, - help, - data, - perNode, - description: ``, - chart: { - type: 'bar', - data: { - labels: tabLabels, - datasets: datasets - }, - options: { - title: { - display: true, - text: nbMembers+' members have written blocks in the range #'+begin+'-#'+end - }, - legend: { - display: false - }, - scales: { - yAxes: [{ - ticks: { - beginAtZero: true, - } - }] - }, - categoryPercentage: 1.0, - barPercentage: 1.0 - } - }, - form: `Begin #<input type="number" name="begin" value="${begin}" size="7" style="width:60px;"> - End #<input type="number" name="end" value="${end}" size="7" style="width:60px;"> - <select name="data"> - <option name="data" value ="nbBlocks">Number of written Blocks - <option name="data" value ="writtenPercent" ${data == 'writtenPercent' ? 'selected' : ''}>Percent of written Blocks - <option name="data" value ="writtenPercentSinceBecomeMember" ${data == 'writtenPercentSinceBecomeMember' ? 'selected' : ''}>Percent of written Blocks since become member - <option name="data" value ="meanNonce" ${data == 'meanNonce' ? 'selected' : ''}>mean nonce - </select> - <input type="checkbox" name="perNode" value="yes" ${perNode == 'yes' ? 'checked' : ''}>detail by node - - significant limit <input type="number" name="significantPercent" value="${significantPercent}" size="2" style="width:30px;">% of blocks` + host: req.headers.host.toString(), + tabBlockMembersSort, + begin, + end, + help, + data, + perNode, + description: ``, + chart: { + type: 'bar', + data: { + labels: tabLabels, + datasets: datasets + }, + options: { + title: { + display: true, + text: nbMembers+' '+LANG["RANGE"]+' #'+begin+'-#'+end + }, + legend: { + display: false + }, + scales: { + yAxes: [{ + ticks: { + beginAtZero: true, + } + }] + }, + categoryPercentage: 1.0, + barPercentage: 1.0 + } + }, + form: `${LANG['BEGIN']} #<input type="number" name="begin" value="${begin}" size="7" style="width:60px;"> - ${LANG['END']} #<input type="number" name="end" value="${end}" size="7" style="width:60px;"> + <select name="data"> + <option name="data" value ="nbBlocks">${LANG["NB_BLOCKS"]} + <option name="data" value ="writtenPercent" ${data == 'writtenPercent' ? 'selected' : ''}>${LANG["PERCENT_OF_WRITTEN_BLOCKS"]} + <option name="data" value ="writtenPercentSinceBecomeMember" ${data == 'writtenPercentSinceBecomeMember' ? 'selected' : ''}>${LANG["PERCENT_OF_WRITTEN_BLOCKS"]} ${LANG["SINCE_BECOME_MEMBER"]} + <option name="data" value ="meanNonce" ${data == 'meanNonce' ? 'selected' : ''}>${LANG['MEAN_NONCE']} + </select> + <input type="checkbox" name="perNode" value="yes" ${perNode == 'yes' ? 'checked' : ''}>${LANG['DETAIL_BY_NODE']} - + ${LANG['SIGNIFICANT_LIMIT']} <input type="number" name="significantPercent" value="${significantPercent}" size="2" style="width:30px;">${LANG['PERCENT_OF_BLOCKS']}` } next() } diff --git a/routes/gaussianWotQuality.js b/routes/gaussianWotQuality.js new file mode 100644 index 0000000000000000000000000000000000000000..d8b6d370942fff4934614f6a31b7ac291e1f8ddd --- /dev/null +++ b/routes/gaussianWotQuality.js @@ -0,0 +1,241 @@ +"use strict"; + +const co = require('co') + +const constants = require(__dirname + '/../lib/constants') +const membersQuality = require(__dirname + '/tools/membersQuality') +const getLang = require(__dirname + '/../lib/getLang') + +// gaussianWotQuality cache +var previousNextYn = "no"; + +module.exports = (req, res, next) => co(function *() { + + var { duniterServer } = req.app.locals + + try { + // get GET parameters + const format = req.query.format || 'HTML'; + const sentries = req.query.sentries || 'yes'; + const unit = req.query.unit || 'quality'; + const nextYn = (req.query.nextYn=="yes") ? "yes":"no"; + + // get lg file + const LANG = getLang(`${__dirname}/../lg/gaussianWotQuality_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`); + + // Définition des contantes + const conf = duniterServer.conf; + const qualityMax = (1/conf.xpercent); + + // Définition des variables + let lastUpgradeTimeDatas = membersQuality(constants.QUALITY_CACHE_ACTION.INIT); + let tabUidIndex = []; + let tabMembersQuality= []; + let tabMembersQualitySorted = []; + let tabLabels = []; + let tabColors = []; + let tabLimit1 = []; + + // Récupérer la liste des identités ayant actuellement le statut de membre + let membersList = yield duniterServer.dal.peerDAL.query('SELECT `uid`,`wotb_id` FROM i_index WHERE `member`=1'); + + // Si les données de qualité n'ont jamais été calculés, le faire + if (lastUpgradeTimeDatas == 0 || (lastUpgradeTimeDatas+constants.MIN_WOT_QUALITY_CACHE_UPDATE_FREQ) < (Math.floor(Date.now() / 1000)) || (previousNextYn != nextYn)) + { + // Calculer dSen + var dSen = Math.ceil(Math.pow(membersList.length, 1 / conf.stepMax)); + if (nextYn == "yes") { dSen++; } + + // récupérer la wot + const wot = duniterServer.dal.wotb; + + // Initialiser le cache des données de qualité + membersQuality(constants.QUALITY_CACHE_ACTION.INIT, 0, dSen, conf.stepMax, conf.xpercent, wot.memCopy()); + } + + // Mettre a jour previousNextYn + previousNextYn = (nextYn=="yes") ? "yes":"no"; + + // Calculer nbSentries, limit1 and label + const nbSentries = (sentries=="no") ? membersList.length:membersQuality(constants.QUALITY_CACHE_ACTION.GET_SENTRIES_COUNT); + let limit1 = 1; + let label = LANG['QUALITY']; + switch (unit) + { + case 'percentReached': limit1 = conf.xpercent*100; label = LANG['PERCENT_REACHED']; break; + case 'nbReached': limit1 = parseInt(conf.xpercent*nbSentries); label = LANG['NB_REACHED']; break; + default: break; + } + + // Remplir les tableaux tabUidIndex et tabLimit1 + for(const member of membersList) + { + tabUidIndex[member.wotb_id] = member.uid; + tabLimit1.push(limit1); + } + + // Récupérer le tableau de qualité des membres + tabMembersQuality= []; + for (let i=0;membersQuality(constants.QUALITY_CACHE_ACTION.GET_QUALITY, i) >= 0;i++) + { + if (sentries == "no") + { + tabMembersQuality[i] = membersQuality(constants.QUALITY_CACHE_ACTION.GET_QUALITY, i, -1); + } + else + { + tabMembersQuality[i] = membersQuality(constants.QUALITY_CACHE_ACTION.GET_QUALITY, i); + } + } + + // Initialisation du tableau tabMembersQualitySorted + for (let i=0;i<tabMembersQuality.length;i++) + { + tabMembersQualitySorted.push(0); + } + + // Trier le tableau de façon gaussienne + let debut = true; + let membersQualityAlreadyCounted = []; + for (let i=0;i<tabMembersQuality.length;i++) + { + let min = qualityMax; + let idMin = 0; + + for (let j=0;j<tabMembersQuality.length;j++) + { + if (tabMembersQuality[j] < min && typeof(membersQualityAlreadyCounted[j])=='undefined') + { + min = tabMembersQuality[j]; + idMin = j; + } + } + + // Remplir les tableaux triée de façon gaussienne + let idGaussian = parseInt(i/2); + if(!debut) + { + idGaussian = parseInt(tabMembersQuality.length-(i/2)); + } + debut = !debut; + tabMembersQualitySorted[idGaussian] = tabMembersQuality[idMin]; + + // Exclure les membres déjà traités + membersQualityAlreadyCounted[idMin] = true; + + // Définir le label pour cet abscisse + tabLabels[idGaussian] = tabUidIndex[idMin]; + + // Définir la couleur + if (tabMembersQuality[idMin] >= 1.10) + { + tabColors[idGaussian] = 'rgba(128, 0, 128, 0.5)'; + } + else if (tabMembersQuality[idMin] >= 1.05) + { + tabColors[idGaussian] = 'rgba(0, 0, 255, 0.5)'; + } + else if (tabMembersQuality[idMin] >= 1.00) + { + tabColors[idGaussian] = 'rgba(0, 255, 0, 0.5)'; + } + else if (tabMembersQuality[idMin] >= 0.95) + { + tabColors[idGaussian] = 'rgba(255, 128, 0, 0.5)'; + } + else if (tabMembersQuality[idMin] >= 0.90) + { + tabColors[idGaussian] = 'rgba(255, 0, 0, 0.5)'; + } + else + { + tabColors[idGaussian] = 'rgba(0, 0, 0, 0.5)'; + } + } + + // Si le client demande les données dans une autre unité, faire la transformation + let unitCoeff = 1.0; + if (unit=='percentReached' || unit=='nbReached') + { + unitCoeff = parseFloat(conf.xpercent); + if (unit=='nbReached') { unitCoeff *= parseFloat(nbSentries); } + else { unitCoeff *= 100.0; } + for (let i=0;i<tabMembersQualitySorted.length;i++) + { + tabMembersQualitySorted[i] = parseInt(tabMembersQualitySorted[i]*unitCoeff); + } + } + + // Si le client demande la réponse au format JSON, le faire + if (format == 'JSON') + { + let tabJson = []; + for (let i=0;i<tabMembersQualitySorted.length;i++) + { + tabJson.push({ + label: tabLabels[i], + quality: tabMembersQualitySorted[i] + }); + } + res.status(200).jsonp( tabJson ) + } + else + { + res.locals = { + host: req.headers.host.toString(), + form: ` + <select name="unit"> + <option name="unit" value ="quality">${LANG['QUALITY']} + <option name="unit" value ="percentReached" ${unit == 'percentReached' ? 'selected' : ''}>${LANG['PERCENT_REACHED']} + <option name="unit" value ="nbReached" ${unit == 'nbReached' ? 'selected' : ''}>${LANG['NB_REACHED']} + </select>`, + form2: `<input type="checkbox" name="sentries" value="no" ${sentries == 'no' ? 'checked' : ''}> ${LANG["IF_NO_SENTRIES"]}<br> + <input type="checkbox" name="nextYn" value="yes" ${nextYn == 'yes' ? 'checked' : ''}> ${LANG["NEXT_YN"]}`, + chart: { + type: 'bar', + data: { + labels: tabLabels, + datasets: [{ + label: label, + data: tabMembersQualitySorted, + backgroundColor: tabColors, + borderWidth: 0 + }, + { + label: 'limit', + data: tabLimit1, + backgroundColor: 'rgba(0, 255, 0, 0.5)', + borderColor: 'rgba(0, 255, 0, 1)', + borderWidth: 2, + type: 'line', + fill: false, + pointStyle: 'dash' + }] + }, + options: { + title: { + display: true, + text: LANG['DISTRIBUTION_QUALITY'] + }, + legend: { + display: false + }, + scales: { + yAxes: [{ + position: 'left', + ticks: { + min: 0, + max: parseFloat(((1/conf.xpercent)*unitCoeff).toFixed(2)) + } + }] + } + } + } + } + next() + } + } catch (e) { + // En cas d'exception, afficher le message + res.status(500).send(`<pre>${e.stack || e.message}</pre>`); + } +}) \ No newline at end of file diff --git a/routes/index.js b/routes/index.js index 921e1dde7f001f29201b30d0a9c78f056da3e139..a1cb8ec0994c22f44b3f7b770389468ec7e9ba23 100755 --- a/routes/index.js +++ b/routes/index.js @@ -1,6 +1,9 @@ const fs = require('fs') const express = require('express') + +const constants = require(__dirname + '/../lib/constants') const getLang = require(__dirname + '/../lib/getLang') +const printMenu = require(__dirname + '/../views/printMenu') var app = express.Router() @@ -25,96 +28,135 @@ Par exemple, home et about n'ont pas besoin de controleur /*************************************** * Home Page ***************************************/ -app.get('/', (req, res)=> res.render('about.html', { - host: req.headers.host.toString(), - help: req.query.help, - MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||'fr'}.txt`) - }) - ) +app.get('/', // chemin (endpoint) + (req, res)=> res.render('about.html', { // rendu (template) + host: req.headers.host.toString(), + printMenu, + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/about_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`) + }) +) /*************************************** * About Page ***************************************/ -app.get('/about', (req, res)=> res.render('about.html', { - host: req.headers.host.toString(), - help: req.query.help, - MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||'fr'}.txt`) - }) - ) +app.get('/about', // chemin (endpoint) + (req, res)=> res.render('about.html', { // rendu (template) + host: req.headers.host.toString(), + printMenu, + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/about_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`) + }) +) /*************************************** * Lister les futurs membres ***************************************/ -app.get('/willMembers', - require(__dirname + '/willMembers.js'), // the route controler - (req, res)=> // Send html page - res.status(200) // 200 n'est pas obligatoire, si on renvoie une réponse - .render('willMembers.html', { - help: req.query.help, - MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||'fr'}.txt`), - LANG: getLang(`${__dirname}/../lg/willMembers_${req.query.lg||'fr'}.txt`) - }) +app.get('/willMembers', // chemin (endpoint) + require(__dirname + '/willMembers.js'), // controleur (route) + (req, res)=> res.render('willMembers.html', { // rendu (template) + printMenu, + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/willMembers_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`) + }) ) /*************************************** * Lister les membres ***************************************/ -app.get('/members', /*require('../lib/updateCache.js'),*/ require(__dirname + '/members.js'), +app.get('/members', + require(__dirname + '/members.js'), (req, res)=> res.render('members.html', { - help: req.query.help, - MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||'fr'}.txt`), - LANG: getLang(`${__dirname}/../lg/members_${req.query.lg||'fr'}.txt`) - }) + printMenu, + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/members_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`) + }) ) /*************************************** * Lister les anciens membres ***************************************/ -/*app.get('/wasMembers', require(__dirname + '/wasMembers.js'), (req, res)=> res.render('wasMembers.html', { - help: req.query.help, - MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||'fr'}.txt`), - LANG: getLang(`${__dirname}/../lg/wasMembers_${lg}.txt`) - }) - )*/ +/*app.get('/wasMembers', + require(__dirname + '/wasMembers.js'), + (req, res)=> res.render('wasMembers.html', { + printMenu, + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/wasMembers_${lg}.txt`) + }) +)*/ /*************************************** * Évolution du nombre de membres ***************************************/ -app.get('/membersCount', require(__dirname + '/../lib/updateCache.js'), require(__dirname + '/membersCount.js'), (req, res)=> res.render('Chart.html', { - help: req.query.help, - MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||'fr'}.txt`), - LANG: getLang(`${__dirname}/../lg/membersCount_${req.query.lg||'fr'}.txt`) - }) - ) +app.get('/membersCount', + require(__dirname + '/../lib/updateCache.js'), require(__dirname + '/membersCount.js'), + (req, res)=> res.render('Chart.html', { + printMenu, + pageName: 'MEMBERS_COUNT', + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/membersCount_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`) + }) +) /*************************************** -* Lister les block en graph +* Wotex ***************************************/ -app.get('/blockCount', /*require('${__dirname}/../lib/updateCache.js'),*/ require(__dirname + '/blockCount.js'), (req, res)=> res.render('Chart.html', { - help: req.query.help, - MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||'fr'}.txt`), - LANG: getLang(`${__dirname}/../lg/blockCount_${req.query.lg||'fr'}.txt`) - }) - ) +app.get('/wotex', + require(__dirname + '/wotex.js'), + (req, res)=> res.render('wotex.html', { + printMenu, + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/wotex_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`) + }) +) + +/******************************************* +* Graphe gaussien de la qualité des membres +********************************************/ +app.get('/gaussianWotQuality', + require(__dirname + '/gaussianWotQuality.js'), + (req, res)=> res.render('Chart.html', { + printMenu, + pageName: 'GAUSSIAN_WOT_QUALITY', + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/gaussianWotQuality_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`) + }) +) /*************************************** -* Évolution de la masse monétaire totale +* Lister les block en graphe ***************************************/ -app.get('/monetaryMass', require(__dirname + '/monetaryMass.js'), (req, res)=> res.render('Chart.html', { - help: req.query.help, - MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||'fr'}.txt`), - LANG: getLang(`${__dirname}/../lg/monetaryMass_${req.query.lg||'fr'}.txt`) - }) - ) +app.get('/blockCount', + require(__dirname + '/blockCount.js'), + (req, res)=> res.render('Chart.html', { + printMenu, + pageName: 'BLOCK_COUNT', + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/blockCount_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`) + }) +) /*************************************** -* Wotex +* Évolution de la masse monétaire totale ***************************************/ -app.get('/wotex', require(__dirname + '/wotex.js'), (req, res)=> res.render('wotex.html', { - help: req.query.help, - MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||'fr'}.txt`), - LANG: getLang(`${__dirname}/../lg/wotex_${req.query.lg||'fr'}.txt`) - }) - ) +app.get('/monetaryMass', + require(__dirname + '/monetaryMass.js'), + (req, res)=> res.render('Chart.html', { + printMenu, + pageName: 'MONETARY_MASS', + help: req.query.help, + MENU_LANG: getLang(`${__dirname}/../lg/menu_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`), + LANG: getLang(`${__dirname}/../lg/monetaryMass_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`) + }) +) module.exports = app \ No newline at end of file diff --git a/routes/members.js b/routes/members.js index 713ae6c11e25bb476dca3db0d1204f72a01d172b..d5ab20d22cca2a8c258563c8e7bdbd52205b25bf 100755 --- a/routes/members.js +++ b/routes/members.js @@ -4,15 +4,18 @@ const co = require('co') const constants = require(__dirname + '/../lib/constants') -const wotb = (constants.USE_WOTB6) ? require('wotb'):null; - +const randomInt = require(__dirname + '/../lib/randomInt') const timestampToDatetime = require(__dirname + '/../lib/timestampToDatetime') +const membersQuality = require(__dirname + '/tools/membersQuality') // Préserver les résultats en cache var lockMembers = false; var membersLastUptime = 0; var previousMode = null; var previousCentrality = null; +var previousNextYn = "no"; +var previousRandomList = "no" +var previousRandomCounts = 10 var membersList = []; var membersIdentity = []; var membersFirstCertifExpire = []; @@ -24,11 +27,11 @@ var membershipsExpireTimeList = []; var nbMaxCertifs = 0; var sentries = []; var sentriesIndex = []; -var membersQualityExt = []; -var meanSentriesReachedBySentriesInSingleExtCert = 0; +var countSentries = 0; +/*var meanSentriesReachedBySentriesInSingleExtCert = 0; var meanMembersReachedBySentriesInSingleExtCert = 0; var meanSentriesReachedByMembersInSingleExtCert = 0; -var meanMembersReachedByMembersInSingleExtCert = 0; +var meanMembersReachedByMembersInSingleExtCert = 0;*/ var proportionMembersWithQualityUpper1 = 0; var proportionMembersWithQualityUpper1IfNoSentries = 0; @@ -51,13 +54,12 @@ module.exports = (req, res, next) => co(function *() { const head = yield duniterServer.dal.getCurrentBlockOrNull(); const currentBlockchainTimestamp = head ? head.medianTime : 0; const membersCount = head ? head.membersCount : 0; - const dSen = Math.ceil(Math.pow(membersCount, 1 / conf.stepMax)); + var dSen = Math.ceil(Math.pow(membersCount, 1 / conf.stepMax)); // Initaliser les variables let membersListOrdered = []; let membersCertifsListSorted = []; let tabSort = []; - let countSentries = 0; let membersNbSentriesUnreached = []; // Récupéré les paramètres @@ -65,18 +67,26 @@ module.exports = (req, res, next) => co(function *() { var mode = req.query.mode || 'received' // Valeur par défaut var order = req.query.d && req.query.order || 'desc' // Valeur par défaut var sort_by = req.query.sort_by || "idtyWritten" // Valeur par défaut - var pendingSigs = req.query.pendingSigs || "no"; // Valeur par défaut - var centrality = req.query.centrality || "no"; // Valeur par défaut - var format = req.query.format || 'HTML'; // Valeur par défaut + var pendingSigs = req.query.pendingSigs || "no"; // Valeur par défaut + var centrality = req.query.centrality || "no"; // Valeur par défaut + var format = req.query.format || 'HTML'; // Valeur par défaut + const nextYn = (req.query.nextYn=="yes") ? "yes":"no"; + const randomList = (req.query.randomList=="yes") ? "yes":"no"; + const numberOfRandomMembers = req.query.randomCounts || 10 + + // Vérifier la valeur de nextYn dans le cache + let lastUpgradeTimeDatas = membersQuality(constants.QUALITY_CACHE_ACTION.INIT); + let dSenCache = membersQuality(constants.QUALITY_CACHE_ACTION.GET_D_SEN); + if (lastUpgradeTimeDatas > 0 && dSenCache > dSen) { previousNextYn == "yes"; } // Alimenter wotb avec la toile actuelle - const wotbInstance = (constants.USE_WOTB6) ? wotb.newFileInstance(duniterServer.home + '/wotb.bin'):duniterServer.dal.wotb; + const wotbInstance = duniterServer.dal.wotb; - // Vérifier si le cache doit être Réinitialiser - let reinitCache = (Math.floor(Date.now() / 1000) > (membersLastUptime + constants.MIN_MEMBERS_UPDATE_FREQ)); + // Vérifier si le cache doit être Réinitialiser + let reinitCache = (Math.floor(Date.now() / 1000) > (membersLastUptime + constants.MIN_MEMBERS_UPDATE_FREQ)); - // Si changement de conditions, alors forcer le rechargement du cache s'il n'est pas, vérouillé, sinon forcer les conditions à celles en mémoire - if (previousMode != mode || previousCentrality != centrality) + // Si changement de conditions, alors forcer le rechargement du cache s'il n'est pas vérouillé, sinon forcer les conditions à celles en mémoire + if (previousMode != mode || previousCentrality != centrality || previousNextYn != nextYn || previousRandomList != randomList || numberOfRandomMembers != previousRandomCounts) { if (!lockMembers) { @@ -87,6 +97,9 @@ module.exports = (req, res, next) => co(function *() { { mode = previousMode; centrality = previousCentrality; + nextYn = previousNextYn; + randomList = previousRandomList; + numberOfRandomMembers = previousRandomCounts; } } // Sinon, si les conditions sont identiques : @@ -106,6 +119,9 @@ module.exports = (req, res, next) => co(function *() { membersLastUptime = Math.floor(Date.now() / 1000); previousMode = mode; previousCentrality = centrality; + previousNextYn = nextYn; + previousRandomList = randomList; + previousRandomCounts = numberOfRandomMembers; membersList = []; membersIdentity = []; membersFirstCertifExpire = []; @@ -117,13 +133,19 @@ module.exports = (req, res, next) => co(function *() { nbMaxCertifs = 0; sentries = []; sentriesIndex = []; - membersQualityExt = []; - meanSentriesReachedBySentriesInSingleExtCert = 0; + countSentries = 0; + /*meanSentriesReachedBySentriesInSingleExtCert = 0; meanMembersReachedBySentriesInSingleExtCert = 0; meanSentriesReachedByMembersInSingleExtCert = 0; - meanMembersReachedByMembersInSingleExtCert = 0; + meanMembersReachedByMembersInSingleExtCert = 0;*/ proportionMembersWithQualityUpper1 = 0; proportionMembersWithQualityUpper1IfNoSentries = 0; + + // Appliquer le paramètre nextYn + if (nextYn=="yes") { dSen++; } + + // réinitialiser le cache des données de qualité + membersQuality(constants.QUALITY_CACHE_ACTION.INIT, 0, dSen, conf.stepMax, conf.xpercent, wotbInstance.memCopy()); // Réinitialiser le cache des données de centralité if (centrality=='yes') @@ -140,6 +162,19 @@ module.exports = (req, res, next) => co(function *() { // Récupérer la liste des identités ayant actuellement le statut de membre membersList = yield duniterServer.dal.peerDAL.query('SELECT `uid`,`pub`,`member`,`written_on`,`wotb_id` FROM i_index WHERE `member`=1'); + + if (randomList == "yes") { + // Tirer au sort randomCounts membres + const maxLengthRandomMembers = Math.min(numberOfRandomMembers,membersList.length) + let randomMembers = [] + while (randomMembers.length < maxLengthRandomMembers) { + const randomInt_ = randomInt(0, membersList.length) + if (randomMembers.indexOf(membersList[randomInt_].uid) == -1) { + randomMembers.push(membersList[randomInt_]) + } + } + membersList = randomMembers + } // Récupérer pour chaque identité, le numéro du block d'écriture du dernier membership // Ainsi que la première ou dernière certification @@ -184,51 +219,27 @@ module.exports = (req, res, next) => co(function *() { let tmpWot = wotbInstance.memCopy(); // Récupérer les informations détaillés de distance pour le membre courant - let detailedDistance = null; - if (constants.USE_WOTB6) + let detailedDistance = tmpWot.detailedDistance(membersList[m].wotb_id, dSen, conf.stepMax, conf.xpercent); + + // Calculer le nombre de membres référents + if (currentMemberIsSentry) { - detailedDistance = tmpWot.detailedDistance(membersList[m].wotb_id, dSen, conf.stepMax, conf.xpercent); + countSentries++; } - else - { - detailedDistance = { - isOutdistanced: tmpWot.isOutdistanced(membersList[m].wotb_id, dSen, conf.stepMax, conf.xpercent) - }; + + // Calculate membersNbSentriesUnreached + membersNbSentriesUnreached[membersList[m].uid] = parseInt(detailedDistance.nbSentries) - parseInt(detailedDistance.nbSuccess); + + // Calculer la qualité du membre courant + if (membersQuality(constants.QUALITY_CACHE_ACTION.GET_QUALITY, membersList[m].wotb_id, (currentMemberIsSentry) ? 1 : 0) >= 1.0) { + proportionMembersWithQualityUpper1++; } - - - if (constants.USE_WOTB6) - { - // Calculate membersNbSentriesUnreached - membersNbSentriesUnreached[membersList[m].uid] = parseInt(detailedDistance.nbSentries)-parseInt(detailedDistance.nbSuccess); - - // Récupérer les informations détaillés de distance pour une nouvelle identité qui ne serait certifiée que par le membre courant (ce qui équivaut à récupérer les informations de distance pour le membre courant en décrémentant stepMax de 1) - let detailedDistanceQualityExt = tmpWot.detailedDistance(membersList[m].wotb_id, dSen, conf.stepMax-1, conf.xpercent); - - // Calculer la qualité du membre courant - membersQualityExt[membersList[m].uid] = ((detailedDistanceQualityExt.nbSuccess/detailedDistanceQualityExt.nbSentries)/conf.xpercent).toFixed(2); - if (membersQualityExt[membersList[m].uid] >= 1.0) - { - proportionMembersWithQualityUpper1++; - } - - // Calculer la qualité du membre courant s'il n'y avait pas de référents (autrement di si tout les membres était référents) - let membersQualityIfNoSentries = ((detailedDistanceQualityExt.nbReached/membersList.length)/conf.xpercent).toFixed(2); - //console.log("membersQualityIfNoSentries[%s] = %s", membersList[m].uid, membersQualityIfNoSentries); - if (membersQualityIfNoSentries >= 1.0) - { - proportionMembersWithQualityUpper1IfNoSentries++; - } - - // Calculate meanSentriesReachedBySentriesInSingleExtCert, meanMembersReachedBySentriesInSingleExtCert, meanSentriesReachedByMembersInSingleExtCert and meanMembersReachedByMembersInSingleExtCert - if (currentMemberIsSentry) - { - meanSentriesReachedBySentriesInSingleExtCert += parseFloat(((detailedDistanceQualityExt.nbSuccess/detailedDistanceQualityExt.nbSentries)*100).toFixed(2)); - meanMembersReachedBySentriesInSingleExtCert += parseFloat(((detailedDistanceQualityExt.nbReached/membersList.length)*100).toFixed(2)); - countSentries++; - } - meanSentriesReachedByMembersInSingleExtCert += parseFloat(((detailedDistanceQualityExt.nbSuccess/detailedDistanceQualityExt.nbSentries)*100).toFixed(2)); - meanMembersReachedByMembersInSingleExtCert += parseFloat(((detailedDistanceQualityExt.nbReached/membersList.length)*100).toFixed(2)); + + // Calculer la qualité du membre courant s'il n'y avait pas de référents (autrement di si tout les membres était référents) + //let membersQualityIfNoSentries = ((detailedDistanceQualityExt.nbReached/membersList.length)/conf.xpercent).toFixed(2); + //console.log("membersQualityIfNoSentries[%s] = %s", membersList[m].uid, membersQualityIfNoSentries); + if (membersQuality(constants.QUALITY_CACHE_ACTION.GET_QUALITY, membersList[m].wotb_id, -1) >= 1.0) { + proportionMembersWithQualityUpper1IfNoSentries++; } // Nettoyer la wot temporaire @@ -470,14 +481,21 @@ module.exports = (req, res, next) => co(function *() { { for (const member of membersList) { - tabSort.push(membersQualityExt[member.uid]); + tabSort.push(membersQuality(constants.QUALITY_CACHE_ACTION.GET_QUALITY, member.wotb_id)); } } else if (sort_by == "sigCount") { for (const memberCertifsList of membersCertifsList) { - tabSort.push(memberCertifsList.length); + if (memberCertifsList.length > 0) + { + tabSort.push(memberCertifsList.length+1); + } + else + { + tabSort.push(1); + } } } else { res.status(500).send(`<pre><p>ERREUR : param <i>sort_by</i> invalid !</p></pre>`) } // @@ -529,24 +547,9 @@ module.exports = (req, res, next) => co(function *() { if (reinitCache) { - if (constants.USE_WOTB6) - { - // Calculate mean Members/Sentries ReachedBy Members/Sentries InSingleExtCert - if (countSentries > 0) - { - meanSentriesReachedBySentriesInSingleExtCert = parseFloat((meanSentriesReachedBySentriesInSingleExtCert/countSentries).toFixed(2)); - meanMembersReachedBySentriesInSingleExtCert = parseFloat((meanMembersReachedBySentriesInSingleExtCert/countSentries).toFixed(2)); - } - if (membersList.length > 0) - { - meanSentriesReachedByMembersInSingleExtCert = parseFloat((meanSentriesReachedByMembersInSingleExtCert/membersList.length).toFixed(2)); - meanMembersReachedByMembersInSingleExtCert = parseFloat((meanMembersReachedByMembersInSingleExtCert/membersList.length).toFixed(2)); - } - - //Calculate proportionMembersWithQualityUpper1 and proportionMembersWithQualityUpper1IfNoSentries - proportionMembersWithQualityUpper1 /= membersList.length; - proportionMembersWithQualityUpper1IfNoSentries /= membersList.length; - } + //Calculate proportionMembersWithQualityUpper1 and proportionMembersWithQualityUpper1IfNoSentries + proportionMembersWithQualityUpper1 /= membersList.length; + proportionMembersWithQualityUpper1IfNoSentries /= membersList.length; // recalculate meanCentrality and meanShortestsPathLength if (centrality=='yes') @@ -572,21 +575,23 @@ module.exports = (req, res, next) => co(function *() { // Sinon, printer le tableau html else { - + let meansMembersQuality = membersQuality(constants.QUALITY_CACHE_ACTION.GET_MEANS); + res.locals = { host: req.headers.host.toString(), - USE_WOTB6: constants.USE_WOTB6, // get parameters - days, mode, sort_by, order, - pendingSigs, centrality, + days, mode, sort_by, order, + pendingSigs, centrality, nextYn, + numberOfRandomMembers, randomList, // page data - currentBlockchainTimestamp, + currentBlockchainTimestamp, limitTimestamp, nbMaxCertifs, membersListFiltered: membersListOrdered.filter( member=> - member.expireMembershipTimestamp < limitTimestamp - && member.expireMembershipTimestamp > currentBlockchainTimestamp + member.expireMembershipTimestamp < limitTimestamp + && member.expireMembershipTimestamp > currentBlockchainTimestamp ), + countSentries, // currency parameters xpercent: conf.xpercent, sigWindow: conf.sigWindow, @@ -597,13 +602,10 @@ module.exports = (req, res, next) => co(function *() { // members cache data membersLastUptime, - membersQualityExt, - meanSentriesReachedBySentriesInSingleExtCert, - meanMembersReachedBySentriesInSingleExtCert, - meanSentriesReachedByMembersInSingleExtCert, - meanMembersReachedByMembersInSingleExtCert, + membersQuality, proportionMembersWithQualityUpper1, proportionMembersWithQualityUpper1IfNoSentries, + meansMembersQuality, // centrality cache data lockCentralityCalc, diff --git a/routes/membersCount.js b/routes/membersCount.js index bba380baa0c4f79c5a3a8d9001e4f0d6aa54d3cf..b69854554865f7fc7251cbb946ae80d006e163ef 100755 --- a/routes/membersCount.js +++ b/routes/membersCount.js @@ -3,8 +3,9 @@ const co = require('co') const timestampToDatetime = require(__dirname + '/../lib/timestampToDatetime') const getLang = require(__dirname + '/../lib/getLang') +//const constants = require(__dirname + '/../lib/constants.js') -const STEP_COUNT_MAX = 150; +//const STEP_COUNT_MAX = 150; module.exports = (req, res, next) => co(function *() { @@ -16,13 +17,13 @@ module.exports = (req, res, next) => co(function *() { var pow = req.query.pow || 'no'; // get lg file - const LANG = getLang(`${__dirname}/../lg/membersCount_${req.query.lg||'fr'}.txt`); // + const LANG = getLang(`${__dirname}/../lg/membersCount_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`); // get medianTime of beginBlock var beginBlock = yield duniterServer.dal.peerDAL.query('SELECT `medianTime`,`hash` FROM block WHERE `fork`=0 AND `number` = '+cache.beginBlock[0].number+' LIMIT 1'); // get blockchain - var blockchain = yield duniterServer.dal.peerDAL.query('SELECT `hash`,`membersCount`,`medianTime`,`number`,`certifications`,`issuersCount`,`powMin` FROM block WHERE `fork`=0 AND `medianTime` <= '+cache.endBlock[0].medianTime+' AND `medianTime` >= '+beginBlock[0].medianTime+' ORDER BY `medianTime` ASC'); + var blockchain = yield duniterServer.dal.peerDAL.query('SELECT `hash`,`membersCount`,`medianTime`,`number`,`certifications`,`issuersCount`,`powMin` FROM block WHERE `fork`=0 AND `medianTime` <= '+cache.endBlock[0].medianTime+' ORDER BY `medianTime` ASC'); // Get blockchain timestamp @@ -32,11 +33,11 @@ module.exports = (req, res, next) => co(function *() { // Traiter le cas stepUnit == "blocks" if (cache.stepUnit == "blocks") { - if ( Math.ceil((cache.endBlock[0].number-cache.beginBlock[0].number)/cache.step) > STEP_COUNT_MAX ) { cache.step = Math.ceil((cache.endBlock[0].number-cache.beginBlock[0].number)/STEP_COUNT_MAX); } + if ( Math.ceil((cache.endBlock[0].number-cache.beginBlock[0].number)/cache.step) > constants.STEP_COUNT_MAX ) { cache.step = Math.ceil((cache.endBlock[0].number-cache.beginBlock[0].number)/constants.STEP_COUNT_MAX); } } // Initialize nextStepTime, stepIssuerCount and bStep - var nextStepTime = blockchain[0].medianTime; + var nextStepTime = cache.beginBlock[0].medianTime; let stepIssuerCount = 0; let stepPowMin = 0; let bStep = 0; @@ -44,25 +45,32 @@ module.exports = (req, res, next) => co(function *() { // Adapt nextStepTime initial value switch (cache.stepUnit) { - case "hours": nextStepTime -= (blockchain[0].medianTime % 3600); break; - case "days":case "weeks":case "months":case "years": nextStepTime -= (blockchain[0].medianTime % 86400); break; - default: break; - } + case "hours": nextStepTime -= (cache.beginBlock[0].medianTime % 3600); break; + case "days":case "weeks":case "months":case "years": nextStepTime -= (cache.beginBlock[0].medianTime % 86400); break; + default: break; + } + + // Calculate initial cacheIndex value + let cacheIndex = 0; + while (cache.blockchain[cacheIndex].number <= cache.beginBlock[0].number && (cache.blockchain.length-1) > cacheIndex) { cacheIndex++; } + console.log("cache.blockchain[cacheIndex].number = %s", cache.blockchain[cacheIndex].number); // DEBUG // fill tabMembersCount var tabMembersCount = []; - let cacheIndex = 0; - for (let b=0;b<blockchain.length;b++) + for (let b=cache.beginBlock[0].number;b<blockchain.length;b++) { stepIssuerCount += blockchain[b].issuersCount; stepPowMin += blockchain[b].powMin; - bStep++; + bStep++; - while (cacheIndex < (cache.blockchain.length-1) && cache.blockchain[cacheIndex+1].number <= b) { cacheIndex++; } - // If achieve next step - if ( (cache.stepUnit == "blocks" && bStep == cache.step) || (cache.stepUnit != "blocks" && blockchain[b].medianTime >= nextStepTime)) + if ( b==cache.beginBlock[0].number || (cache.stepUnit == "blocks" && bStep == cache.step) || (cache.stepUnit != "blocks" && blockchain[b].medianTime >= nextStepTime/*(tabMembersCount[tabMembersCount.length-1].timestamp+cache.stepTime)*/)) { + let blockIndex = b;//-cache.beginBlock[0].number;//b-blockchain[0].number; + + // Calculate if increment cacheIndex + while ((cache.blockchain.length-1) > cacheIndex && cache.blockchain[cacheIndex].number <= b) { cacheIndex++; } + let previousDateTime = ""; if(tabMembersCount.length > 0) { @@ -70,7 +78,7 @@ module.exports = (req, res, next) => co(function *() { } else { - previousDateTime = timestampToDatetime(blockchain[0].medianTime); + previousDateTime = timestampToDatetime(cache.beginBlock[0].medianTime); } let dateTime = ""; if (cache.stepUnit != "blocks") @@ -79,11 +87,11 @@ module.exports = (req, res, next) => co(function *() { { switch (cache.stepUnit) { - case "hours": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[b].medianTime, cache.onlyDate); break; - case "days": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[b].medianTime-(cache.stepTime/cache.step), cache.onlyDate); break; - case "weeks": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[b].medianTime, cache.onlyDate); break; - case "months": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[b].medianTime, cache.onlyDate); break; - case "years": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[b].medianTime, cache.onlyDate); break; + case "hours": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[blockIndex].medianTime, cache.onlyDate); break; + case "days": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[blockIndex].medianTime-(cache.stepTime/cache.step), cache.onlyDate); break; + case "weeks": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[blockIndex].medianTime, cache.onlyDate); break; + case "months": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[blockIndex].medianTime, cache.onlyDate); break; + case "years": dateTime = previousDateTime+" - "+timestampToDatetime(blockchain[blockIndex].medianTime, cache.onlyDate); break; } } else @@ -94,10 +102,10 @@ module.exports = (req, res, next) => co(function *() { // push tabMembersCount tabMembersCount.push({ - blockNumber: blockchain[b].number, - timestamp: blockchain[b].medianTime, + blockNumber: blockchain[blockIndex].number, + timestamp: blockchain[blockIndex].medianTime, dateTime: dateTime, - membersCount: blockchain[b].membersCount, + membersCount: blockchain[blockIndex].membersCount, sentriesCount: cache.blockchain[cacheIndex].sentries, issuersCount: parseInt(stepIssuerCount/bStep), powMin: parseInt(stepPowMin/bStep) @@ -178,17 +186,23 @@ module.exports = (req, res, next) => co(function *() { tabMembersCount, begin: cache.beginBlock[0].number, end: cache.endBlock[0].number, - form: `${LANG["BEGIN"]} #<input type="number" name="begin" value="${cache.beginBlock[0].number}" min="0"> - ${LANG["END"]} #<input type="number" name="end" value="${cache.endBlock[0].number}" > - ${LANG["STEP"]} <input type="number" name="step" value="${cache.step}" min="1"> + form: `${LANG["BEGIN"]} #<input type="number" name="begin" value="${cache.beginBlock[0].number}" min="0" size="7" style="width:60px;"> - ${LANG["END"]} #<input type="number" name="end" value="${cache.endBlock[0].number}" size="7" style="width:60px;"> - ${LANG["STEP"]} <input type="number" name="step" value="${cache.step}" min="1" size="4" style="width:50px;"> <select name="stepUnit"> - <option name="stepUnit" value ="blocks"${cache.stepUnit == 'blocks' ? 'selected' : ''}>${LANG["BLOCKS"]} - <option name="stepUnit" value ="hours"${cache.stepUnit == 'hours' ? 'selected' : ''}>${LANG["HOURS"]} + <option name="stepUnit" value ="blocks" ${cache.stepUnit == 'blocks' ? 'selected' : ''}>${LANG["BLOCKS"]} + <option name="stepUnit" value ="hours" ${cache.stepUnit == 'hours' ? 'selected' : ''}>${LANG["HOURS"]} <option name="stepUnit" value ="days" ${cache.stepUnit == 'days' ? 'selected' : ''}>${LANG["DAYS"]} <option name="stepUnit" value ="weeks" ${cache.stepUnit == 'weeks' ? 'selected' : ''}>${LANG["WEEKS"]} <option name="stepUnit" value ="months" ${cache.stepUnit == 'months' ? 'selected' : ''}>${LANG["MONTHS"]} <option name="stepUnit" value ="years" ${cache.stepUnit == 'years' ? 'selected' : ''}>${LANG["YEARS"]} - </select>`, + </select> - ${LANG["MAX"]} <input type="number" name="nbMaxPoints" value="${cache.nbMaxPoints}" min="1" size="4" style="width:50px;"> ${LANG["POINTS"]} (${LANG["REGULATE_BY_ADAPTING"]} + <select name="adaptMaxPoints"> + <option name="adaptMaxPoints" value ="begin"> ${LANG["BEGIN"]} + <option name="adaptMaxPoints" value ="step" ${cache.adaptMaxPoints == 'step' ? 'selected' : ''}> ${LANG["STEP"]} + <option name="adaptMaxPoints" value ="end" ${cache.adaptMaxPoints == 'end' ? 'selected' : ''}> ${LANG["END"]} + </select>)`, description: `${LANG["DESCRIPTION1"]+'<br>'+LANG["DESCRIPTION2"]+'<b>'+cache.Yn+'</b>.'}`, - form2: `<input type="checkbox" name="pow" value="yes" ${pow == 'yes' ? 'checked' : ''}> ${LANG["SHOW_POW_MIN"]}`, + form2: ` + <input type="checkbox" name="pow" value="yes" ${pow == 'yes' ? 'checked' : ''}> ${LANG["SHOW_POW_MIN"]}`, chart: { type: 'line', data: { diff --git a/routes/monetaryMass.js b/routes/monetaryMass.js index 6ca6471b4cb7d034aeea255fdae2b43ada98e108..90da810fd6bf08bc818b178e41eb79ed2f8e63ed 100755 --- a/routes/monetaryMass.js +++ b/routes/monetaryMass.js @@ -2,6 +2,7 @@ const co = require('co') const timestampToDatetime = require(__dirname + '/../lib/timestampToDatetime') +const getLang = require(__dirname + '/../lib/getLang') module.exports = (req, res, next) => co(function *() { @@ -9,9 +10,16 @@ module.exports = (req, res, next) => co(function *() { try { // get GET parameters - var begin = req.query.begin >= 2 && req.query.begin || 2;// Default Value - var end = req.query.end || -1;// Default Value is current timestamp + var begin = req.query.begin >= 2 && req.query.begin || 2; // Default Value + var end = req.query.end || -1; // Default Value is current timestamp + var unit = req.query.unit || 'relative'; var format = req.query.format || 'HTML'; + + // get lg file + const LANG = getLang(`${__dirname}/../lg/monetaryMass_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`); + + // calculate meanMonetaryMassAtFullCurrency + const meanMonetaryMassAtFullCurrency = Math.ceil((1/duniterServer.conf.c)*(duniterServer.conf.dtReeval / duniterServer.conf.dt)); // get beginBlock and endBlock var beginBlock = yield duniterServer.dal.peerDAL.query('SELECT `medianTime` FROM block WHERE `number` = '+begin+' LIMIT 1'); @@ -60,8 +68,7 @@ module.exports = (req, res, next) => co(function *() { membersCount: blockchain[b].membersCount, monetaryMass: parseInt(blockchain[b].monetaryMass / 100), monetaryMassPerMembers: parseFloat(((blockchain[b].monetaryMass / 100) / blockchain[b].membersCount).toFixed(2)), - relativeMonetaryMass: 0, - relativeMonetaryMassPerMembers: 0 + derivedChoiceMonetaryMass: 0 }); if ( blockchain[b].dividend > 0 ) @@ -72,11 +79,20 @@ module.exports = (req, res, next) => co(function *() { } } - // calculate relativMonetaryMass + // calculate choiceMonetaryMass and derivedChoiceMonetaryMass for (let i=0;i<tabCurrency.length;i++) { - tabCurrency[i].relativeMonetaryMass = parseFloat(((tabCurrency[i].monetaryMass / currentDividend) * 100).toFixed(2)); - tabCurrency[i].relativeMonetaryMassPerMembers = parseFloat(((tabCurrency[i].monetaryMassPerMembers / currentDividend) * 100).toFixed(2)); + if (unit == "relative") + { + tabCurrency[i].monetaryMass = parseFloat(((tabCurrency[i].monetaryMass / currentDividend) * 100).toFixed(2)); + tabCurrency[i].monetaryMassPerMembers = parseFloat(((tabCurrency[i].monetaryMassPerMembers / currentDividend) * 100).toFixed(2)); + } + else if (unit == "percentOfFullCurrency") + { + tabCurrency[i].monetaryMass = parseFloat((((tabCurrency[i].monetaryMassPerMembers / currentDividend) / meanMonetaryMassAtFullCurrency) * 10000).toFixed(2)); + tabCurrency[i].monetaryMassPerMembers = tabCurrency[i].monetaryMass; + } + if (i>0) { tabCurrency[i].derivedChoiceMonetaryMass = parseFloat((((tabCurrency[i].monetaryMass / tabCurrency[i-1].monetaryMass) - 1.0) * 100).toFixed(2)); } } // Si le client demande la réponse au format JSON, le faire @@ -85,64 +101,72 @@ module.exports = (req, res, next) => co(function *() { else { // GET parameters - var unit = req.query.unit == 'quantitative' ? 'quantitative' : 'relative'; - var massByMembers = req.query.massByMembers == 'no' ? 'no' : 'yes'; - var type = req.query.type == 'linear' ? 'linear' : 'logarithmic'; - - + var massByMembers = req.query.massByMembers == 'no' && unit != "percentOfFullCurrency" ? 'no' : 'yes'; + var type = req.query.type || 'logarithmic'; + if (unit == "percentOfFullCurrency") { type = 'linear'; } + if (type != 'linear') { type = 'logarithmic'; } // Define full currency description - let meanMonetaryMassAtFullCurrency = Math.ceil((1/duniterServer.conf.c)*(duniterServer.conf.dtReeval / duniterServer.conf.dt)); - var fullCurrency = "The currency will be full when the money supply by member will be worth <b>"+meanMonetaryMassAtFullCurrency - +" DU</b> (because 1/c * dtReeval/dt = <b>"+meanMonetaryMassAtFullCurrency+" DU</b>)<br>" - +"Currently, 1 DU<sub>"+duniterServer.conf.currency+"</sub> = <b>"+(currentDividend/100)+"</b> "+duniterServer.conf.currency+" and we have <b>" - +endBlock[0].membersCount+"</b> members. Thus in full currency we would have a total money supply of <b>" + var fullCurrency = LANG['DESC1']+" <b>"+meanMonetaryMassAtFullCurrency + +" "+LANG['UD']+"</b> ("+LANG['FULL_CURRENCY_FORMULA']+" = <b>"+meanMonetaryMassAtFullCurrency+" "+LANG['UD']+"</b>)<br>" + +LANG['CURRENTLY']+", 1 "+LANG['UD']+"<sub>"+duniterServer.conf.currency+"</sub> = <b>"+(currentDividend/100)+"</b> "+duniterServer.conf.currency+" "+LANG['AND_WE_HAVE']+" <b>" + +endBlock[0].membersCount+"</b> "+LANG['DESC2']+" <b>" +(meanMonetaryMassAtFullCurrency*currentDividend*endBlock[0].membersCount/100)+"</b> "+duniterServer.conf.currency - +" (<b>"+(meanMonetaryMassAtFullCurrency*currentDividend/100)+"</b> "+duniterServer.conf.currency+"/member)." ; + +" (<b>"+(meanMonetaryMassAtFullCurrency*currentDividend/100)+"</b> "+duniterServer.conf.currency+"/"+LANG['MEMBER']+")." ; // Define max yAxes var maxYAxes = meanMonetaryMassAtFullCurrency; + let indexEnd = tabCurrency.length-1; if (unit == "quantitative") { maxYAxes = maxYAxes*currentDividend/100; } - if (massByMembers == "no") { maxYAxes = maxYAxes*endBlock[0].membersCount; } + if (unit == "percentOfFullCurrency") { maxYAxes = 100; } + else if (massByMembers == "no") { maxYAxes = maxYAxes*endBlock[0].membersCount; } res.locals = { - host: req.headers.host.toString(), - tabCurrency, - currentDividend, - begin, - end, - unit, - massByMembers, - type, - form: `Begin #<input type="number" name="begin" value="${begin}"> - End #<input type="number" name="end" value="${end}"> <select name="unit"> - <option name="unit" value ="quantitative">quantitative - <option name="unit" value ="relative" ${unit == 'relative' ? 'selected' : ''}>relative -</select> <select name="massByMembers"> - <option name="massByMembers" value ="yes">mass by members - <option name="massByMembers" value ="no" ${massByMembers == 'no' ? 'selected' : ''}>total mass -</select> <select name="type"> - <option name="type" value ="logarithmic">logarithmic - <option name="type" value ="linear" ${type == 'linear' ? 'selected' : ''}>linear -</select>`, - description: `${fullCurrency}`, + host: req.headers.host.toString(), + tabCurrency, + currentDividend, + begin, + end, + unit, + massByMembers, + type, + form: `${LANG['BEGIN']} #<input type="number" name="begin" value="${begin}"> - ${LANG['END']} #<input type="number" name="end" value="${end}"> <select name="unit"> + <option name="unit" value ="quantitative">${LANG['QUANTITATIVE']} + <option name="unit" value ="relative" ${unit == 'relative' ? 'selected' : ''}>${LANG['RELATIVE']} + <option name="unit" value ="percentOfFullCurrency" ${unit == 'percentOfFullCurrency' ? 'selected' : ''}>${LANG['PERCENT_OF_FULL_CURRENCY']} + </select> <select name="massByMembers"> + <option name="massByMembers" value ="yes">${LANG['MASS_BY_MEMBERS']} + <option name="massByMembers" value ="no" ${massByMembers == 'no' ? 'selected' : ''}>${LANG['TOTAL_MASS']} + </select> <select name="type"> + <option name="type" value ="logarithmic">${LANG['LOGARITHMIC']} + <option name="type" value ="linear" ${type == 'linear' ? 'selected' : ''}>${LANG['LINEAR']} + </select>`, + description: `${fullCurrency}`, chart: { type: 'bar', data: { labels: tabCurrency.map( item=> item.dateTime ), + //yLabels: tabCurrency.map( item=> item.monetaryMass, item=>derivedChoiceMonetaryMass), datasets: [{ - label: `#${unit == "relative" ? "DUğ1" : 'Ğ1'}${massByMembers == "yes" ? '/member' : ''}`, - data: unit == 'quantitative' - ? tabCurrency.map( item=> + //yAxisID: 1, + label: `${unit == "percentOfFullCurrency" ? `${LANG['MONETARY_MASS']} (${LANG['IN_PERCENT_OF_FULL_CURRENCY']})`:`#${unit == "relative" ? LANG['UD']+"ğ1" : 'Ğ1'}${massByMembers == "yes" ? '/'+LANG['MEMBER'] : ''}`}`, + data: tabCurrency.map( item=> massByMembers == "no" ? item.monetaryMass - : item.monetaryMassPerMembers) - : tabCurrency.map( item=> - massByMembers == "no" - ? item.relativeMonetaryMass - : item.relativeMonetaryMassPerMembers), + : item.monetaryMassPerMembers), backgroundColor: 'rgba(54, 162, 235, 0.5)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 1 + }, + { + //yAxisID: 2, + label: LANG['PERCENT_VARIATION_MONETARY_MASS'], + data: tabCurrency.map( item=> item.derivedChoiceMonetaryMass), + backgroundColor: 'rgba(0, 162, 0, 0.5)', + borderColor: 'rgba(0, 162, 0, 1)', + borderWidth: 1, + type: 'line', + fill: false }] }, options: { @@ -154,20 +178,33 @@ module.exports = (req, res, next) => co(function *() { // }, title: { display: true, - text: `${unit == "relative" ? "DUğ1" : 'Ğ1'} Monetary Mass ${massByMembers == "yes" ? 'by members ' : ''}in the range #${begin}-#${end }` + text: `${unit == "percentOfFullCurrency" ? LANG['MONETARY_MASS']+' '+LANG['IN_PERCENT_OF_FULL_CURRENCY']:`${unit == "relative" ? LANG['UD']+"ğ1 " : 'Ğ1 '} ${massByMembers == "yes" ? LANG['BY_MEMBER']: ''}`} ${LANG['IN_THE_RANGE']} #${begin}-#${end }` }, legend: { - display: false + display: true }, scales: { yAxes: [{ - type: type, + //yAxisID: 1, + type: type, position: 'left', ticks: { - min: 1, - max: maxYAxes + callback: function(value, index, values) {//needed to change the scientific notation results from using logarithmic scale + return Number(value.toString()); //pass tick values as a string into Number function + }, + max: maxYAxes, + } + }/*, + { + //yAxisID: 2, + type: type, + position: 'right', + ticks: { + callback: function(value, index, values) {//needed to change the scientific notation results from using logarithmic scale + return Number(value.toString()); //pass tick values as a string into Number function + } } - }] + }*/] } } } diff --git a/routes/tools/membersQuality.js b/routes/tools/membersQuality.js new file mode 100644 index 0000000000000000000000000000000000000000..bc65860ee6057ca903b53339dbaec10662517681 --- /dev/null +++ b/routes/tools/membersQuality.js @@ -0,0 +1,113 @@ +const constants = require(__dirname + '/../../lib/constants') + +// membersQuality cache +var lastUpgradeTime = 0; +var wot = null; +var membersCount = 0; +var sentriesCount = 0; +var conf = { + dSen : 0, + stepMax: 0, + xprecent: 1.0 +}; +var tabMembersQuality = []; +var tabMembersQualityIfNoSentries = []; +var tabMembersQualityDetailedDistance = []; +var meansCalculate = false; +var means = { + meanSentriesReachedBySentries: 0, + meanMembersReachedBySentries: 0, + meanSentriesReachedByMembers: 0, + meanMembersReachedByMembers: 0 +}; + +module.exports = function membersQuality(action, wotb_id = 0, dSen = 0, stepMax = 0, xpercent = 0, wotCopy = null) { + + if (action == constants.QUALITY_CACHE_ACTION.GET_QUALITY) { + if (typeof(tabMembersQuality[wotb_id])=='undefined') + { + // Si le wotb_id n'existe pas, renvoyer -1 + if (wotb_id > membersCount) + { + return -1; + } + + // Récupérer les informations détaillés de distance pour une nouvelle identité qui ne serait certifiée que par le membre courant (ce qui équivaut à récupérer les informations de distance pour le membre courant en décrémentant stepMax de 1) + let detailedDistance = wot.detailedDistance(wotb_id, conf.dSen, conf.stepMax-1, conf.xpercent); + + // Calculer la qualité du membre + tabMembersQuality[wotb_id] = parseFloat(((detailedDistance.nbSuccess/detailedDistance.nbSentries)/conf.xpercent).toFixed(2)); + tabMembersQualityIfNoSentries[wotb_id] = parseFloat(((detailedDistance.nbReached/membersCount)/conf.xpercent).toFixed(2)); + + if (dSen > 0) + { + means.meanSentriesReachedBySentries += parseFloat(((detailedDistance.nbSuccess/detailedDistance.nbSentries)*100).toFixed(2)); + means.meanMembersReachedBySentries += parseFloat(((detailedDistance.nbReached/membersCount)*100).toFixed(2)); + } + means.meanSentriesReachedByMembers += parseFloat(((detailedDistance.nbSuccess/detailedDistance.nbSentries)*100).toFixed(2)); + means.meanMembersReachedByMembers += parseFloat(((detailedDistance.nbReached/membersCount)*100).toFixed(2)); + } + if (dSen < 0) + { + return tabMembersQualityIfNoSentries[wotb_id]; + } + else + { + return tabMembersQuality[wotb_id]; + } + } + else if (action == constants.QUALITY_CACHE_ACTION.GET_MEANS) { + if (!meansCalculate) + { + // Calculate mean Members/Sentries ReachedBy Members/Sentries + if (sentriesCount > 0) + { + means.meanSentriesReachedBySentries = parseFloat((means.meanSentriesReachedBySentries/sentriesCount).toFixed(2)); + means.meanMembersReachedBySentries = parseFloat((means.meanMembersReachedBySentries/sentriesCount).toFixed(2)); + } + if (membersCount > 0) + { + means.meanSentriesReachedByMembers = parseFloat((means.meanSentriesReachedByMembers/membersCount).toFixed(2)); + means.meanMembersReachedByMembers = parseFloat((means.meanMembersReachedByMembers/membersCount).toFixed(2)); + } + meansCalculate = true; + } + + return means; + } + else if (action == constants.QUALITY_CACHE_ACTION.INIT) { + if (wot != null) + { + wot.clear(); + wot = null + } + if (wotCopy != null) + { + lastUpgradeTime = Math.floor(Date.now() / 1000); + + wot = wotCopy; + membersCount = wot.getWoTSize()-wot.getDisabled().length; + sentriesCount = wot.getSentries(dSen).length; + conf.dSen = dSen; + conf.stepMax = stepMax; + conf.xpercent = xpercent; + + tabMembersQuality = []; + tabMembersQualityIfNoSentries = []; + + meansCalculate = false; + means.meanSentriesReachedBySentries = 0; + means.meanMembersReachedBySentries = 0; + means.meanSentriesReachedByMembers = 0; + means.meanMembersReachedByMembers = 0; + } + + return lastUpgradeTime; + } + else if (action == constants.QUALITY_CACHE_ACTION.GET_SENTRIES_COUNT) { + return sentriesCount; + } + else if (action == constants.QUALITY_CACHE_ACTION.GET_D_SEN) { + return conf.dSen; + } +} diff --git a/routes/willMembers.js b/routes/willMembers.js index 42857c873e4ac5ae14cfbf8e32d0eda0c8f2d218..e6a66f0e4a83a7b1693c9d3eac7edb345b1ea901 100755 --- a/routes/willMembers.js +++ b/routes/willMembers.js @@ -5,8 +5,6 @@ const crypto = require('crypto') const constants = require(__dirname + '/../lib/constants') -const wotb = (constants.USE_WOTB6) ? require('wotb'):null; - const timestampToDatetime = require(__dirname + '/../lib/timestampToDatetime') // Préserver les résultats en cache @@ -58,40 +56,37 @@ module.exports = (req, res, next) => co(function *() { let limitTimestamp = currentBlockchainTimestamp + (days*86400); // Alimenter wotb avec la toile de confiance - const wotbInstance = (constants.USE_WOTB6) ? wotb.newFileInstance(duniterServer.home + '/wotb.bin'):duniterServer.dal.wotb; + const wotbInstance = duniterServer.dal.wotb; - // Vérifier si le cache doit être Réinitialiser - let reinitCache = (Math.floor(Date.now() / 1000) > (willMembersLastUptime + constants.MIN_WILLMEMBERS_UPDATE_FREQ)); + // Vérifier si le cache doit être Réinitialiser + let reinitCache = (Math.floor(Date.now() / 1000) > (willMembersLastUptime + constants.MIN_WILLMEMBERS_UPDATE_FREQ)); - // Si le cache willMembers est dévérouillé, le vérouiller, sinon ne pas réinitialiser le cache - if (reinitCache && !lockWillMembers) - { - lockWillMembers = true; - } - else if(lockWillMembers) - { - reinitCache = false; - } + // Si le cache willMembers est dévérouillé, le vérouiller, sinon ne pas réinitialiser le cache + if (reinitCache && !lockWillMembers) { + lockWillMembers = true; + } else if(lockWillMembers) { + reinitCache = false; + } - if (reinitCache) + if (reinitCache) { - // Réinitialiser le cache - identitiesList = []; - idtysPendingCertifsList = []; - nbMaxCertifs = 0; - countMembersWithSigQtyValidCert = 0; - sentries = []; - sentriesIndex = []; - wotbIdIndex = []; - membersQualityExt = []; - willMembersLastUptime = Math.floor(Date.now() / 1000); + // Réinitialiser le cache + identitiesList = []; + idtysPendingCertifsList = []; + nbMaxCertifs = 0; + countMembersWithSigQtyValidCert = 0; + sentries = []; + sentriesIndex = []; + wotbIdIndex = []; + membersQualityExt = []; + willMembersLastUptime = Math.floor(Date.now() / 1000); // Récupérer la liste des membres référents sentries = wotbInstance.getSentries(dSen); // Récupérer la liste des identités en piscine - const resultQueryIdtys = yield duniterServer.dal.peerDAL.query('SELECT `buid`,`pubkey`,`uid`,`hash`,`expires_on` FROM identities_pending WHERE `member`=0'); + const resultQueryIdtys = yield duniterServer.dal.peerDAL.query('SELECT `buid`,`pubkey`,`uid`,`hash`,`expires_on`,`revocation_sig` FROM identities_pending WHERE `member`=0'); // Récupérer pour chaque identité, l'ensemble des certifications qu'elle à reçue. for (let i=0;i<resultQueryIdtys.length;i++) @@ -101,12 +96,12 @@ module.exports = (req, res, next) => co(function *() { let idtyBlockNumber = idtyBlockStamp[0]; // récupérer le medianTime et le hash du bloc d'émission de l'identité - let idtyEmittedBlock = yield duniterServer.dal.peerDAL.query('SELECT `medianTime`,`hash` FROM block WHERE `number`=\''+idtyBlockNumber+'\' LIMIT 1'); + let idtyEmittedBlock = yield duniterServer.dal.peerDAL.query('SELECT `medianTime`,`hash` FROM block WHERE `number`=\''+idtyBlockNumber+'\' AND fork=0 LIMIT 1'); // Récupérer l'identifiant wotex de l'identité (en cas d'identité multiple) let idties = yield duniterServer.dal.idtyDAL.query('' + - 'SELECT hash, uid, pub, wotb_id FROM i_index WHERE (uid = ? or pub = ?) ' + - 'UNION ALL ' + 'SELECT hash, uid, pubkey as pub, (SELECT NULL) AS wotb_id FROM idty WHERE (uid = ? or pubkey = ?)', [resultQueryIdtys[i].uid, resultQueryIdtys[i].uid, resultQueryIdtys[i].uid, resultQueryIdtys[i].uid]); + 'SELECT hash, uid, pub, wotb_id FROM i_index WHERE uid = ? ' + + 'UNION ALL ' + 'SELECT hash, uid, pubkey as pub, (SELECT NULL) AS wotb_id FROM idty WHERE uid = ?', [resultQueryIdtys[i].uid, resultQueryIdtys[i].uid]); let wotexId = ''; if (idties.length > 1) { @@ -123,6 +118,13 @@ module.exports = (req, res, next) => co(function *() { if (typeof(idtyEmittedBlock[0]) == 'undefined' || idtyEmittedBlock[0].hash == idtyBlockStamp[1]) { validIdtyBlockStamp = true; } + // vérifier si l'identité a été révoquée ou non + let idtyRevoked = false; + if (resultQueryIdtys[i].revocation_sig != null) + { + idtyRevoked = true; + } + // Stocker les informations de l'identité identitiesList.push({ BlockNumber: idtyBlockNumber, @@ -135,7 +137,8 @@ module.exports = (req, res, next) => co(function *() { nbCert: 0, nbValidPendingCert: 0, registrationAvailability: 0, - validBlockStamp: validIdtyBlockStamp + validBlockStamp: validIdtyBlockStamp, + idtyRevoked: idtyRevoked }); idtysPendingCertifsList.push(new Array()); @@ -215,6 +218,7 @@ module.exports = (req, res, next) => co(function *() { wotb_id: wotb_id, issuerIsSentry: issuerIsSentry, blockNumber: tmpQueryPendingCertifsList[j].block_number, + creationTimestamp: emittedBlock[0].medianTime, timestampExpire: tmpQueryPendingCertifsList[j].expires_on, timestampWritable: certTimestampWritable, validBlockStamp: validBlockStamp @@ -273,6 +277,7 @@ module.exports = (req, res, next) => co(function *() { wotb_id: idtysPendingCertifsList[i][idMin].wotb_id, issuerIsSentry: idtysPendingCertifsList[i][idMin].issuerIsSentry, blockNumber: idtysPendingCertifsList[i][idMin].blockNumber, + creationTimestamp: idtysPendingCertifsList[i][idMin].creationTimestamp, timestampExpire: idtysPendingCertifsList[i][idMin].timestampExpire, timestampWritable: idtysPendingCertifsList[i][idMin].timestampWritable, validBlockStamp: idtysPendingCertifsList[i][idMin].validBlockStamp @@ -370,15 +375,10 @@ module.exports = (req, res, next) => co(function *() { let tmpWot = wotbInstance.memCopy(); // Mesurer la qualité externe de chaque emetteur de chaque certification - if (constants.USE_WOTB6) - { - for (const cert of idtysPendingCertifsList[idMax]) - { - if ( typeof(membersQualityExt[cert.from]) == 'undefined' ) - { - let detailedDistanceQualityExt = tmpWot.detailedDistance(cert.wotb_id, dSen, conf.stepMax-1, conf.xpercent); - membersQualityExt[cert.from] = ((detailedDistanceQualityExt.nbSuccess/detailedDistanceQualityExt.nbSentries)/conf.xpercent).toFixed(2); - } + for (const cert of idtysPendingCertifsList[idMax]) { + if (typeof (membersQualityExt[cert.from]) == 'undefined') { + let detailedDistanceQualityExt = tmpWot.detailedDistance(cert.wotb_id, dSen, conf.stepMax - 1, conf.xpercent); + membersQualityExt[cert.from] = ((detailedDistanceQualityExt.nbSuccess / detailedDistanceQualityExt.nbSentries) / conf.xpercent).toFixed(2); } } @@ -392,14 +392,14 @@ module.exports = (req, res, next) => co(function *() { } } // Récupérer les données de distance du dossier d'adhésion de l'indentité idMax - let detailedDistance = (constants.USE_WOTB6) ? tmpWot.detailedDistance(pendingIdtyWID, dSen, conf.stepMax, conf.xpercent):tmpWot.isOutdistanced(pendingIdtyWID, dSen, conf.stepMax, conf.xpercent); + let detailedDistance = tmpWot.detailedDistance(pendingIdtyWID, dSen, conf.stepMax, conf.xpercent); // Nettoyer la wot temporaire tmpWot.clear(); // Calculer percentSentriesReached et percentMembersReached - let percentSentriesReached = (constants.USE_WOTB6) ? parseFloat(((detailedDistance.nbSuccess/detailedDistance.nbSentries)*100).toFixed(2)):null; - let percentMembersReached = (constants.USE_WOTB6) ? parseFloat(((detailedDistance.nbReached/currentMembersCount)*100).toFixed(2)):null; + let percentSentriesReached = parseFloat(((detailedDistance.nbSuccess/detailedDistance.nbSentries)*100).toFixed(2)); + let percentMembersReached = parseFloat(((detailedDistance.nbReached/currentMembersCount)*100).toFixed(2)); // Pousser l'identité dans le tableau idtysListOrdered idtysListOrdered.push({ @@ -415,11 +415,12 @@ module.exports = (req, res, next) => co(function *() { percentMembersReached: percentMembersReached, membership: membership, pendingCertifications: idtysPendingCertifsList[idMax], - validBlockStamp: identitiesList[idMax].validBlockStamp + validBlockStamp: identitiesList[idMax].validBlockStamp, + idtyRevoked: identitiesList[idMax].idtyRevoked }); // Si le cache a été réinitialiser, recalculer les sommes meanSentriesReachedByIdtyPerCert et meanMembersReachedByIdtyPerCert - if (constants.USE_WOTB6 && reinitCache && identitiesList[idMax].nbValidPendingCert > 0) + if (reinitCache && identitiesList[idMax].nbValidPendingCert > 0) { let nbReceiveCert = identitiesList[idMax].nbValidPendingCert; meanSentriesReachedByIdtyPerCert[nbReceiveCert-1] += percentSentriesReached; @@ -445,29 +446,22 @@ module.exports = (req, res, next) => co(function *() { idtysListOrdered = idtysListOrdered2; } - if (reinitCache) - { + if (reinitCache) { // Calculate meanSentriesReachedByIdtyPerCert and meanMembersReachedByIdtyPerCert - if (constants.USE_WOTB6) - { - for (let i=0;i<=nbMaxCertifs;i++) - { - if ( countIdtiesPerReceiveCert[i] > 0 ) - { - meanSentriesReachedByIdtyPerCert[i] = parseFloat((meanSentriesReachedByIdtyPerCert[i]/countIdtiesPerReceiveCert[i]).toFixed(2)); - meanMembersReachedByIdtyPerCert[i] = parseFloat((meanMembersReachedByIdtyPerCert[i]/countIdtiesPerReceiveCert[i]).toFixed(2)); - } - else - { - meanSentriesReachedByIdtyPerCert[i] = 0.0; - meanMembersReachedByIdtyPerCert[i] = 0.0; - } - } - } - - // Dévérouiller le cache willMembers - lockWillMembers = false; - } + for (let i = 0; i <= nbMaxCertifs; i++) { + if (countIdtiesPerReceiveCert[i] > 0) { + meanSentriesReachedByIdtyPerCert[i] = parseFloat((meanSentriesReachedByIdtyPerCert[i] / countIdtiesPerReceiveCert[i]).toFixed(2)); + meanMembersReachedByIdtyPerCert[i] = parseFloat((meanMembersReachedByIdtyPerCert[i] / countIdtiesPerReceiveCert[i]).toFixed(2)); + } + else { + meanSentriesReachedByIdtyPerCert[i] = 0.0; + meanMembersReachedByIdtyPerCert[i] = 0.0; + } + } + + // Dévérouiller le cache willMembers + lockWillMembers = false; + } // Si le client demande la réponse au format JSON, le faire if (format == 'JSON') @@ -481,7 +475,6 @@ module.exports = (req, res, next) => co(function *() { res.locals = { // Les varibles à passer au template host: req.headers.host.toString(), - USE_WOTB6: constants.USE_WOTB6, // get parameters days, sort_by, order, sortSig, showIdtyWithZeroCert, diff --git a/routes/wotex.js b/routes/wotex.js index 7c3222f6aa7058409b7d1cfa0574b30350e1da8a..828adb22eb97ca54768e6316600b4e4c90668ce2 100755 --- a/routes/wotex.js +++ b/routes/wotex.js @@ -16,7 +16,7 @@ module.exports = (req, res, next) => co(function *() { var help = req.query.help || 'yes'; // get lg file - const LANG = getLang(`${__dirname}/../lg/wotex_${req.query.lg||'fr'}.txt`); + const LANG = getLang(`${__dirname}/../lg/wotex_${req.query.lg||constants.DEFAULT_LANGUAGE}.txt`); // Trouve les points de contrôle efficacement grâce au module C (nommé "wotb") const wotb = duniterServer.dal.wotb.memCopy(); diff --git a/views/Chart.html b/views/Chart.html index 3a695de2d8b441f8210e55cdb5da895ee9513fd6..8c1236dd93d9924ac3c067a10aa32b377393d3fd 100755 --- a/views/Chart.html +++ b/views/Chart.html @@ -1,29 +1,11 @@ ${(host.substr(host.length-6,6) == '.onion') ? HTML_TOR_HEAD:HTML_HEAD} - <title>${currencyName}-monit</title> + <title>${currencyName}-monit : ${MENU_LANG[pageName]}</title> </head> <body> <!-- Afficher le menu --> <script src=${(host.substr(host.length-6,6) == '.onion') ? "http://722iprlhm7pw4mx7.onion/js/Chart.min.js":"https://librelois.fr/js/Chart.min.js"}></script> -<table align="center" width="100%"> - <tr> - <td><a href="willMembers?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WILL_MEMBERS"]}</a></td> - <td><a href="members?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS"]}</a></td> - <td><a href="membersCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS_COUNT"]}</a></td> - <td><a href="blockCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["BLOCK_COUNT"]}</a></td> - <td><a href="monetaryMass?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MONETARY_MASS"]}</a></td> - <td><a href="wotex?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WOTEX"]}</a></td> - <td><a href="about?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["ABOUT"]}</a></td> - <td> - <form action="" method="GET"> - <select name="lg" onchange="this.form.submit()"> - <option name="lg" value="fr" ${MENU_LANG['LG'] == 'fr' ? 'selected' : ''}>FR - <option name="lg" value="en" ${MENU_LANG['LG'] == 'en' ? 'selected' : ''}>EN - </select> - </td> -</tr> -</table> -<hr> +${printMenu(MENU_LANG, help, pageName)} <!-- Afficher le formulaire --> ${form||''} <input type="submit" value="${LANG['SUBMIT_BUTTON']}"><br>${(typeof(form2) != 'undefined') ? form2:''}</form><br> diff --git a/views/HEAD.html b/views/HEAD.html index 3436a2795c4f8da18c15d282e34fe2d9e443c30b..4ce1c8115c223024c62d5cffd5038c9a028c4c13 100755 --- a/views/HEAD.html +++ b/views/HEAD.html @@ -12,4 +12,58 @@ html, body, *, pre { font-family: "Ubuntu"; } + ul { + list-style-type: none; + margin: 0; + padding: 0; + overflow: hidden; + background-color: #333; + } + .menu li { + float: left; + } + li a { + display: block; + color: white; + text-align: center; + padding: 14px 16px; + text-decoration: none; + } + li a:hover:not(.active) { + background-color: #111; + } + .active { + background-color: #4CAF50; + } + span[data-tip]{ + border-bottom: 1px dotted #000; + } + [data-tip]{ + display: inline-block; + position: relative; + } + [data-tip]:hover:before { + content: attr(data-tip); + position: absolute; + padding: 0 8px; + height: 28px; + line-height: 28px; + background-color: rgba(0, 0, 0, 0.8); + left: -10px; + top: -38px; + font-size: 14px; + border-radius: 3px; + white-space: nowrap; + color: #fff; + font-size: 11px; + } + [data-tip]:hover:after { + content: ""; + position: absolute; + border-top: 8px solid rgba(0, 0, 0, 0.8); + border-left: 8px solid transparent; + border-right: 8px solid transparent; + left: 0; + top: -10px; + } </style> \ No newline at end of file diff --git a/views/about.html b/views/about.html index 60d1c27c1f94e535bfbb08004b882387e85afd3d..464f5769f161d58c441416577cd03fb62a5aae57 100755 --- a/views/about.html +++ b/views/about.html @@ -1,38 +1,21 @@ ${(host.substr(host.length-6,6) == '.onion') ? HTML_TOR_HEAD:HTML_HEAD} - <title>${currencyName}-monit</title> + <title>${currencyName}-monit : ${MENU_LANG['ABOUT']}</title> </head> <body> <!-- Afficher le menu --> -<table align="center" width="100%"> - <tr> - <td><a href="willMembers?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WILL_MEMBERS"]}</a></td> - <td><a href="members?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS"]}</a></td> - <td><a href="membersCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS_COUNT"]}</a></td> - <td><a href="blockCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["BLOCK_COUNT"]}</a></td> - <td><a href="monetaryMass?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MONETARY_MASS"]}</a></td> - <td><a href="wotex?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WOTEX"]}</a></td> - <td><a href="about?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["ABOUT"]}</a></td> - <td> - <form action="" method="GET"> - <select name="lg" onchange="this.form.submit()"> - <option name="lg" value="fr" ${MENU_LANG['LG'] == 'fr' ? 'selected' : ''}>FR - <option name="lg" value="en" ${MENU_LANG['LG'] == 'en' ? 'selected' : ''}>EN - </select> - </td> -</tr> -</table> -</select> +${printMenu(MENU_LANG, help, "ABOUT")} </form> -<hr> -<div align="center"> - <br> - <a href="https://github.com/duniter/duniter-currency-monit/blob/master/LICENSE">License AGPL V 3.0</a><br> - <a href="https://github.com/duniter/duniter-currency-monit/">github repository</a> +<div align="left">${LANG['VERSION']} : <a href="https://git.duniter.org/nodes/typescript/modules/duniter-currency-monit/tree/v0.4.11">0.4.11</a></div><br> +<div align="left">${LANG['AUTHOR']} : <a href="https://github.com/librelois">Éloïs Librelois</a></div><br> +<div align="left"><small>${LANG['CONTRIBUTORS']}: <a href="https://github.com/jytou">jytou</a> (translator), <a href="https://github.com/devingfx">devingfx</a> (frontend), <a href="https://github.com/c-geek">cgeek</a> (willMembers and wotex),<a href="https://github.com/M5oul">M5oul</a> (adjustments)</small></div><br> +<div align="left"> + ${LANG['LICENSE']} : <a href="https://github.com/duniter/duniter-currency-monit/blob/master/LICENSE">AGPL V 3.0</a><br> + <a href="https://git.duniter.org/nodes/typescript/modules/duniter-currency-monit">${LANG['GIT_REPOSITORY']}</a> </div><br> -<div align="left">Version : <a href="https://github.com/duniter/duniter-currency-monit/releases/tag/0.3.7">module-0.3.7</a></div><br> -<div align="left">Author : <a href="https://github.com/librelois">Éloïs Librelois</a></div><br> -<div align="left">Contributors : <a href="https://github.com/jytou">jytou</a> (translator), <a href="https://github.com/devingfx">devingfx</a> (frontend), <a href="https://github.com/c-geek">cgeek</a> (willMembers and wotex),<a href="https://github.com/M5oul">M5oul</a> (adjustments)</div><br> -<br> -<div align="left">If you want you can <a href="?lg=${MENU_LANG['LG']}&help=no">disable help</a>.</div><br> \ No newline at end of file +<div align="left">${LANG['IF_YOU_WANT']} <a href="?lg=${MENU_LANG['LG']}&help=no">${LANG['DISABLE_HELP']}</a>.</div><br> +<div align="left">${LANG['DONATE']} :<br> + <a href="https://g1.duniter.fr/#/app/transfer/D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx?comment=thank for currency-monit"><img src="https://librelois.fr/public/duniter_button.png"></a><br> + </div> +<br> \ No newline at end of file diff --git a/views/members.html b/views/members.html index df0c293d08f2eacd24c12f725d8ad7f15eae0bbe..462045c5c590858926962919ed65c154f8b0655a 100755 --- a/views/members.html +++ b/views/members.html @@ -1,38 +1,43 @@ ${(host.substr(host.length-6,6) == '.onion') ? HTML_TOR_HEAD:HTML_HEAD} - <title>${currencyName}-monit</title> + <title>${currencyName}-monit : ${MENU_LANG['MEMBERS']}</title> + <script type="text/javascript">// <![CDATA[ + var table = document.getElementById('table') + var filter = document.getElementById('filter') + + /*filter.onchange = function() { + return filterRows(table, 1, 0, this.value); + }*/ + + // tableElem : HTMLElement + // rowIndex : index du row pour commencer le filtrage (0 indexed) + // tableElem : index du col à prendre en compte (0 indexed) + // tableElem : lettre pour la comparaison + function filterRows(tableElem, rowIndex, colIndex, filterString) { + for (var i = rowIndex, val; i < tableElem.rows.length; ++i) { + val = tableElem.rows[i].cells[colIndex].firstChild.nodeValue; + if (val.substr(0,filterString.length).toLowerCase() != filterString.toLowerCase()) + tableElem.rows[i].style.display = 'none'; + else + tableElem.rows[i].style.display = ''; + } + return false; + } +// ]]></script> </head> <body> <!-- Afficher le menu --> -<table align="center" width="100%"> - <tr> - <td><a href="willMembers?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WILL_MEMBERS"]}</a></td> - <td><a href="members?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS"]}</a></td> - <td><a href="membersCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS_COUNT"]}</a></td> - <td><a href="blockCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["BLOCK_COUNT"]}</a></td> - <td><a href="monetaryMass?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MONETARY_MASS"]}</a></td> - <td><a href="wotex?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WOTEX"]}</a></td> - <td><a href="about?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["ABOUT"]}</a></td> - <td> - <form action="" method="GET"> - <select name="lg" onchange="this.form.submit()"> - <option name="lg" value="fr" ${MENU_LANG['LG'] == 'fr' ? 'selected' : ''}>FR - <option name="lg" value="en" ${MENU_LANG['LG'] == 'en' ? 'selected' : ''}>EN - </select> - </td> -</tr> -</table> -<hr> +${printMenu(MENU_LANG, help, "MEMBERS")} <!-- Afficher le formulaire --> -<input type="number" name="d" value="${days}"/>${LANG["FORM1"]} <select name="sort_by"> +<input type="number" name="d" value="${days}"/> ${LANG["FORM1"]} <select name="sort_by"> <option name="sort_by" value ="idtyWritten">${LANG["SORT_BY_IDTY_WRITTEN"]} <option name="sort_by" value ="expireMembership" ${sort_by == 'expireMembership' ? 'selected' : ''}>${LANG["SORT_BY_EXPIRE_MEMBERSHIP"]} <option name="sort_by" value ="lastRenewal" ${sort_by == 'lastRenewal' ? 'selected' : ''}>${LANG["SORT_LAST_RENEWAL"]} <option name="sort_by" value ="oldestSig" ${sort_by == 'oldestSig' ? 'selected' : ''}>${LANG["SORT_BY_OLDEST_SIG"]} <option name="sort_by" value ="lastSig" ${sort_by == 'lastSig' ? 'selected' : ''}>${LANG["SORT_BY_LAST_SIG"]} <option name="sort_by" value ="centrality" ${sort_by == 'centrality' ? 'selected' : ''}>${LANG["SORT_BY_CENTRALITY"]} -${(USE_WOTB6) ? `<option name="sort_by" value ="quality" ${sort_by == 'quality' ? 'selected' : ''}>${LANG["SORT_BY_QUALITY"]}`:``} +<option name="sort_by" value ="quality" ${sort_by == 'quality' ? 'selected' : ''}>${LANG["SORT_BY_QUALITY"]} <option name="sort_by" value ="sigCount" ${sort_by == 'sigCount' ? 'selected' : ''}>${LANG["SORT_BY_SIG_COUNT"]} </select> ${LANG["ORDER_BY"]} <select name="order"> <option name="order" value ="asc"> ${LANG["ASC"]} @@ -41,7 +46,9 @@ ${(USE_WOTB6) ? `<option name="sort_by" value ="quality" ${sort_by == 'quality' <input type="checkbox" name="centrality" value="yes">${LANG["CHECKBOX_CENTRALITY"]}.<br> <input type="checkbox" name="pendingSigs" value="yes" ${pendingSigs == 'yes' ? 'checked' : ''}>${LANG["CHECKBOX_PENDING_SIGS"]}.<br> <input type="checkbox" name="mode" value="emitted" ${mode == 'emitted' ? 'checked' : ''}>${LANG["CHECKBOX_MODE_SIG"]}.<br> -</form> +<input type="checkbox" name="nextYn" value="yes" ${nextYn == 'yes' ? 'checked' : ''}> ${LANG["NEXT_YN"]}<br> +<input type="checkbox" name="randomList" value="yes" ${randomList == 'yes' ? 'checked' : ''}> ${LANG["RANDOM_LIST"]} +<input type="number" name="randomCounts" value="${numberOfRandomMembers}"/> ${LANG["MEMBERS"]}.<br> <hr> <!-- Afficher la légende et l'aide --> @@ -76,27 +83,31 @@ ${(membersLastCentralityCalcTime==0) ? ` `} `} <br> -${(USE_WOTB6) ? ` <table border="1"> <tr><td align='center' colspan='3'>${LANG["DATA_AT"]} ${timestampToDatetime(membersLastUptime)}</td></tr> - <tr><td align='center'>${LANG["meanMembersReachedByMembersInSingleExtCert"]}</td><td align='center'>${LANG["SENTRIES_REACHED"]}</td><td align='center'>${LANG["MEMBERS_REACHED"]}</td></tr> - <tr><td align='center'>${LANG["SENTRY_CERT"]}</td><td align='center'>${meanSentriesReachedBySentriesInSingleExtCert}%</td><td align='center'>${meanMembersReachedBySentriesInSingleExtCert}%</td></tr> - <tr><td align='center'>${LANG["MEMBER_CERT"]}</td><td align='center'><font color="${(meanSentriesReachedByMembersInSingleExtCert<xpercent) ? 'DarkRed' : 'blue' }"><b>${meanSentriesReachedByMembersInSingleExtCert}%</b></font></td><td align='center'><b>${meanMembersReachedByMembersInSingleExtCert}%</b></td></tr> - <tr><td align='center'><b>${LANG["MEAN_QUALITY"]}</b></td><td align='center'><font color="red"><b>${(meanSentriesReachedByMembersInSingleExtCert/(xpercent*100)).toFixed(2)}</b></font></td><td align='center'><b>${(meanMembersReachedByMembersInSingleExtCert/(xpercent*100)).toFixed(2)}</b></td></tr> + <tr><td align='center'>${LANG["meanMembersReachedByMembers"]}</td><td align='center'>${LANG["SENTRIES_REACHED"]}</td><td align='center'>${LANG["MEMBERS_REACHED"]}</td></tr> + <tr><td align='center'>${LANG["SENTRY_CERT"]}</td><td align='center'>${(meansMembersQuality.meanSentriesReachedBySentries).toFixed(2)}%</td><td align='center'>${(meansMembersQuality.meanMembersReachedBySentries).toFixed(2)}%</td></tr> + <tr><td align='center'>${LANG["MEMBER_CERT"]}</td><td align='center'><font ${(meansMembersQuality.meanSentriesReachedByMembers<xpercent) ? 'color="DarkRed"' : 'color="blue"' }><b>${meansMembersQuality.meanSentriesReachedByMembers}%</b></font></td><td align='center'><b>${meansMembersQuality.meanMembersReachedByMembers}%</b></td></tr> + <tr><td align='center'><b>${LANG["MEAN_QUALITY"]}</b></td><td align='center'><font color="red"><b>${(meansMembersQuality.meanSentriesReachedByMembers/(xpercent*100)).toFixed(2)}</b></font></td><td align='center'><b>${(meansMembersQuality.meanMembersReachedByMembers/(xpercent*100)).toFixed(2)}</b></td></tr> <tr><td align='center'><b>${LANG["PROPORTION_MEMBERS_WITH_QUALITY_UPPER_1"]}</b></td><td align='center'><font color="red"><b>${(proportionMembersWithQualityUpper1*100).toFixed(2)}%</b></font></td><td align='center'><b>${(proportionMembersWithQualityUpper1IfNoSentries*100).toFixed(2)}%</b></td></tr> </table> <br> -`:``} <!-- Afficher le currentBlockchainTimestamp --> -<i>${LANG["CURRENT_BLOCKCHAIN_TIME"]} : ${timestampToDatetime(currentBlockchainTimestamp)}.</i><br> +<i>${LANG["CURRENT_BLOCKCHAIN_TIME"]} : ${timestampToDatetime(currentBlockchainTimestamp)}.<br> +<!-- Afficher le nombre de membres et de membres référents --> +<b>${membersListFiltered.length}</b> ${LANG["MEMBERS"]} ${LANG["OF_WHICH"]} <b>${countSentries}</b> ${LANG['ARE_REFERRING_MEMBERS']} (<b>${((countSentries/membersListFiltered.length)*100).toFixed(2)}%</b>).</i><br> <br> +<!-- Afficher le filtre des membres --> +${LANG["MEMBER_FILTER"]} : <input type="text" name="filter" id="filter" value="" maxlength="20" onchange="filterRows(document.getElementById('table'),1,0,this.value);" onkeypress="this.onchange();" onpaste="this.onchange();" oninput="this.onchange();"/><br> +</form> <!-- On parcour tout les membres pour afficher ceux dont la date d'expiration est dans moins de 'd' jours --> <b>${LANG["TABLE_TITLE"]} ${days} ${LANG["DAYS"]} :</b> -<table border="1"> +<table id="table" border="1"> <!-- Printer les nom des colonnes --> <tr> + <td align="center" style="display :none"></td> <td align="center">${LANG["COL_UID"]}</td> <td align="center">${LANG["COL_IDTY_WRITTEN_TIME"]}</td> <td align="center">${LANG["COL_LAST_RENEWAL"]}</td> @@ -105,19 +116,16 @@ ${(USE_WOTB6) ? ` <td style="background:#000000">-</td> <td align="left" colspan="${nbMaxCertifs}">${(mode=='emitted') ? LANG['COL_LIST_EMITTED_CERT']:LANG['COL_LIST_RECEIVED_CERT']} (${sort_by == 'lastSig' ? LANG["LAST2OLDEST"] : LANG["OLDEST2LAST"]})</td> </tr> - ${membersListFiltered .map( member=> ` <!-- Printer la ligne --> <tr> ${(member.proportion = proportion(member.expireMembershipTimestamp,msValidity,0,120),'')} - <td align="center" style="background:hsla(${member.proportion}, 100%, 50%, 1)"> - <b>${member.uid}</b><br> + <td align="center" style="display :none">${member.uid}</td> + <td align="center" style="background:hsla(${member.proportion}, 100%, 50%, 1)"><b>${member.uid}</b><br> ${(member.isSentry) ? `<font color="blue">${LANG['REFERRING_MEMBER']} : ${LANG['YES']}</font>`:`${LANG['REFERRING_MEMBER']} : ${LANG['NO']}`}<br> - ${(USE_WOTB6) ? ` - ${LANG['QUALITY_EXT']} : <b>${(typeof(membersQualityExt[member.uid])=='undefined') ? `0.00`:membersQualityExt[member.uid]}</b><br> - `:``} + ${LANG['QUALITY_EXT']} : <b>${(typeof(membersQuality(0, member.wotb_id))=='undefined') ? `0.00`:membersQuality(0, member.wotb_id)}</b><br> <b>${(membersLastCentralityCalcTime==0) ? `${LANG['CENTRALITY']} : ?`:` ${LANG['CENTRALITY']} : <b>${(typeof(membersCentrality[member.wotb_id])=='undefined') ? `0`:membersCentrality[member.wotb_id]} `}</b><br> @@ -127,12 +135,10 @@ ${(USE_WOTB6) ? ` : ''} </td> <td align="center" style="background:hsla(${member.proportion}, 100%, 50%, 1)"> - ${timestampToDatetime(member.idtyWrittenTimestamp)}<br> - #${member.idtyWrittenBloc} + <span data-tip="${timestampToDatetime(member.idtyWrittenTimestamp)}">#${member.idtyWrittenBloc}</span> </td> <td align="center" style="background:hsla(${member.proportion}, 100%, 50%, 1)"> - ${timestampToDatetime(member.lastRenewalTimestamp)}<br> - #${member.lastRenewalWrittenBloc} + <span data-tip="${timestampToDatetime(member.lastRenewalTimestamp)}">#${member.lastRenewalWrittenBloc}</span> </td> <td align="center" style="background:hsla(${member.proportion}, 100%, 50%, 1)"> ${timestampToDatetime(member.expireMembershipTimestamp)}</td> @@ -140,9 +146,7 @@ ${(USE_WOTB6) ? ` <font color="${member.detailedDistance.isOutdistanced ? 'red' : 'blue' }"> ${member.detailedDistance.isOutdistanced ? LANG['COL_DISTANCE_isOutdistanced'] : LANG['COL_DISTANCE_isNotOutdistanced'] } - ${(USE_WOTB6) ? ` <br>${member.percentSentriesReached}% (${member.detailedDistance.nbSuccess}/${member.detailedDistance.nbSentries}) - `:``} </font> </td> <td style="background:#000000">-</td> @@ -153,7 +157,8 @@ ${(USE_WOTB6) ? ` <!-- Printer la certification --> <td align="center" style="background:${(cert.validBlockStamp) ? cert.colorPending=color(cert.expires_on,idtyWindow,250) : cert.colorPending='#FF8000'}"> <b>${cert.protagonist}</b><br> - ${timestampToDatetime(cert.timestampExpire)}<br> + <span data-tip="${LANG['EXPIRE_TIME']}">${timestampToDatetime(cert.timestampExpire)}</span><br> + ${LANG["EMITTED"]} #${cert.blockNumber}<br> ${ ( !cert.validBlockStamp || cert.timestampWritable > currentBlockchainTimestamp ) ? ` <font color="DarkRed">[${ (cert.validBlockStamp) ? timestampToDatetime(cert.timestampWritable):LANG['INVALID_BLOCKSTAMP']}]</font> @@ -170,16 +175,13 @@ ${(USE_WOTB6) ? ` <!-- Printer la certification --> <td align="center" style="background:hsla(${proportion(cert.timestampExpire,sigValidity,0,120)}, 100%, 50%, 1 )"> <b>${(mode=='emitted') ? cert.receiver:cert.issuer}</b><br> - ${timestampToDatetime(cert.timestampExpire)}<br> + <span data-tip="${LANG['EXPIRE_TIME']}">${timestampToDatetime(cert.timestampExpire)}</span><br> ${LANG["WRITTEN"]} #${cert.writtenBloc} </td> `).join('')} </tr> `).join('') } - <tr> - <td colspan="${4+nbMaxCertifs}" align="center"> ${LANG["OVERALL"]} : <b>${membersListFiltered.length}</b> ${LANG["MEMBERS"]}.</td> - </tr> </table><br> <hr> diff --git a/views/printMenu.js b/views/printMenu.js new file mode 100644 index 0000000000000000000000000000000000000000..82854f66fd045b972eaf193494820c11f1ef7efb --- /dev/null +++ b/views/printMenu.js @@ -0,0 +1,20 @@ +const constants = require(__dirname + '/../lib/constants') + +module.exports = function printMenu(lang, help, location) { + let htmlMenu = '<ul class="menu">';//'<table align="center" width="100%"><tr>'; + + htmlMenu += `<li class="menu"><a class="${(location=="WILL_MEMBERS") ? 'active':'menu'}" href="willMembers?lg=${lang['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${lang["WILL_MEMBERS"]}</a></li>`; + htmlMenu += `<li class="menu"><a class="${(location=="MEMBERS") ? 'active':'menu'}" href="members?lg=${lang['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${lang["MEMBERS"]}</a></li>`; + htmlMenu += `<li class="menu"><a class="${(location=="MEMBERS_COUNT") ? 'active':'menu'}" href="membersCount?lg=${lang['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${lang["MEMBERS_COUNT"]}</a></li>`; + htmlMenu += `<li class="menu"><a class="${(location=="WOTEX") ? 'active':'menu'}" href="wotex?lg=${lang['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${lang["WOTEX"]}</a></li>`; + htmlMenu += `<li class="menu"><a class="${(location=="GAUSSIAN_WOT_QUALITY") ? 'active':'menu'}" href="gaussianWotQuality?lg=${lang['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${lang["GAUSSIAN_WOT_QUALITY"]}</a></li>`; + htmlMenu += `<li class="menu"><a class="${(location=="BLOCK_COUNT") ? 'active':'menu'}" href="blockCount?lg=${lang['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${lang["BLOCK_COUNT"]}</a></li>`; + htmlMenu += `<li class="menu"><a class="${(location=="MONETARY_MASS") ? 'active':'menu'}" href="monetaryMass?lg=${lang['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${lang["MONETARY_MASS"]}</a></li>`; + htmlMenu += `<li class="menu"><a class="${(location=="ABOUT") ? 'active':'menu'}" href="about?lg=${lang['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${lang["ABOUT"]}</a></li>`; + htmlMenu += `<li class="menu" style="float:right"><form action="" method="GET"><a class="menu"><select name="lg" onchange="this.form.submit()">`; + htmlMenu += `<option name="lg" value="fr" ${lang['LG'] == 'fr' ? 'selected' : ''}>FR`; + htmlMenu += `<option name="lg" value="en" ${lang['LG'] == 'en' ? 'selected' : ''}>EN`; + htmlMenu += '</select></a></li></ul><br>';//</td></tr></table><hr>'; + + return htmlMenu; +} diff --git a/views/willMembers.html b/views/willMembers.html index b8b4ec30e07c37e016b25ff7307f501cf3cc0839..da3010ddbb056c3aefb9602767c6e10788ba565a 100755 --- a/views/willMembers.html +++ b/views/willMembers.html @@ -1,7 +1,29 @@ ${(host.substr(host.length-6,6) == '.onion') ? HTML_TOR_HEAD:HTML_HEAD} - <title>${currencyName}-monit</title> + <title>${currencyName}-monit : ${MENU_LANG['WILL_MEMBERS']}</title> <script type="text/javascript">// <![CDATA[ + var table = document.getElementById('table') + var filter = document.getElementById('filter') + + /*filter.onchange = function() { + return filterRows(table, 1, 0, this.value); + }*/ + + // tableElem : HTMLElement + // rowIndex : index du row pour commencer le filtrage (0 indexed) + // tableElem : index du col à prendre en compte (0 indexed) + // tableElem : lettre pour la comparaison + function filterRows(tableElem, rowIndex, colIndex, filterString) { + for (var i = rowIndex, val; i < tableElem.rows.length; ++i) { + val = tableElem.rows[i].cells[colIndex].firstChild.nodeValue; + if (val.substr(0,filterString.length).toLowerCase() != filterString.toLowerCase()) + tableElem.rows[i].style.display = 'none'; + else + tableElem.rows[i].style.display = ''; + } + return false; + } + function deroule(champ,valeur) { /*valeur est la hauteur en pixel de la zone*/ @@ -20,25 +42,7 @@ ${(host.substr(host.length-6,6) == '.onion') ? HTML_TOR_HEAD:HTML_HEAD} <body> <!-- Afficher le menu --> -<table align="center" width="100%"> - <tr> - <td><a href="willMembers?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WILL_MEMBERS"]}</a></td> - <td><a href="members?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS"]}</a></td> - <td><a href="membersCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS_COUNT"]}</a></td> - <td><a href="blockCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["BLOCK_COUNT"]}</a></td> - <td><a href="monetaryMass?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MONETARY_MASS"]}</a></td> - <td><a href="wotex?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WOTEX"]}</a></td> - <td><a href="about?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["ABOUT"]}</a></td> - <td> - <form action="" method="GET"><input type="hidden" name="help" value="${help}"> - <select name="lg" onchange="this.form.submit()"> - <option name="lg" value="fr" ${MENU_LANG['LG'] == 'fr' ? 'selected' : ''}>FR - <option name="lg" value="en" ${MENU_LANG['LG'] == 'en' ? 'selected' : ''}>EN - </select> - </td> -</tr> -</table> -<hr> +${printMenu(MENU_LANG, help, "WILL_MEMBERS")} <!-- Afficher le formulaire --> <input type="number" name="d" value="${days}"/>${LANG["DAYS"]} - ${LANG["SORT_BY"]} @@ -50,10 +54,9 @@ ${(host.substr(host.length-6,6) == '.onion') ? HTML_TOR_HEAD:HTML_HEAD} <option name="order" value ="asc">${LANG["ORDER_ASC"]} <option name="order" value ="desc" ${order == 'desc' ? 'selected' : ''}>${LANG["ORDER_DESC"]} </select> <input type="submit" value="${LANG["SUBMIT_TXT"]}"><br> - <input type="checkbox" name="showIdtyWithZeroCert" value="yes" ${showIdtyWithZeroCert == 'yes' ? 'checked' : ''}>${LANG["CHECKBOX_SHOW_IDTY_WITH_ZERO_CERT"]}<br> - - <input type="checkbox" name="sortSig" value="Availability" ${sortSig == 'Availability' ? 'checked' : ''}>${LANG["CHECKBOX_SORT_SIG"]} + <input type="checkbox" name="sortSig" value="Availability" ${sortSig == 'Availability' ? 'checked' : ''}>${LANG["CHECKBOX_SORT_SIG"]}<br> + ${LANG["IDTY_FILTER"]} : <input type="text" name="filter" id="filter" value="" maxlength="20" onchange="filterRows(document.getElementById('table'),2,0,this.value);" onkeypress="this.onchange();" onpaste="this.onchange();" oninput="this.onchange();"/> </form> <br> <hr> @@ -83,7 +86,6 @@ ${(help != 'no') ? ` `:''} <!-- Afficher l'état de tension de la toile de confiance --> -${ (USE_WOTB6) ? ` <div id="zone2" style="width: 100%; height: 20px; background: White; border: 1px solid DimGrey; transition: height 1s; -moz-transition: height 1s;-webkit-transition: height 1s;-o-transition: height 1s; overflow: hidden;"> <div id="bandeau2" style="height: 20px; width: 100%; font-size: medium; color: white; background-color: darkgrey;" onmouseover="deroule(2,150);" onmouseout="deroule(2,20);"><b>${LANG["WOT_TENSION_STATE"]}</b> </div> @@ -142,18 +144,18 @@ ${ (USE_WOTB6) ? ` </table> </div> </div> -`:``} <!-- Afficher le currentBlockchainTimestamp et le nombre d'identités au dossier complet --> ${LANG["BLOCKCHAIN_TIME"]} : <b>${timestampToDatetime(currentBlockchainTimestamp)}</b> (#<b>${currentBlockNumber}</b>).<br> ${LANG["COUNT_READY_MEMBERS"]} : <b>${countMembersWithSigQtyValidCert}</b>.<br> <!-- Tableau de toutes les identités en piscine --> -<table border="1"> - <tr><td colspan="${nbMaxCertifs+3}" align='center'>${LANG["TABLE_TITLE1"]} <b>${days}</b> ${LANG["TABLE_TITLE2"]}</td></tr> +<table id="table" border="1"> + <tr><td colspan="${nbMaxCertifs+4}" align='center'>${LANG["TABLE_TITLE1"]} <b>${days}</b> ${LANG["TABLE_TITLE2"]}</td></tr> <!-- Printer les nom des colonnes --> <tr> - <td align='center'>${LANG['IDENTITY']}</td><td align='center'>${LANG['MEMBERSHIP_CASE']}</td> + <td align="center" style="display :none"></td> + <td align='center'>${LANG['IDENTITY']}</td><td align='center'>${LANG['MEMBERSHIP_CASE_FULL']}</td><td align='center'>${LANG['DISTANCE_RULE']}</td> <td style="background:#000000">-</td> <td align='center' colspan="${nbMaxCertifs}">${sortSig == "Availability" ? LANG['COL_4_WITH_AVAIlABILITY_SORT'] @@ -165,30 +167,34 @@ ${ (USE_WOTB6) ? ` <!--Printer la ligne--> <tr> + <td align="center" style="display :none">${idty['uid']}</td> <td align="center" style="background:${(idty.validBlockStamp) ? ((idty.expires_on > 0) ? idty.colorPending=color(idty.expires_on,idtyWindow,250):idty.colorPending='#FF0000'): idty.colorPending='#FF8000'}"> - <a href="wotex?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}&to=${idty['uid']}${idty['wotexId']}&pending=on&mode=undefined">${idty['uid'].substring(0, 25)}</a> - <br>${idty['pubkey'].substring(0, 16)} - <br>${LANG['EMITTED_ON']} ${timestampToDatetime(idty['creationTimestamp'])} - <br>${LANG['AT_BLOCK']} #${idty['BlockNumber']} - ${(idty.expires_on > 0) ? '<br><b>'+LANG['EXPIRE_ON']+' '+timestampToDatetime(idty.expires_on)+'</b>':``} + <a href="wotex?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}&to=${idty['uid']}${idty['wotexId']}&pending=on&mode=undefined">${idty['uid'].substring(0, 25)}</a> + <br><span data-tip="${LANG['PUBKEY_PART']} ">${idty['pubkey'].substring(0, 16)}</span> + <br><b><span data-tip="${LANG['EMITTED_ON']} ${timestampToDatetime(idty['creationTimestamp'])}">#${idty['BlockNumber']}</span></b> + ${(idty.expires_on > 0) ? '<br><b><span data-tip="'+LANG['EXPIRE_TIME']+'">'+timestampToDatetime(idty.expires_on)+'</span></b>':``} ${(!idty.validBlockStamp) ? ` <br><font color="DarkRed">[${LANG['INVALID_BLOCKSTAMP']}]</font> `:``} </td> - ${(idty.expires_on > 0) ? ` + ${(idty.expires_on && !idty.idtyRevoked > 0) ? ` <td align='center' style="background:${idty.colorPending}"> - ${(idty.membership && !idty.detailedDistance.isOutdistanced && idty.nbValidPendingCert >= sigQty) ? `<font color="green">${LANG['OK']}` : `<font color="DarkRed">${LANG['KO']}` }</font><br> - ${LANG['MEMBERSHIP_ASKED']} : ${idty.membership? `<font color="green">${LANG['YES']}` : `<font color="DarkRed">${LANG['NO']}` }</font><br> - ${ (USE_WOTB6) ? ` - <font color="${idty.detailedDistance.isOutdistanced ? 'DarkRed' : 'blue' }">${idty.percentSentriesReached}% (${idty.detailedDistance.nbSuccess}/${idty.detailedDistance.nbSentries}) - <br>${LANG['QUALITY_CERTIFIERS']} : <b>${(idty.validBlockStamp) ? ((idty.detailedDistance.nbSuccess/idty.detailedDistance.nbSentries)/xpercent).toFixed(2):`0.00`}</b></font> + ${(idty.membership && !idty.detailedDistance.isOutdistanced && idty.nbValidPendingCert >= sigQty) ? ` + <font color="green">${LANG['YES']} `:` - <font color="${idty.detailedDistance ? 'DarkRed' : 'blue' }">${LANG['DISTANCE']} : ${idty.detailedDistance ? LANG['KO']:LANG['OK'] }</font> + <font color="DarkRed">${LANG['NO']} + ${!idty.membership? `<br>${LANG['MEMBERSHIP_NOT_ASKED']}`:``} + ${(idty.nbValidPendingCert < sigQty) ? `<br>${LANG['MISS']} <b>${(sigQty-idty.nbValidPendingCert)}</b> ${LANG['CERTS']}`:``} + ${idty.detailedDistance.isOutdistanced? `<br>${LANG['DISTANCE']} ${LANG['KO']}`:``} + </font> `} - <br>${LANG['CERTIFIERS_COUNT']} : ${(idty.nbValidPendingCert >= sigQty) ? `<font color="green">${idty.nbValidPendingCert}/${sigQty}` : `<font color="DarkRed">${idty.nbValidPendingCert}/${sigQty}` }</font> + </td> + <td align='center' style="background:${idty.colorPending}"> + <font color="${idty.detailedDistance.isOutdistanced ? 'DarkRed' : 'blue' }">${idty.percentSentriesReached}% (${idty.detailedDistance.nbSuccess}/${idty.detailedDistance.nbSentries}) + <br>${LANG['QUALITY_CERTIFIERS']} : <b>${(idty.validBlockStamp) ? ((idty.detailedDistance.nbSuccess/idty.detailedDistance.nbSentries)/xpercent).toFixed(2):`0.00`}</b></font> </td> `:` - <td align='center' style="background:#FF0000"><b>${LANG['IDTY_REVOKED']}</b></td> + <td align='center' colspan=2 style="background:#FF0000"><b>${LANG['IDTY_REVOKED']}</b></td> `} <td style="background:#000000">-</td> @@ -199,11 +205,9 @@ ${ (USE_WOTB6) ? ` ${cert.issuerIsSentry ? '<b>':''} <a href="wotex?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}&to=${cert['from']}&pending=on&mode=undefined">${cert['from']}</a> ${cert.issuerIsSentry ? '</b>':''} - ${ (USE_WOTB6) ? ` <br>${LANG['QUALITY']} : <b>${(typeof(membersQualityExt[cert.from])=='undefined' || !cert.validBlockStamp) ? `0.00`:membersQualityExt[cert.from]}</b> - `:''} - <br>${timestampToDatetime(cert.timestampExpire)} - <br>#${cert['blockNumber']} + <br><b><span data-tip="${LANG['EMITTED_ON']} ${timestampToDatetime(cert['creationTimestamp'])}">#${cert['blockNumber']}</span></b> + <br><span data-tip="${LANG['EXPIRE_TIME']}">${timestampToDatetime(cert.timestampExpire)}</span> ${j==(4+idty.pendingCertifications.length-idty.nbValidPendingCert)?'<b>':''} ${ ( !cert.validBlockStamp || cert['timestampWritable'] > currentBlockchainTimestamp ) ? ` <br><font color="DarkRed">[${ (cert.validBlockStamp) ? timestampToDatetime(cert['timestampWritable']):LANG['INVALID_BLOCKSTAMP']}]</font> @@ -220,7 +224,7 @@ ${ (USE_WOTB6) ? ` `).join('')} <tr> - <td colspan="${nbMaxCertifs+3}" align="center"> + <td colspan="${nbMaxCertifs+4}" align="center"> ${LANG['LAST_TR1']} : <b>${idtysListFiltered.length}</b> ${LANG['LAST_TR2']}. </td> </tr> diff --git a/views/wotex.html b/views/wotex.html index 86d8dfc0c9dbaa85ebf213b5cbd13b2f0f27d9f2..a697e76a13549e6127f3c8e2c2666e0875fad06b 100755 --- a/views/wotex.html +++ b/views/wotex.html @@ -1,6 +1,6 @@ <!-- Inclure l'en-tête commun --> ${(host.substr(host.length-6,6) == '.onion') ? HTML_TOR_HEAD:HTML_HEAD} - <title>${currencyName}-monit</title> + <title>${currencyName}-monit : ${MENU_LANG['WOTEX']}</title> <!-- Style css spécifique de wotex --> <style> .sentry { @@ -117,25 +117,7 @@ ${(host.substr(host.length-6,6) == '.onion') ? HTML_TOR_HEAD:HTML_HEAD} <body onload="onLoadedPage()"> <!-- Afficher le menu --> - <table align="center" width="100%"> - <tr> - <td><a href="willMembers?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WILL_MEMBERS"]}</a></td> - <td><a href="members?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS"]}</a></td> - <td><a href="membersCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MEMBERS_COUNT"]}</a></td> - <td><a href="blockCount?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["BLOCK_COUNT"]}</a></td> - <td><a href="monetaryMass?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["MONETARY_MASS"]}</a></td> - <td><a href="wotex?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["WOTEX"]}</a></td> - <td><a href="about?lg=${MENU_LANG['LG']}${(typeof(help) != 'undefined' && help == 'no') ? '&help=no':''}">${MENU_LANG["ABOUT"]}</a></td> - <td> - <form action="" method="GET"> - <select name="lg" onchange="this.form.submit()"> - <option name="lg" value="fr" ${MENU_LANG['LG'] == 'fr' ? 'selected' : ''}>FR - <option name="lg" value="en" ${MENU_LANG['LG'] == 'en' ? 'selected' : ''}>EN - </select> - </td> - </tr> - </table> - <hr/> + ${printMenu(MENU_LANG, help, "WOTEX")} <!-- Afficher le titre et le formulaire --> <h1>${LANG['TITLE']}</h1> @@ -158,12 +140,12 @@ ${(host.substr(host.length-6,6) == '.onion') ? HTML_TOR_HEAD:HTML_HEAD} <!-- Afficher la légende --> ${(typeof(help) == 'undefined' || help != 'no') ? ` <p>${LANG['LEGEND_TITLE']} :</p> - <ul> - <li><span class="isMember">${LANG['LEGEND_BLACK']}</span></li> - <li><span class="isSentry">${LANG['LEGEND_BLUE']}</span></li> - <li><span class="isPending">${LANG['LEGEND_ORANGE']}</span></li> - <li><span class="isNonMember">${LANG['LEGEND_RED']}</span></li> - </ul> + + <span class="isMember">${LANG['LEGEND_BLACK']}</span></br> + <span class="isSentry">${LANG['LEGEND_BLUE']}</span></br> + <span class="isPending">${LANG['LEGEND_ORANGE']}</span></br> + <span class="isNonMember">${LANG['LEGEND_RED']}</span></br> + <hr/> `:``} <!-- Afficher tout les plus courts chemins -->