diff --git a/www/index.html b/www/index.html index 6d86e543ee8a9064cee048804a5523edc9d0cd79..f8d64a11f70b03b3d9b65e91a4168dbabb8efe57 100644 --- a/www/index.html +++ b/www/index.html @@ -200,6 +200,7 @@ <script src="dist/dist_js/plugins/graph/js/controllers/currency-controllers.js"></script> <script src="dist/dist_js/plugins/graph/js/controllers/account-controllers.js"></script> <script src="dist/dist_js/plugins/graph/js/controllers/docstats-controllers.js"></script> +<script src="dist/dist_js/plugins/graph/js/controllers/synchro-controllers.js"></script> <!--endRemoveIf(ubuntu)--> <!-- Map plugin --> diff --git a/www/plugins/graph/i18n/locale-fr-FR.json b/www/plugins/graph/i18n/locale-fr-FR.json index af9f181845731507e68ab8963055aaacbd11bcbc..1501f1268fe804ad67849106efdedbe976e37356 100644 --- a/www/plugins/graph/i18n/locale-fr-FR.json +++ b/www/plugins/graph/i18n/locale-fr-FR.json @@ -89,6 +89,14 @@ "TITLE": "Autres documents", "HISTORY_DELETE": "Suppressions de documents", } + }, + "SYNCHRO": { + "EXECUTION": { + "TITLE": "Synchronisation", + "INSERTS": "Insertions", + "UPDATES": "Mises à jour", + "DELETES": "Suppressions" + } } } } diff --git a/www/plugins/graph/js/controllers/common-controllers.js b/www/plugins/graph/js/controllers/common-controllers.js index 95fb03db848488a8f267b226eca5d8fc4d81aacf..1f79881825fa04ffe562d5921a9043b6031cfbc7 100644 --- a/www/plugins/graph/js/controllers/common-controllers.js +++ b/www/plugins/graph/js/controllers/common-controllers.js @@ -72,6 +72,7 @@ function GpCurrencyAbstractController($scope, $filter, $ionicPopover, $ionicHist console.warn('[graph] currency.firstBlockTime not loaded ! Should have been loaded by currrency service!'); } $scope.formData.currencyAge = _truncDate(esHttp.date.now()) - $scope.formData.firstBlockTime; + return $scope.enter(e, state); }); } diff --git a/www/plugins/graph/js/controllers/docstats-controllers.js b/www/plugins/graph/js/controllers/docstats-controllers.js index f864345ab8af23f951429f631998311c97362212..d868d1101fc0bb5fd8c30f870c1bd1ecdabe0170 100644 --- a/www/plugins/graph/js/controllers/docstats-controllers.js +++ b/www/plugins/graph/js/controllers/docstats-controllers.js @@ -9,7 +9,8 @@ angular.module('cesium.graph.docstats.controllers', ['chart.js', 'cesium.graph.s url: "/data/stats?stepUnit&t&hide&scale", views: { 'menuContent': { - templateUrl: "plugins/graph/templates/docstats/view_doc_stats_lg.html" + templateUrl: "plugins/graph/templates/docstats/view_stats.html", + controller: 'GpDocStatsCtrl' } } }); @@ -43,7 +44,7 @@ function GpDocStatsController($scope, $controller, $q, $translate, gpColor, gpDa key: 'user_profile', label: 'GRAPH.DOC_STATS.USER.USER_PROFILE', color: gpColor.rgba.royal(1), - pointHoverBackgroundColor: gpColor.rgba.gray(1) + pointHoverBackgroundColor: gpColor.rgba.royal(1) }, { key: 'user_settings', @@ -161,7 +162,6 @@ function GpDocStatsController($scope, $controller, $q, $translate, gpColor, gpDa $scope.load = function(updateTimePct) { - return $q.all([ // Get i18n keys (chart title, series labels, date patterns) $translate($scope.charts.reduce(function(res, chart) { diff --git a/www/plugins/graph/js/controllers/synchro-controllers.js b/www/plugins/graph/js/controllers/synchro-controllers.js new file mode 100644 index 0000000000000000000000000000000000000000..580ce9d93ab67a5fd52c4f03f35e5c7e8cc5fd09 --- /dev/null +++ b/www/plugins/graph/js/controllers/synchro-controllers.js @@ -0,0 +1,187 @@ + +angular.module('cesium.graph.synchro.controllers', ['chart.js', 'cesium.graph.services', 'cesium.graph.common.controllers']) + + .config(function($stateProvider, PluginServiceProvider, csConfig) { + 'ngInject'; + + $stateProvider + .state('app.doc_synchro_lg', { + url: "/data/synchro?stepUnit&t&hide&scale", + views: { + 'menuContent': { + templateUrl: "plugins/graph/templates/synchro/view_stats.html", + controller: "GpSynchroCtrl" + } + } + }); + + var enable = csConfig.plugins && csConfig.plugins.es; + if (enable) { + // TODO: add buttons to link with doc stats + } + }) + + + .controller('GpSynchroCtrl', GpSynchroController) +; + +function GpSynchroController($scope, $controller, $q, $translate, gpColor, gpData, $filter) { + 'ngInject'; + + // Initialize the super class and extend it. + angular.extend(this, $controller('GpCurrencyAbstractCtrl', {$scope: $scope})); + + $scope.hiddenDatasets = []; + + $scope.charts = [ + + // User count + { + id: 'user', + title: 'GRAPH.SYNCHRO.EXECUTION.TITLE', + series: [ + { + key: 'inserts', + label: 'GRAPH.SYNCHRO.EXECUTION.INSERTS', + color: gpColor.rgba.royal(1), + pointHoverBackgroundColor: gpColor.rgba.royal(1) + }, + { + key: 'updates', + label: 'GRAPH.SYNCHRO.EXECUTION.UPDATES', + color: gpColor.rgba.calm(1), + pointHoverBackgroundColor: gpColor.rgba.calm(1) + }, + { + key: 'deletes', + label: 'GRAPH.SYNCHRO.EXECUTION.DELETES', + color: gpColor.rgba.assertive(0.5), + pointHoverBackgroundColor: gpColor.rgba.assertive(1) + } + ] + } + ]; + + var formatInteger = $filter('formatInteger'); + + $scope.defaultChartOptions = { + responsive: true, + maintainAspectRatio: $scope.maintainAspectRatio, + title: { + display: true + }, + legend: { + display: true, + onClick: $scope.onLegendClick + }, + scales: { + yAxes: [ + { + stacked: true, + id: 'y-axis' + } + ] + }, + tooltips: { + enabled: true, + mode: 'index', + callbacks: { + label: function(tooltipItems, data) { + return data.datasets[tooltipItems.datasetIndex].label + + ': ' + formatInteger(tooltipItems.yLabel); + } + } + } + }; + + $scope.init = function(e, state) { + if (state && state.stateParams) { + // Manage URL parameters + } + }; + + $scope.load = function(updateTimePct) { + + return $q.all([ + // Get i18n keys (chart title, series labels, date patterns) + $translate($scope.charts.reduce(function(res, chart) { + return res.concat(chart.series.reduce(function(res, serie) { + return res.concat(serie.label); + }, [chart.title])); + }, [ + 'COMMON.DATE_PATTERN', + 'COMMON.DATE_SHORT_PATTERN', + 'COMMON.DATE_MONTH_YEAR_PATTERN' + ])), + + // get Data + gpData.synchro.execution.get($scope.formData) + ]) + .then(function(result) { + var translations = result[0]; + var datePatterns = { + hour: translations['COMMON.DATE_PATTERN'], + day: translations['COMMON.DATE_SHORT_PATTERN'], + month: translations['COMMON.DATE_MONTH_YEAR_PATTERN'] + }; + + result = result[1]; + console.log("TODO", result); + if (!result || !result.times) return; // no data + $scope.times = result.times; + + // Labels + var labelPattern = datePatterns[$scope.formData.rangeDuration]; + $scope.labels = result.times.reduce(function(res, time) { + return res.concat(moment.unix(time).local().format(labelPattern)); + }, []); + + // Update range options with received values + $scope.updateRange(result.times[0], result.times[result.times.length-1], updateTimePct); + + $scope.setScale($scope.scale); + + // For each chart + _.forEach($scope.charts, function(chart){ + + // Data + chart.data = []; + _.forEach(chart.series, function(serie){ + chart.data.push(result[serie.key]||[]); + }); + + // Options (with title) + chart.options = angular.copy($scope.defaultChartOptions); + chart.options.title.text = translations[chart.title]; + + // Series datasets + chart.datasetOverride = chart.series.reduce(function(res, serie) { + return res.concat({ + yAxisID: 'y-axis', + type: 'bar', + label: translations[serie.label], + fill: true, + borderColor: serie.color, + borderWidth: 2, + backgroundColor: serie.color, + pointBackgroundColor: serie.color, + pointBorderColor: gpColor.rgba.white(), + pointHoverBackgroundColor: serie.pointHoverBackgroundColor||serie.color, + pointHoverBorderColor: gpColor.rgba.translucent(), + pointRadius: 3 + }); + }, []); + }); + }); + + }; + + $scope.onChartClick = function(data, e, item) { + if (!item) return; + console.log('Click on item index='+ item._index); + var from = $scope.times[item._index]; + var to = moment.unix(from).utc().add(1, $scope.formData.rangeDuration).unix(); + }; + + +} diff --git a/www/plugins/graph/js/plugin.js b/www/plugins/graph/js/plugin.js index d91d14b069a5bc908d375c902f547dd35073ed27..afb599035bba0dfcda52812c39223e1db11cd0e0 100644 --- a/www/plugins/graph/js/plugin.js +++ b/www/plugins/graph/js/plugin.js @@ -9,5 +9,6 @@ angular.module('cesium.graph.plugin', [ 'cesium.graph.currency.controllers', 'cesium.graph.account.controllers', 'cesium.graph.docstats.controllers', + 'cesium.graph.synchro.controllers' ]) ; diff --git a/www/plugins/graph/js/services/data-services.js b/www/plugins/graph/js/services/data-services.js index c7f92796f298e13e9a77ec2843ab64ac1c457587..b9bfdfdfb178b852367603ce58d3da5a7b70342f 100644 --- a/www/plugins/graph/js/services/data-services.js +++ b/www/plugins/graph/js/services/data-services.js @@ -10,6 +10,9 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es. wot: {}, blockchain: {}, docstat: {}, + synchro: { + execution: {} + }, raw: { block: { search: esHttp.post('/:currency/block/_search') @@ -25,6 +28,9 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es. }, docstat: { search: esHttp.post('/docstat/record/_search') + }, + synchro: { + search: esHttp.post('/:currency/synchro/_search') } }, regex: { @@ -775,6 +781,114 @@ angular.module('cesium.graph.data.services', ['cesium.wot.services', 'cesium.es. }); }; + + /** + * Graph: "statictics on ES documents" + * @param currency + * @returns {*} + */ + exports.synchro.execution.get = function(options) { + + options = _initRangeOptions(options); + + var jobs = []; + + var from = moment.unix(options.startTime).utc().startOf(options.rangeDuration); + var to = moment.unix(options.endTime).utc().startOf(options.rangeDuration); + var ranges = []; + while(from.isBefore(to)) { + + ranges.push({ + from: from.unix(), + to: from.add(1, options.rangeDuration).unix() + }); + + // Flush if max range count, or just before loop condition end (fix #483) + var flush = (ranges.length === options.maxRangeSize) || !from.isBefore(to); + if (flush) { + var request = { + size: 0, + aggs: { + range: { + range: { + field: "time", + ranges: ranges + }, + aggs: { + result: { + nested: { + path: "result" + }, + aggs: { + inserts : { + sum: { + field : "result.inserts" + } + }, + updates : { + sum: { + field : "result.updates" + } + }, + deletes : { + sum: { + field : "result.deletes" + } + } + } + } + } + } + } + + }; + + // prepare next loop + ranges = []; + + if (jobs.length == 10) { + console.error('Too many parallel jobs!'); + from = moment.unix(options.endTime).utc(); // stop while + } + else { + jobs.push( + exports.raw.synchro.search(request, {currency: options.currency}) + .then(function (res) { + var aggs = res.aggregations; + + return (aggs.range && aggs.range.buckets || []).reduce(function (res, agg) { + return res.concat({ + from: agg.from, + to: agg.to, + inserts: agg.result.inserts.value, + updates: agg.result.inserts.value, + deletes: agg.result.deletes.value + }); + }, []); + }) + ); + } + } + } // loop + + return $q.all(jobs) + .then(function(res) { + res = res.reduce(function(res, hits){ + if (!hits || !hits.length) return res; + return res.concat(hits); + }, []); + + res = _.sortBy(res, 'from'); + + return { + times: _.pluck(res, 'from'), + inserts: _.pluck(res, 'inserts'), + updates: _.pluck(res, 'updates'), + deletes: _.pluck(res, 'deletes') + }; + }); + }; + return exports; }) diff --git a/www/plugins/graph/templates/docstats/graph_doc_stats.html b/www/plugins/graph/templates/docstats/graph.html similarity index 100% rename from www/plugins/graph/templates/docstats/graph_doc_stats.html rename to www/plugins/graph/templates/docstats/graph.html diff --git a/www/plugins/graph/templates/docstats/view_doc_stats_lg.html b/www/plugins/graph/templates/docstats/view_doc_stats_lg.html deleted file mode 100644 index e18218d7a7f9c19a2451b030d9df177f1c4fcf70..0000000000000000000000000000000000000000 --- a/www/plugins/graph/templates/docstats/view_doc_stats_lg.html +++ /dev/null @@ -1,33 +0,0 @@ -<ion-view left-buttons="leftButtons" - cache-view="false"> - <ion-nav-title> - {{'GRAPH.DOC_STATS.TITLE' | translate}} - </ion-nav-title> - - <ion-content scroll="true" class="padding" > - - - - <div class="list" > - - <!-- Doc stat --> - <ng-controller ng-controller="GpDocStatsCtrl" > - - <div class="center padding" ng-if="loading"> - <ion-spinner icon="android"></ion-spinner> - </div> - - <div class="item no-padding-xs" ng-if="!loading" - ng-repeat="chart in charts" - ng-include="'plugins/graph/templates/docstats/graph_doc_stats.html'" - ng-init="setSize(250, 1000)"> - </div> - </ng-controller> - - - - </div> - - </ion-content> - -</ion-view> diff --git a/www/plugins/graph/templates/docstats/view_stats.html b/www/plugins/graph/templates/docstats/view_stats.html new file mode 100644 index 0000000000000000000000000000000000000000..dfea0e947cae1f356e5ca05b660a1c8e5fd112a3 --- /dev/null +++ b/www/plugins/graph/templates/docstats/view_stats.html @@ -0,0 +1,26 @@ +<ion-view left-buttons="leftButtons" + cache-view="false"> + <ion-nav-title> + {{'GRAPH.DOC_STATS.TITLE' | translate}} + </ion-nav-title> + + <ion-content scroll="true" class="padding" > + + <div class="list" > + + <!-- Doc stat --> + <div class="center padding" ng-if="loading"> + <ion-spinner icon="android"></ion-spinner> + </div> + + <div class="item no-padding-xs" ng-if="!loading" + ng-repeat="chart in charts" + ng-include="'plugins/graph/templates/docstats/graph.html'" + ng-init="setSize(250, 1000)"> + </div> + + </div> + + </ion-content> + +</ion-view> diff --git a/www/plugins/graph/templates/synchro/graph.html b/www/plugins/graph/templates/synchro/graph.html new file mode 100644 index 0000000000000000000000000000000000000000..19ac3b723b553020daf60208a1d88f7541e063f9 --- /dev/null +++ b/www/plugins/graph/templates/synchro/graph.html @@ -0,0 +1,22 @@ + + <!-- graphs button bar --> + <div class="button-bar-inline " + style="top: 33px; margin-top:-33px; position: relative;"> + <button + class="button button-stable button-clear no-padding-xs pull-right" + ng-click="showActionsPopover($event)"> + <i class="icon ion-navicon-round"></i> + </button> + </div> + + <canvas id="synchro-chart-{{chart.id}}" + class="chart-bar" + height="{{height}}" + width="{{width}}" + chart-data="chart.data" + chart-labels="labels" + chart-dataset-override="chart.datasetOverride" + chart-options="chart.options"> + </canvas> + + <ng-include src="'plugins/graph/templates/common/graph_range_bar.html'"></ng-include> diff --git a/www/plugins/graph/templates/synchro/view_stats.html b/www/plugins/graph/templates/synchro/view_stats.html new file mode 100644 index 0000000000000000000000000000000000000000..ec7f0ad311175c9e8c6cfae7f57edf16d4f77fbc --- /dev/null +++ b/www/plugins/graph/templates/synchro/view_stats.html @@ -0,0 +1,25 @@ +<ion-view left-buttons="leftButtons" + cache-view="false"> + <ion-nav-title> + {{'GRAPH.DOC_STATS.TITLE' | translate}} + </ion-nav-title> + + <ion-content scroll="true" class="padding" > + + <div class="list" > + + <div class="center padding" ng-if="loading"> + <ion-spinner icon="android"></ion-spinner> + </div> + + <div class="item no-padding-xs" ng-if="!loading" + ng-repeat="chart in charts" + ng-include="'plugins/graph/templates/synchro/graph.html'" + ng-init="setSize(250, 1000)"> + </div> + + </div> + + </ion-content> + +</ion-view>