http://git-wip-us.apache.org/repos/asf/flink/blob/624f8ae9/flink-runtime-web/web-dashboard/web/js/index.js ---------------------------------------------------------------------- diff --git a/flink-runtime-web/web-dashboard/web/js/index.js b/flink-runtime-web/web-dashboard/web/js/index.js index 6d9df04..b3dbd0a 100644 --- a/flink-runtime-web/web-dashboard/web/js/index.js +++ b/flink-runtime-web/web-dashboard/web/js/index.js @@ -1,2249 +1,2 @@ -angular.module('flinkApp', ['ui.router', 'angularMoment', 'dndLists']).run(["$rootScope", function($rootScope) { - $rootScope.sidebarVisible = false; - return $rootScope.showSidebar = function() { - $rootScope.sidebarVisible = !$rootScope.sidebarVisible; - return $rootScope.sidebarClass = 'force-show'; - }; -}]).value('flinkConfig', { - jobServer: '', - "refresh-interval": 10000 -}).run(["JobsService", "MainService", "flinkConfig", "$interval", function(JobsService, MainService, flinkConfig, $interval) { - return MainService.loadConfig().then(function(config) { - angular.extend(flinkConfig, config); - JobsService.listJobs(); - return $interval(function() { - return JobsService.listJobs(); - }, flinkConfig["refresh-interval"]); - }); -}]).config(["$uiViewScrollProvider", function($uiViewScrollProvider) { - return $uiViewScrollProvider.useAnchorScroll(); -}]).run(["$rootScope", "$state", function($rootScope, $state) { - return $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState) { - if (toState.redirectTo) { - event.preventDefault(); - return $state.go(toState.redirectTo, toParams); - } - }); -}]).config(["$stateProvider", "$urlRouterProvider", function($stateProvider, $urlRouterProvider) { - $stateProvider.state("overview", { - url: "/overview", - views: { - main: { - templateUrl: "partials/overview.html", - controller: 'OverviewController' - } - } - }).state("running-jobs", { - url: "/running-jobs", - views: { - main: { - templateUrl: "partials/jobs/running-jobs.html", - controller: 'RunningJobsController' - } - } - }).state("completed-jobs", { - url: "/completed-jobs", - views: { - main: { - templateUrl: "partials/jobs/completed-jobs.html", - controller: 'CompletedJobsController' - } - } - }).state("single-job", { - url: "/jobs/{jobid}", - abstract: true, - views: { - main: { - templateUrl: "partials/jobs/job.html", - controller: 'SingleJobController' - } - } - }).state("single-job.plan", { - url: "", - redirectTo: "single-job.plan.subtasks", - views: { - details: { - templateUrl: "partials/jobs/job.plan.html", - controller: 'JobPlanController' - } - } - }).state("single-job.plan.subtasks", { - url: "", - views: { - 'node-details': { - templateUrl: "partials/jobs/job.plan.node-list.subtasks.html", - controller: 'JobPlanSubtasksController' - } - } - }).state("single-job.plan.metrics", { - url: "/metrics", - views: { - 'node-details': { - templateUrl: "partials/jobs/job.plan.node-list.metrics.html", - controller: 'JobPlanMetricsController' - } - } - }).state("single-job.plan.taskmanagers", { - url: "/taskmanagers", - views: { - 'node-details': { - templateUrl: "partials/jobs/job.plan.node-list.taskmanagers.html", - controller: 'JobPlanTaskManagersController' - } - } - }).state("single-job.plan.accumulators", { - url: "/accumulators", - views: { - 'node-details': { - templateUrl: "partials/jobs/job.plan.node-list.accumulators.html", - controller: 'JobPlanAccumulatorsController' - } - } - }).state("single-job.plan.checkpoints", { - url: "/checkpoints", - redirectTo: "single-job.plan.checkpoints.overview", - views: { - 'node-details': { - templateUrl: "partials/jobs/job.plan.node-list.checkpoints.html", - controller: 'JobPlanCheckpointsController' - } - } - }).state("single-job.plan.checkpoints.overview", { - url: "/overview", - views: { - 'checkpoints-view': { - templateUrl: "partials/jobs/job.plan.node.checkpoints.overview.html", - controller: 'JobPlanCheckpointsController' - } - } - }).state("single-job.plan.checkpoints.summary", { - url: "/summary", - views: { - 'checkpoints-view': { - templateUrl: "partials/jobs/job.plan.node.checkpoints.summary.html", - controller: 'JobPlanCheckpointsController' - } - } - }).state("single-job.plan.checkpoints.history", { - url: "/history", - views: { - 'checkpoints-view': { - templateUrl: "partials/jobs/job.plan.node.checkpoints.history.html", - controller: 'JobPlanCheckpointsController' - } - } - }).state("single-job.plan.checkpoints.config", { - url: "/config", - views: { - 'checkpoints-view': { - templateUrl: "partials/jobs/job.plan.node.checkpoints.config.html", - controller: 'JobPlanCheckpointsController' - } - } - }).state("single-job.plan.checkpoints.details", { - url: "/details/{checkpointId}", - views: { - 'checkpoints-view': { - templateUrl: "partials/jobs/job.plan.node.checkpoints.details.html", - controller: 'JobPlanCheckpointDetailsController' - } - } - }).state("single-job.plan.backpressure", { - url: "/backpressure", - views: { - 'node-details': { - templateUrl: "partials/jobs/job.plan.node-list.backpressure.html", - controller: 'JobPlanBackPressureController' - } - } - }).state("single-job.timeline", { - url: "/timeline", - views: { - details: { - templateUrl: "partials/jobs/job.timeline.html" - } - } - }).state("single-job.timeline.vertex", { - url: "/{vertexId}", - views: { - vertex: { - templateUrl: "partials/jobs/job.timeline.vertex.html", - controller: 'JobTimelineVertexController' - } - } - }).state("single-job.exceptions", { - url: "/exceptions", - views: { - details: { - templateUrl: "partials/jobs/job.exceptions.html", - controller: 'JobExceptionsController' - } - } - }).state("single-job.config", { - url: "/config", - views: { - details: { - templateUrl: "partials/jobs/job.config.html" - } - } - }).state("all-manager", { - url: "/taskmanagers", - views: { - main: { - templateUrl: "partials/taskmanager/index.html", - controller: 'AllTaskManagersController' - } - } - }).state("single-manager", { - url: "/taskmanager/{taskmanagerid}", - abstract: true, - views: { - main: { - templateUrl: "partials/taskmanager/taskmanager.html", - controller: 'SingleTaskManagerController' - } - } - }).state("single-manager.metrics", { - url: "/metrics", - views: { - details: { - templateUrl: "partials/taskmanager/taskmanager.metrics.html" - } - } - }).state("single-manager.stdout", { - url: "/stdout", - views: { - details: { - templateUrl: "partials/taskmanager/taskmanager.stdout.html", - controller: 'SingleTaskManagerStdoutController' - } - } - }).state("single-manager.log", { - url: "/log", - views: { - details: { - templateUrl: "partials/taskmanager/taskmanager.log.html", - controller: 'SingleTaskManagerLogsController' - } - } - }).state("jobmanager", { - url: "/jobmanager", - views: { - main: { - templateUrl: "partials/jobmanager/index.html" - } - } - }).state("jobmanager.config", { - url: "/config", - views: { - details: { - templateUrl: "partials/jobmanager/config.html", - controller: 'JobManagerConfigController' - } - } - }).state("jobmanager.stdout", { - url: "/stdout", - views: { - details: { - templateUrl: "partials/jobmanager/stdout.html", - controller: 'JobManagerStdoutController' - } - } - }).state("jobmanager.log", { - url: "/log", - views: { - details: { - templateUrl: "partials/jobmanager/log.html", - controller: 'JobManagerLogsController' - } - } - }).state("submit", { - url: "/submit", - views: { - main: { - templateUrl: "partials/submit.html", - controller: "JobSubmitController" - } - } - }); - return $urlRouterProvider.otherwise("/overview"); -}]); - -angular.module('flinkApp').directive('bsLabel', ["JobsService", function(JobsService) { - return { - transclude: true, - replace: true, - scope: { - getLabelClass: "&", - status: "@" - }, - template: "<span title='{{status}}' ng-class='getLabelClass()'><ng-transclude></ng-transclude></span>", - link: function(scope, element, attrs) { - return scope.getLabelClass = function() { - return 'label label-' + JobsService.translateLabelState(attrs.status); - }; - } - }; -}]).directive('bpLabel', ["JobsService", function(JobsService) { - return { - transclude: true, - replace: true, - scope: { - getBackPressureLabelClass: "&", - status: "@" - }, - template: "<span title='{{status}}' ng-class='getBackPressureLabelClass()'><ng-transclude></ng-transclude></span>", - link: function(scope, element, attrs) { - return scope.getBackPressureLabelClass = function() { - return 'label label-' + JobsService.translateBackPressureLabelState(attrs.status); - }; - } - }; -}]).directive('indicatorPrimary', ["JobsService", function(JobsService) { - return { - replace: true, - scope: { - getLabelClass: "&", - status: '@' - }, - template: "<i title='{{status}}' ng-class='getLabelClass()' />", - link: function(scope, element, attrs) { - return scope.getLabelClass = function() { - return 'fa fa-circle indicator indicator-' + JobsService.translateLabelState(attrs.status); - }; - } - }; -}]).directive('tableProperty', function() { - return { - replace: true, - scope: { - value: '=' - }, - template: "<td title=\"{{value || 'None'}}\">{{value || 'None'}}</td>" - }; -}); - -angular.module('flinkApp').filter("amDurationFormatExtended", ["angularMomentConfig", function(angularMomentConfig) { - var amDurationFormatExtendedFilter; - amDurationFormatExtendedFilter = function(value, format, durationFormat) { - if (typeof value === "undefined" || value === null) { - return ""; - } - return moment.duration(value, format).format(durationFormat, { - trim: false - }); - }; - amDurationFormatExtendedFilter.$stateful = angularMomentConfig.statefulFilters; - return amDurationFormatExtendedFilter; -}]).filter("humanizeDuration", function() { - return function(value, short) { - var days, hours, minutes, ms, seconds, x; - if (typeof value === "undefined" || value === null) { - return ""; - } - ms = value % 1000; - x = Math.floor(value / 1000); - seconds = x % 60; - x = Math.floor(x / 60); - minutes = x % 60; - x = Math.floor(x / 60); - hours = x % 24; - x = Math.floor(x / 24); - days = x; - if (days === 0) { - if (hours === 0) { - if (minutes === 0) { - if (seconds === 0) { - return ms + "ms"; - } else { - return seconds + "s "; - } - } else { - return minutes + "m " + seconds + "s"; - } - } else { - if (short) { - return hours + "h " + minutes + "m"; - } else { - return hours + "h " + minutes + "m " + seconds + "s"; - } - } - } else { - if (short) { - return days + "d " + hours + "h"; - } else { - return days + "d " + hours + "h " + minutes + "m " + seconds + "s"; - } - } - }; -}).filter("humanizeText", function() { - return function(text) { - if (text) { - return text.replace(/>/g, ">").replace(/<br\/>/g, ""); - } else { - return ''; - } - }; -}).filter("humanizeBytes", function() { - return function(bytes) { - var converter, units; - units = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]; - converter = function(value, power) { - var base; - base = Math.pow(1024, power); - if (value < base) { - return (value / base).toFixed(2) + " " + units[power]; - } else if (value < base * 1000) { - return (value / base).toPrecision(3) + " " + units[power]; - } else { - return converter(value, power + 1); - } - }; - if (typeof bytes === "undefined" || bytes === null) { - return ""; - } - if (bytes < 1000) { - return bytes + " B"; - } else { - return converter(bytes, 1); - } - }; -}).filter("toLocaleString", function() { - return function(text) { - return text.toLocaleString(); - }; -}).filter("toUpperCase", function() { - return function(text) { - return text.toUpperCase(); - }; -}).filter("percentage", function() { - return function(number) { - return (number * 100).toFixed(0) + '%'; - }; -}); - -angular.module('flinkApp').service('MainService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) { - this.loadConfig = function() { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "config").success(function(data, status, headers, config) { - return deferred.resolve(data); - }); - return deferred.promise; - }; - return this; -}]); - -angular.module('flinkApp').controller('JobManagerConfigController', ["$scope", "JobManagerConfigService", function($scope, JobManagerConfigService) { - return JobManagerConfigService.loadConfig().then(function(data) { - if ($scope.jobmanager == null) { - $scope.jobmanager = {}; - } - return $scope.jobmanager['config'] = data; - }); -}]).controller('JobManagerLogsController', ["$scope", "JobManagerLogsService", function($scope, JobManagerLogsService) { - JobManagerLogsService.loadLogs().then(function(data) { - if ($scope.jobmanager == null) { - $scope.jobmanager = {}; - } - return $scope.jobmanager['log'] = data; - }); - return $scope.reloadData = function() { - return JobManagerLogsService.loadLogs().then(function(data) { - return $scope.jobmanager['log'] = data; - }); - }; -}]).controller('JobManagerStdoutController', ["$scope", "JobManagerStdoutService", function($scope, JobManagerStdoutService) { - JobManagerStdoutService.loadStdout().then(function(data) { - if ($scope.jobmanager == null) { - $scope.jobmanager = {}; - } - return $scope.jobmanager['stdout'] = data; - }); - return $scope.reloadData = function() { - return JobManagerStdoutService.loadStdout().then(function(data) { - return $scope.jobmanager['stdout'] = data; - }); - }; -}]); - -angular.module('flinkApp').service('JobManagerConfigService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) { - var config; - config = {}; - this.loadConfig = function() { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "jobmanager/config").success(function(data, status, headers, config) { - config = data; - return deferred.resolve(data); - }); - return deferred.promise; - }; - return this; -}]).service('JobManagerLogsService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) { - var logs; - logs = {}; - this.loadLogs = function() { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "jobmanager/log").success(function(data, status, headers, config) { - logs = data; - return deferred.resolve(data); - }); - return deferred.promise; - }; - return this; -}]).service('JobManagerStdoutService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) { - var stdout; - stdout = {}; - this.loadStdout = function() { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "jobmanager/stdout").success(function(data, status, headers, config) { - stdout = data; - return deferred.resolve(data); - }); - return deferred.promise; - }; - return this; -}]); - -angular.module('flinkApp').controller('RunningJobsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) { - $scope.jobObserver = function() { - return $scope.jobs = JobsService.getJobs('running'); - }; - JobsService.registerObserver($scope.jobObserver); - $scope.$on('$destroy', function() { - return JobsService.unRegisterObserver($scope.jobObserver); - }); - return $scope.jobObserver(); -}]).controller('CompletedJobsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) { - $scope.jobObserver = function() { - return $scope.jobs = JobsService.getJobs('finished'); - }; - JobsService.registerObserver($scope.jobObserver); - $scope.$on('$destroy', function() { - return JobsService.unRegisterObserver($scope.jobObserver); - }); - return $scope.jobObserver(); -}]).controller('SingleJobController', ["$scope", "$state", "$stateParams", "JobsService", "MetricsService", "$rootScope", "flinkConfig", "$interval", function($scope, $state, $stateParams, JobsService, MetricsService, $rootScope, flinkConfig, $interval) { - var refresher; - $scope.jobid = $stateParams.jobid; - $scope.job = null; - $scope.plan = null; - $scope.vertices = null; - $scope.backPressureOperatorStats = {}; - JobsService.loadJob($stateParams.jobid).then(function(data) { - $scope.job = data; - $scope.plan = data.plan; - $scope.vertices = data.vertices; - return MetricsService.setupMetrics($stateParams.jobid, data.vertices); - }); - refresher = $interval(function() { - return JobsService.loadJob($stateParams.jobid).then(function(data) { - $scope.job = data; - return $scope.$broadcast('reload'); - }); - }, flinkConfig["refresh-interval"]); - $scope.$on('$destroy', function() { - $scope.job = null; - $scope.plan = null; - $scope.vertices = null; - $scope.backPressureOperatorStats = null; - return $interval.cancel(refresher); - }); - $scope.cancelJob = function(cancelEvent) { - angular.element(cancelEvent.currentTarget).removeClass("btn").removeClass("btn-default").html('Cancelling...'); - return JobsService.cancelJob($stateParams.jobid).then(function(data) { - return {}; - }); - }; - return $scope.stopJob = function(stopEvent) { - angular.element(stopEvent.currentTarget).removeClass("btn").removeClass("btn-default").html('Stopping...'); - return JobsService.stopJob($stateParams.jobid).then(function(data) { - return {}; - }); - }; -}]).controller('JobPlanController', ["$scope", "$state", "$stateParams", "$window", "JobsService", function($scope, $state, $stateParams, $window, JobsService) { - $scope.nodeid = null; - $scope.nodeUnfolded = false; - $scope.stateList = JobsService.stateList(); - $scope.changeNode = function(nodeid) { - if (nodeid !== $scope.nodeid) { - $scope.nodeid = nodeid; - $scope.vertex = null; - $scope.subtasks = null; - $scope.accumulators = null; - $scope.operatorCheckpointStats = null; - $scope.$broadcast('reload'); - return $scope.$broadcast('node:change', $scope.nodeid); - } else { - $scope.nodeid = null; - $scope.nodeUnfolded = false; - $scope.vertex = null; - $scope.subtasks = null; - $scope.accumulators = null; - return $scope.operatorCheckpointStats = null; - } - }; - $scope.deactivateNode = function() { - $scope.nodeid = null; - $scope.nodeUnfolded = false; - $scope.vertex = null; - $scope.subtasks = null; - $scope.accumulators = null; - return $scope.operatorCheckpointStats = null; - }; - return $scope.toggleFold = function() { - return $scope.nodeUnfolded = !$scope.nodeUnfolded; - }; -}]).controller('JobPlanSubtasksController', ["$scope", "JobsService", function($scope, JobsService) { - var getSubtasks; - getSubtasks = function() { - return JobsService.getSubtasks($scope.nodeid).then(function(data) { - return $scope.subtasks = data; - }); - }; - if ($scope.nodeid && (!$scope.vertex || !$scope.vertex.st)) { - getSubtasks(); - } - return $scope.$on('reload', function(event) { - if ($scope.nodeid) { - return getSubtasks(); - } - }); -}]).controller('JobPlanTaskManagersController', ["$scope", "JobsService", function($scope, JobsService) { - var getTaskManagers; - getTaskManagers = function() { - return JobsService.getTaskManagers($scope.nodeid).then(function(data) { - return $scope.taskmanagers = data; - }); - }; - if ($scope.nodeid && (!$scope.vertex || !$scope.vertex.st)) { - getTaskManagers(); - } - return $scope.$on('reload', function(event) { - if ($scope.nodeid) { - return getTaskManagers(); - } - }); -}]).controller('JobPlanAccumulatorsController', ["$scope", "JobsService", function($scope, JobsService) { - var getAccumulators; - getAccumulators = function() { - return JobsService.getAccumulators($scope.nodeid).then(function(data) { - $scope.accumulators = data.main; - return $scope.subtaskAccumulators = data.subtasks; - }); - }; - if ($scope.nodeid && (!$scope.vertex || !$scope.vertex.accumulators)) { - getAccumulators(); - } - return $scope.$on('reload', function(event) { - if ($scope.nodeid) { - return getAccumulators(); - } - }); -}]).controller('JobPlanCheckpointsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) { - var getGeneralCheckpointStats; - $scope.checkpointDetails = {}; - $scope.checkpointDetails.id = -1; - JobsService.getCheckpointConfig().then(function(data) { - return $scope.checkpointConfig = data; - }); - getGeneralCheckpointStats = function() { - return JobsService.getCheckpointStats().then(function(data) { - if (data !== null) { - return $scope.checkpointStats = data; - } - }); - }; - getGeneralCheckpointStats(); - return $scope.$on('reload', function(event) { - return getGeneralCheckpointStats(); - }); -}]).controller('JobPlanCheckpointDetailsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) { - var getCheckpointDetails, getCheckpointSubtaskDetails; - $scope.subtaskDetails = {}; - $scope.checkpointDetails.id = $stateParams.checkpointId; - getCheckpointDetails = function(checkpointId) { - return JobsService.getCheckpointDetails(checkpointId).then(function(data) { - if (data !== null) { - return $scope.checkpoint = data; - } else { - return $scope.unknown_checkpoint = true; - } - }); - }; - getCheckpointSubtaskDetails = function(checkpointId, vertexId) { - return JobsService.getCheckpointSubtaskDetails(checkpointId, vertexId).then(function(data) { - if (data !== null) { - return $scope.subtaskDetails[vertexId] = data; - } - }); - }; - getCheckpointDetails($stateParams.checkpointId); - if ($scope.nodeid) { - getCheckpointSubtaskDetails($stateParams.checkpointId, $scope.nodeid); - } - $scope.$on('reload', function(event) { - getCheckpointDetails($stateParams.checkpointId); - if ($scope.nodeid) { - return getCheckpointSubtaskDetails($stateParams.checkpointId, $scope.nodeid); - } - }); - return $scope.$on('$destroy', function() { - return $scope.checkpointDetails.id = -1; - }); -}]).controller('JobPlanBackPressureController', ["$scope", "JobsService", function($scope, JobsService) { - var getOperatorBackPressure; - getOperatorBackPressure = function() { - $scope.now = Date.now(); - if ($scope.nodeid) { - return JobsService.getOperatorBackPressure($scope.nodeid).then(function(data) { - return $scope.backPressureOperatorStats[$scope.nodeid] = data; - }); - } - }; - getOperatorBackPressure(); - return $scope.$on('reload', function(event) { - return getOperatorBackPressure(); - }); -}]).controller('JobTimelineVertexController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) { - var getVertex; - getVertex = function() { - return JobsService.getVertex($stateParams.vertexId).then(function(data) { - return $scope.vertex = data; - }); - }; - getVertex(); - return $scope.$on('reload', function(event) { - return getVertex(); - }); -}]).controller('JobExceptionsController', ["$scope", "$state", "$stateParams", "JobsService", function($scope, $state, $stateParams, JobsService) { - return JobsService.loadExceptions().then(function(data) { - return $scope.exceptions = data; - }); -}]).controller('JobPropertiesController', ["$scope", "JobsService", function($scope, JobsService) { - return $scope.changeNode = function(nodeid) { - if (nodeid !== $scope.nodeid) { - $scope.nodeid = nodeid; - return JobsService.getNode(nodeid).then(function(data) { - return $scope.node = data; - }); - } else { - $scope.nodeid = null; - return $scope.node = null; - } - }; -}]).controller('JobPlanMetricsController', ["$scope", "JobsService", "MetricsService", function($scope, JobsService, MetricsService) { - var loadMetrics; - $scope.dragging = false; - $scope.window = MetricsService.getWindow(); - $scope.availableMetrics = null; - $scope.$on('$destroy', function() { - return MetricsService.unRegisterObserver(); - }); - loadMetrics = function() { - JobsService.getVertex($scope.nodeid).then(function(data) { - return $scope.vertex = data; - }); - return MetricsService.getAvailableMetrics($scope.jobid, $scope.nodeid).then(function(data) { - $scope.availableMetrics = data; - $scope.metrics = MetricsService.getMetricsSetup($scope.jobid, $scope.nodeid).names; - return MetricsService.registerObserver($scope.jobid, $scope.nodeid, function(data) { - return $scope.$broadcast("metrics:data:update", data.timestamp, data.values); - }); - }); - }; - $scope.dropped = function(event, index, item, external, type) { - MetricsService.orderMetrics($scope.jobid, $scope.nodeid, item, index); - $scope.$broadcast("metrics:refresh", item); - loadMetrics(); - return false; - }; - $scope.dragStart = function() { - return $scope.dragging = true; - }; - $scope.dragEnd = function() { - return $scope.dragging = false; - }; - $scope.addMetric = function(metric) { - MetricsService.addMetric($scope.jobid, $scope.nodeid, metric.id); - return loadMetrics(); - }; - $scope.removeMetric = function(metric) { - MetricsService.removeMetric($scope.jobid, $scope.nodeid, metric); - return loadMetrics(); - }; - $scope.setMetricSize = function(metric, size) { - MetricsService.setMetricSize($scope.jobid, $scope.nodeid, metric, size); - return loadMetrics(); - }; - $scope.getValues = function(metric) { - return MetricsService.getValues($scope.jobid, $scope.nodeid, metric); - }; - $scope.$on('node:change', function(event, nodeid) { - if (!$scope.dragging) { - return loadMetrics(); - } - }); - if ($scope.nodeid) { - return loadMetrics(); - } -}]); - -angular.module('flinkApp').directive('vertex', ["$state", function($state) { - return { - template: "<svg class='timeline secondary' width='0' height='0'></svg>", - scope: { - data: "=" - }, - link: function(scope, elem, attrs) { - var analyzeTime, containerW, svgEl; - svgEl = elem.children()[0]; - containerW = elem.width(); - angular.element(svgEl).attr('width', containerW); - analyzeTime = function(data) { - var chart, svg, testData; - d3.select(svgEl).selectAll("*").remove(); - testData = []; - angular.forEach(data.subtasks, function(subtask, i) { - var times; - times = [ - { - label: "Scheduled", - color: "#666", - borderColor: "#555", - starting_time: subtask.timestamps["SCHEDULED"], - ending_time: subtask.timestamps["DEPLOYING"], - type: 'regular' - }, { - label: "Deploying", - color: "#aaa", - borderColor: "#555", - starting_time: subtask.timestamps["DEPLOYING"], - ending_time: subtask.timestamps["RUNNING"], - type: 'regular' - } - ]; - if (subtask.timestamps["FINISHED"] > 0) { - times.push({ - label: "Running", - color: "#ddd", - borderColor: "#555", - starting_time: subtask.timestamps["RUNNING"], - ending_time: subtask.timestamps["FINISHED"], - type: 'regular' - }); - } - return testData.push({ - label: "(" + subtask.subtask + ") " + subtask.host, - times: times - }); - }); - chart = d3.timeline().stack().tickFormat({ - format: d3.time.format("%L"), - tickSize: 1 - }).prefix("single").labelFormat(function(label) { - return label; - }).margin({ - left: 100, - right: 0, - top: 0, - bottom: 0 - }).itemHeight(30).relativeTime(); - return svg = d3.select(svgEl).datum(testData).call(chart); - }; - analyzeTime(scope.data); - } - }; -}]).directive('timeline', ["$state", function($state) { - return { - template: "<svg class='timeline' width='0' height='0'></svg>", - scope: { - vertices: "=", - jobid: "=" - }, - link: function(scope, elem, attrs) { - var analyzeTime, containerW, svgEl, translateLabel; - svgEl = elem.children()[0]; - containerW = elem.width(); - angular.element(svgEl).attr('width', containerW); - translateLabel = function(label) { - return label.replace(">", ">"); - }; - analyzeTime = function(data) { - var chart, svg, testData; - d3.select(svgEl).selectAll("*").remove(); - testData = []; - angular.forEach(data, function(vertex) { - if (vertex['start-time'] > -1) { - if (vertex.type === 'scheduled') { - return testData.push({ - times: [ - { - label: translateLabel(vertex.name), - color: "#cccccc", - borderColor: "#555555", - starting_time: vertex['start-time'], - ending_time: vertex['end-time'], - type: vertex.type - } - ] - }); - } else { - return testData.push({ - times: [ - { - label: translateLabel(vertex.name), - color: "#d9f1f7", - borderColor: "#62cdea", - starting_time: vertex['start-time'], - ending_time: vertex['end-time'], - link: vertex.id, - type: vertex.type - } - ] - }); - } - } - }); - chart = d3.timeline().stack().click(function(d, i, datum) { - if (d.link) { - return $state.go("single-job.timeline.vertex", { - jobid: scope.jobid, - vertexId: d.link - }); - } - }).tickFormat({ - format: d3.time.format("%L"), - tickSize: 1 - }).prefix("main").margin({ - left: 0, - right: 0, - top: 0, - bottom: 0 - }).itemHeight(30).showBorderLine().showHourTimeline(); - return svg = d3.select(svgEl).datum(testData).call(chart); - }; - scope.$watch(attrs.vertices, function(data) { - if (data) { - return analyzeTime(data); - } - }); - } - }; -}]).directive('split', function() { - return { - compile: function(tElem, tAttrs) { - return Split(tElem.children(), { - sizes: [50, 50], - direction: 'vertical' - }); - } - }; -}).directive('jobPlan', ["$timeout", function($timeout) { - return { - template: "<svg class='graph' width='500' height='400'><g /></svg> <svg class='tmp' width='1' height='1'><g /></svg> <div class='btn-group zoom-buttons'> <a class='btn btn-default zoom-in' ng-click='zoomIn()'><i class='fa fa-plus' /></a> <a class='btn btn-default zoom-out' ng-click='zoomOut()'><i class='fa fa-minus' /></a> </div>", - scope: { - plan: '=', - setNode: '&' - }, - link: function(scope, elem, attrs) { - var containerW, createEdge, createLabelEdge, createLabelNode, createNode, d3mainSvg, d3mainSvgG, d3tmpSvg, drawGraph, extendLabelNodeForIteration, g, getNodeType, isSpecialIterationNode, jobid, loadJsonToDagre, mainG, mainSvgElement, mainTmpElement, mainZoom, searchForNode, shortenString, subgraphs; - g = null; - mainZoom = d3.behavior.zoom(); - subgraphs = []; - jobid = attrs.jobid; - mainSvgElement = elem.children()[0]; - mainG = elem.children().children()[0]; - mainTmpElement = elem.children()[1]; - d3mainSvg = d3.select(mainSvgElement); - d3mainSvgG = d3.select(mainG); - d3tmpSvg = d3.select(mainTmpElement); - containerW = elem.width(); - angular.element(elem.children()[0]).width(containerW); - scope.zoomIn = function() { - var translate, v1, v2; - if (mainZoom.scale() < 2.99) { - translate = mainZoom.translate(); - v1 = translate[0] * (mainZoom.scale() + 0.1 / (mainZoom.scale())); - v2 = translate[1] * (mainZoom.scale() + 0.1 / (mainZoom.scale())); - mainZoom.scale(mainZoom.scale() + 0.1); - mainZoom.translate([v1, v2]); - return d3mainSvgG.attr("transform", "translate(" + v1 + "," + v2 + ") scale(" + mainZoom.scale() + ")"); - } - }; - scope.zoomOut = function() { - var translate, v1, v2; - if (mainZoom.scale() > 0.31) { - mainZoom.scale(mainZoom.scale() - 0.1); - translate = mainZoom.translate(); - v1 = translate[0] * (mainZoom.scale() - 0.1 / (mainZoom.scale())); - v2 = translate[1] * (mainZoom.scale() - 0.1 / (mainZoom.scale())); - mainZoom.translate([v1, v2]); - return d3mainSvgG.attr("transform", "translate(" + v1 + "," + v2 + ") scale(" + mainZoom.scale() + ")"); - } - }; - createLabelEdge = function(el) { - var labelValue; - labelValue = ""; - if ((el.ship_strategy != null) || (el.local_strategy != null)) { - labelValue += "<div class='edge-label'>"; - if (el.ship_strategy != null) { - labelValue += el.ship_strategy; - } - if (el.temp_mode !== undefined) { - labelValue += " (" + el.temp_mode + ")"; - } - if (el.local_strategy !== undefined) { - labelValue += ",<br>" + el.local_strategy; - } - labelValue += "</div>"; - } - return labelValue; - }; - isSpecialIterationNode = function(info) { - return info === "partialSolution" || info === "nextPartialSolution" || info === "workset" || info === "nextWorkset" || info === "solutionSet" || info === "solutionDelta"; - }; - getNodeType = function(el, info) { - if (info === "mirror") { - return 'node-mirror'; - } else if (isSpecialIterationNode(info)) { - return 'node-iteration'; - } else { - return 'node-normal'; - } - }; - createLabelNode = function(el, info, maxW, maxH) { - var labelValue, stepName; - labelValue = "<div href='#/jobs/" + jobid + "/vertex/" + el.id + "' class='node-label " + getNodeType(el, info) + "'>"; - if (info === "mirror") { - labelValue += "<h3 class='node-name'>Mirror of " + el.operator + "</h3>"; - } else { - labelValue += "<h3 class='node-name'>" + el.operator + "</h3>"; - } - if (el.description === "") { - labelValue += ""; - } else { - stepName = el.description; - stepName = shortenString(stepName); - labelValue += "<h4 class='step-name'>" + stepName + "</h4>"; - } - if (el.step_function != null) { - labelValue += extendLabelNodeForIteration(el.id, maxW, maxH); - } else { - if (isSpecialIterationNode(info)) { - labelValue += "<h5>" + info + " Node</h5>"; - } - if (el.parallelism !== "") { - labelValue += "<h5>Parallelism: " + el.parallelism + "</h5>"; - } - if (!(el.operator === undefined || !el.operator_strategy)) { - labelValue += "<h5>Operation: " + shortenString(el.operator_strategy) + "</h5>"; - } - } - labelValue += "</div>"; - return labelValue; - }; - extendLabelNodeForIteration = function(id, maxW, maxH) { - var labelValue, svgID; - svgID = "svg-" + id; - labelValue = "<svg class='" + svgID + "' width=" + maxW + " height=" + maxH + "><g /></svg>"; - return labelValue; - }; - shortenString = function(s) { - var sbr; - if (s.charAt(0) === "<") { - s = s.replace("<", "<"); - s = s.replace(">", ">"); - } - sbr = ""; - while (s.length > 30) { - sbr = sbr + s.substring(0, 30) + "<br>"; - s = s.substring(30, s.length); - } - sbr = sbr + s; - return sbr; - }; - createNode = function(g, data, el, isParent, maxW, maxH) { - if (isParent == null) { - isParent = false; - } - if (el.id === data.partial_solution) { - return g.setNode(el.id, { - label: createLabelNode(el, "partialSolution", maxW, maxH), - labelType: 'html', - "class": getNodeType(el, "partialSolution") - }); - } else if (el.id === data.next_partial_solution) { - return g.setNode(el.id, { - label: createLabelNode(el, "nextPartialSolution", maxW, maxH), - labelType: 'html', - "class": getNodeType(el, "nextPartialSolution") - }); - } else if (el.id === data.workset) { - return g.setNode(el.id, { - label: createLabelNode(el, "workset", maxW, maxH), - labelType: 'html', - "class": getNodeType(el, "workset") - }); - } else if (el.id === data.next_workset) { - return g.setNode(el.id, { - label: createLabelNode(el, "nextWorkset", maxW, maxH), - labelType: 'html', - "class": getNodeType(el, "nextWorkset") - }); - } else if (el.id === data.solution_set) { - return g.setNode(el.id, { - label: createLabelNode(el, "solutionSet", maxW, maxH), - labelType: 'html', - "class": getNodeType(el, "solutionSet") - }); - } else if (el.id === data.solution_delta) { - return g.setNode(el.id, { - label: createLabelNode(el, "solutionDelta", maxW, maxH), - labelType: 'html', - "class": getNodeType(el, "solutionDelta") - }); - } else { - return g.setNode(el.id, { - label: createLabelNode(el, "", maxW, maxH), - labelType: 'html', - "class": getNodeType(el, "") - }); - } - }; - createEdge = function(g, data, el, existingNodes, pred) { - return g.setEdge(pred.id, el.id, { - label: createLabelEdge(pred), - labelType: 'html', - arrowhead: 'normal' - }); - }; - loadJsonToDagre = function(g, data) { - var el, existingNodes, isParent, k, l, len, len1, maxH, maxW, pred, r, ref, sg, toIterate; - existingNodes = []; - if (data.nodes != null) { - toIterate = data.nodes; - } else { - toIterate = data.step_function; - isParent = true; - } - for (k = 0, len = toIterate.length; k < len; k++) { - el = toIterate[k]; - maxW = 0; - maxH = 0; - if (el.step_function) { - sg = new dagreD3.graphlib.Graph({ - multigraph: true, - compound: true - }).setGraph({ - nodesep: 20, - edgesep: 0, - ranksep: 20, - rankdir: "LR", - marginx: 10, - marginy: 10 - }); - subgraphs[el.id] = sg; - loadJsonToDagre(sg, el); - r = new dagreD3.render(); - d3tmpSvg.select('g').call(r, sg); - maxW = sg.graph().width; - maxH = sg.graph().height; - angular.element(mainTmpElement).empty(); - } - createNode(g, data, el, isParent, maxW, maxH); - existingNodes.push(el.id); - if (el.inputs != null) { - ref = el.inputs; - for (l = 0, len1 = ref.length; l < len1; l++) { - pred = ref[l]; - createEdge(g, data, el, existingNodes, pred); - } - } - } - return g; - }; - searchForNode = function(data, nodeID) { - var el, i, j; - for (i in data.nodes) { - el = data.nodes[i]; - if (el.id === nodeID) { - return el; - } - if (el.step_function != null) { - for (j in el.step_function) { - if (el.step_function[j].id === nodeID) { - return el.step_function[j]; - } - } - } - } - }; - drawGraph = function(data) { - var i, newScale, renderer, sg, xCenterOffset, yCenterOffset; - g = new dagreD3.graphlib.Graph({ - multigraph: true, - compound: true - }).setGraph({ - nodesep: 70, - edgesep: 0, - ranksep: 50, - rankdir: "LR", - marginx: 40, - marginy: 40 - }); - loadJsonToDagre(g, data); - renderer = new dagreD3.render(); - d3mainSvgG.call(renderer, g); - for (i in subgraphs) { - sg = subgraphs[i]; - d3mainSvg.select('svg.svg-' + i + ' g').call(renderer, sg); - } - newScale = 0.5; - xCenterOffset = Math.floor((angular.element(mainSvgElement).width() - g.graph().width * newScale) / 2); - yCenterOffset = Math.floor((angular.element(mainSvgElement).height() - g.graph().height * newScale) / 2); - mainZoom.scale(newScale).translate([xCenterOffset, yCenterOffset]); - d3mainSvgG.attr("transform", "translate(" + xCenterOffset + ", " + yCenterOffset + ") scale(" + mainZoom.scale() + ")"); - mainZoom.on("zoom", function() { - var ev; - ev = d3.event; - return d3mainSvgG.attr("transform", "translate(" + ev.translate + ") scale(" + ev.scale + ")"); - }); - mainZoom(d3mainSvg); - return d3mainSvgG.selectAll('.node').on('click', function(d) { - return scope.setNode({ - nodeid: d - }); - }); - }; - scope.$watch(attrs.plan, function(newPlan) { - if (newPlan) { - return drawGraph(newPlan); - } - }); - } - }; -}]); - -angular.module('flinkApp').service('JobsService', ["$http", "flinkConfig", "$log", "amMoment", "$q", "$timeout", function($http, flinkConfig, $log, amMoment, $q, $timeout) { - var currentJob, currentPlan, deferreds, jobObservers, jobs, notifyObservers; - currentJob = null; - currentPlan = null; - deferreds = {}; - jobs = { - running: [], - finished: [], - cancelled: [], - failed: [] - }; - jobObservers = []; - notifyObservers = function() { - return angular.forEach(jobObservers, function(callback) { - return callback(); - }); - }; - this.registerObserver = function(callback) { - return jobObservers.push(callback); - }; - this.unRegisterObserver = function(callback) { - var index; - index = jobObservers.indexOf(callback); - return jobObservers.splice(index, 1); - }; - this.stateList = function() { - return ['SCHEDULED', 'DEPLOYING', 'RUNNING', 'FINISHED', 'FAILED', 'CANCELING', 'CANCELED']; - }; - this.translateLabelState = function(state) { - switch (state.toLowerCase()) { - case 'finished': - return 'success'; - case 'failed': - return 'danger'; - case 'scheduled': - return 'default'; - case 'deploying': - return 'info'; - case 'running': - return 'primary'; - case 'canceling': - return 'warning'; - case 'pending': - return 'info'; - case 'total': - return 'black'; - default: - return 'default'; - } - }; - this.setEndTimes = function(list) { - return angular.forEach(list, function(item, jobKey) { - if (!(item['end-time'] > -1)) { - return item['end-time'] = item['start-time'] + item['duration']; - } - }); - }; - this.processVertices = function(data) { - angular.forEach(data.vertices, function(vertex, i) { - return vertex.type = 'regular'; - }); - return data.vertices.unshift({ - name: 'Scheduled', - 'start-time': data.timestamps['CREATED'], - 'end-time': data.timestamps['CREATED'] + 1, - type: 'scheduled' - }); - }; - this.listJobs = function() { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "joboverview").success((function(_this) { - return function(data, status, headers, config) { - angular.forEach(data, function(list, listKey) { - switch (listKey) { - case 'running': - return jobs.running = _this.setEndTimes(list); - case 'finished': - return jobs.finished = _this.setEndTimes(list); - case 'cancelled': - return jobs.cancelled = _this.setEndTimes(list); - case 'failed': - return jobs.failed = _this.setEndTimes(list); - } - }); - deferred.resolve(jobs); - return notifyObservers(); - }; - })(this)); - return deferred.promise; - }; - this.getJobs = function(type) { - return jobs[type]; - }; - this.getAllJobs = function() { - return jobs; - }; - this.loadJob = function(jobid) { - currentJob = null; - deferreds.job = $q.defer(); - $http.get(flinkConfig.jobServer + "jobs/" + jobid).success((function(_this) { - return function(data, status, headers, config) { - _this.setEndTimes(data.vertices); - _this.processVertices(data); - return $http.get(flinkConfig.jobServer + "jobs/" + jobid + "/config").success(function(jobConfig) { - data = angular.extend(data, jobConfig); - currentJob = data; - return deferreds.job.resolve(currentJob); - }); - }; - })(this)); - return deferreds.job.promise; - }; - this.getNode = function(nodeid) { - var deferred, seekNode; - seekNode = function(nodeid, data) { - var j, len, node, sub; - for (j = 0, len = data.length; j < len; j++) { - node = data[j]; - if (node.id === nodeid) { - return node; - } - if (node.step_function) { - sub = seekNode(nodeid, node.step_function); - } - if (sub) { - return sub; - } - } - return null; - }; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - var foundNode; - foundNode = seekNode(nodeid, currentJob.plan.nodes); - foundNode.vertex = _this.seekVertex(nodeid); - return deferred.resolve(foundNode); - }; - })(this)); - return deferred.promise; - }; - this.seekVertex = function(nodeid) { - var j, len, ref, vertex; - ref = currentJob.vertices; - for (j = 0, len = ref.length; j < len; j++) { - vertex = ref[j]; - if (vertex.id === nodeid) { - return vertex; - } - } - return null; - }; - this.getVertex = function(vertexid) { - var deferred; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - var vertex; - vertex = _this.seekVertex(vertexid); - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasktimes").success(function(data) { - vertex.subtasks = data.subtasks; - return deferred.resolve(vertex); - }); - }; - })(this)); - return deferred.promise; - }; - this.getSubtasks = function(vertexid) { - var deferred; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/vertices/" + vertexid).success(function(data) { - var subtasks; - subtasks = data.subtasks; - return deferred.resolve(subtasks); - }); - }; - })(this)); - return deferred.promise; - }; - this.getTaskManagers = function(vertexid) { - var deferred; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/vertices/" + vertexid + "/taskmanagers").success(function(data) { - var taskmanagers; - taskmanagers = data.taskmanagers; - return deferred.resolve(taskmanagers); - }); - }; - })(this)); - return deferred.promise; - }; - this.getAccumulators = function(vertexid) { - var deferred; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - console.log(currentJob.jid); - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/vertices/" + vertexid + "/accumulators").success(function(data) { - var accumulators; - accumulators = data['user-accumulators']; - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/vertices/" + vertexid + "/subtasks/accumulators").success(function(data) { - var subtaskAccumulators; - subtaskAccumulators = data.subtasks; - return deferred.resolve({ - main: accumulators, - subtasks: subtaskAccumulators - }); - }); - }); - }; - })(this)); - return deferred.promise; - }; - this.getCheckpointConfig = function() { - var deferred; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/checkpoints/config").success(function(data) { - if (angular.equals({}, data)) { - return deferred.resolve(null); - } else { - return deferred.resolve(data); - } - }); - }; - })(this)); - return deferred.promise; - }; - this.getCheckpointStats = function() { - var deferred; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/checkpoints").success(function(data, status, headers, config) { - if (angular.equals({}, data)) { - return deferred.resolve(null); - } else { - return deferred.resolve(data); - } - }); - }; - })(this)); - return deferred.promise; - }; - this.getCheckpointDetails = function(checkpointid) { - var deferred; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/checkpoints/details/" + checkpointid).success(function(data) { - if (angular.equals({}, data)) { - return deferred.resolve(null); - } else { - return deferred.resolve(data); - } - }); - }; - })(this)); - return deferred.promise; - }; - this.getCheckpointSubtaskDetails = function(checkpointid, vertexid) { - var deferred; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/checkpoints/details/" + checkpointid + "/subtasks/" + vertexid).success(function(data) { - if (angular.equals({}, data)) { - return deferred.resolve(null); - } else { - return deferred.resolve(data); - } - }); - }; - })(this)); - return deferred.promise; - }; - this.getOperatorBackPressure = function(vertexid) { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/vertices/" + vertexid + "/backpressure").success((function(_this) { - return function(data) { - return deferred.resolve(data); - }; - })(this)); - return deferred.promise; - }; - this.translateBackPressureLabelState = function(state) { - switch (state.toLowerCase()) { - case 'in-progress': - return 'danger'; - case 'ok': - return 'success'; - case 'low': - return 'warning'; - case 'high': - return 'danger'; - default: - return 'default'; - } - }; - this.loadExceptions = function() { - var deferred; - deferred = $q.defer(); - deferreds.job.promise.then((function(_this) { - return function(data) { - return $http.get(flinkConfig.jobServer + "jobs/" + currentJob.jid + "/exceptions").success(function(exceptions) { - currentJob.exceptions = exceptions; - return deferred.resolve(exceptions); - }); - }; - })(this)); - return deferred.promise; - }; - this.cancelJob = function(jobid) { - return $http.get(flinkConfig.jobServer + "jobs/" + jobid + "/yarn-cancel"); - }; - this.stopJob = function(jobid) { - return $http.get("jobs/" + jobid + "/yarn-stop"); - }; - return this; -}]); - -angular.module('flinkApp').directive('metricsGraph', function() { - return { - template: '<div class="panel panel-default panel-metric"> <div class="panel-heading"> <span class="metric-title">{{metric.id}}</span> <div class="buttons"> <div class="btn-group"> <button type="button" ng-class="[btnClasses, {active: metric.size != \'big\'}]" ng-click="setSize(\'small\')">Small</button> <button type="button" ng-class="[btnClasses, {active: metric.size == \'big\'}]" ng-click="setSize(\'big\')">Big</button> </div> <a title="Remove" class="btn btn-default btn-xs remove" ng-click="removeMetric()"><i class="fa fa-close" /></a> </div> </div> <div class="panel-body"> <svg /> </div> </div>', - replace: true, - scope: { - metric: "=", - window: "=", - removeMetric: "&", - setMetricSize: "=", - getValues: "&" - }, - link: function(scope, element, attrs) { - scope.btnClasses = ['btn', 'btn-default', 'btn-xs']; - scope.value = null; - scope.data = [ - { - values: scope.getValues() - } - ]; - scope.options = { - x: function(d, i) { - return d.x; - }, - y: function(d, i) { - return d.y; - }, - xTickFormat: function(d) { - return d3.time.format('%H:%M:%S')(new Date(d)); - }, - yTickFormat: function(d) { - var absD, found, pow, step; - found = false; - pow = 0; - step = 1; - absD = Math.abs(d); - while (!found && pow < 50) { - if (Math.pow(10, pow) <= absD && absD < Math.pow(10, pow + step)) { - found = true; - } else { - pow += step; - } - } - if (found && pow > 6) { - return (d / Math.pow(10, pow)) + "E" + pow; - } else { - return "" + d; - } - } - }; - scope.showChart = function() { - return d3.select(element.find("svg")[0]).datum(scope.data).transition().duration(250).call(scope.chart); - }; - scope.chart = nv.models.lineChart().options(scope.options).showLegend(false).margin({ - top: 15, - left: 60, - bottom: 30, - right: 30 - }); - scope.chart.yAxis.showMaxMin(false); - scope.chart.tooltip.hideDelay(0); - scope.chart.tooltip.contentGenerator(function(obj) { - return "<p>" + (d3.time.format('%H:%M:%S')(new Date(obj.point.x))) + " | " + obj.point.y + "</p>"; - }); - nv.utils.windowResize(scope.chart.update); - scope.setSize = function(size) { - return scope.setMetricSize(scope.metric, size); - }; - scope.showChart(); - scope.$on('metrics:data:update', function(event, timestamp, data) { - scope.value = parseFloat(data[scope.metric.id]); - scope.data[0].values.push({ - x: timestamp, - y: scope.value - }); - if (scope.data[0].values.length > scope.window) { - scope.data[0].values.shift(); - } - scope.showChart(); - scope.chart.clearHighlights(); - return scope.chart.tooltip.hidden(true); - }); - return element.find(".metric-title").qtip({ - content: { - text: scope.metric.id - }, - position: { - my: 'bottom left', - at: 'top left' - }, - style: { - classes: 'qtip-light qtip-timeline-bar' - } - }); - } - }; -}); - -angular.module('flinkApp').service('MetricsService', ["$http", "$q", "flinkConfig", "$interval", function($http, $q, flinkConfig, $interval) { - this.metrics = {}; - this.values = {}; - this.watched = {}; - this.observer = { - jobid: null, - nodeid: null, - callback: null - }; - this.refresh = $interval((function(_this) { - return function() { - return angular.forEach(_this.metrics, function(vertices, jobid) { - return angular.forEach(vertices, function(metrics, nodeid) { - var names; - names = []; - angular.forEach(metrics, function(metric, index) { - return names.push(metric.id); - }); - if (names.length > 0) { - return _this.getMetrics(jobid, nodeid, names).then(function(values) { - if (jobid === _this.observer.jobid && nodeid === _this.observer.nodeid) { - if (_this.observer.callback) { - return _this.observer.callback(values); - } - } - }); - } - }); - }); - }; - })(this), flinkConfig["refresh-interval"]); - this.registerObserver = function(jobid, nodeid, callback) { - this.observer.jobid = jobid; - this.observer.nodeid = nodeid; - return this.observer.callback = callback; - }; - this.unRegisterObserver = function() { - return this.observer = { - jobid: null, - nodeid: null, - callback: null - }; - }; - this.setupMetrics = function(jobid, vertices) { - this.setupLS(); - this.watched[jobid] = []; - return angular.forEach(vertices, (function(_this) { - return function(v, k) { - if (v.id) { - return _this.watched[jobid].push(v.id); - } - }; - })(this)); - }; - this.getWindow = function() { - return 100; - }; - this.setupLS = function() { - if (localStorage.flinkMetrics == null) { - this.saveSetup(); - } - return this.metrics = JSON.parse(localStorage.flinkMetrics); - }; - this.saveSetup = function() { - return localStorage.flinkMetrics = JSON.stringify(this.metrics); - }; - this.saveValue = function(jobid, nodeid, value) { - if (this.values[jobid] == null) { - this.values[jobid] = {}; - } - if (this.values[jobid][nodeid] == null) { - this.values[jobid][nodeid] = []; - } - this.values[jobid][nodeid].push(value); - if (this.values[jobid][nodeid].length > this.getWindow()) { - return this.values[jobid][nodeid].shift(); - } - }; - this.getValues = function(jobid, nodeid, metricid) { - var results; - if (this.values[jobid] == null) { - return []; - } - if (this.values[jobid][nodeid] == null) { - return []; - } - results = []; - angular.forEach(this.values[jobid][nodeid], (function(_this) { - return function(v, k) { - if (v.values[metricid] != null) { - return results.push({ - x: v.timestamp, - y: v.values[metricid] - }); - } - }; - })(this)); - return results; - }; - this.setupLSFor = function(jobid, nodeid) { - if (this.metrics[jobid] == null) { - this.metrics[jobid] = {}; - } - if (this.metrics[jobid][nodeid] == null) { - return this.metrics[jobid][nodeid] = []; - } - }; - this.addMetric = function(jobid, nodeid, metricid) { - this.setupLSFor(jobid, nodeid); - this.metrics[jobid][nodeid].push({ - id: metricid, - size: 'small' - }); - return this.saveSetup(); - }; - this.removeMetric = (function(_this) { - return function(jobid, nodeid, metric) { - var i; - if (_this.metrics[jobid][nodeid] != null) { - i = _this.metrics[jobid][nodeid].indexOf(metric); - if (i === -1) { - i = _.findIndex(_this.metrics[jobid][nodeid], { - id: metric - }); - } - if (i !== -1) { - _this.metrics[jobid][nodeid].splice(i, 1); - } - return _this.saveSetup(); - } - }; - })(this); - this.setMetricSize = (function(_this) { - return function(jobid, nodeid, metric, size) { - var i; - if (_this.metrics[jobid][nodeid] != null) { - i = _this.metrics[jobid][nodeid].indexOf(metric.id); - if (i === -1) { - i = _.findIndex(_this.metrics[jobid][nodeid], { - id: metric.id - }); - } - if (i !== -1) { - _this.metrics[jobid][nodeid][i] = { - id: metric.id, - size: size - }; - } - return _this.saveSetup(); - } - }; - })(this); - this.orderMetrics = function(jobid, nodeid, item, index) { - this.setupLSFor(jobid, nodeid); - angular.forEach(this.metrics[jobid][nodeid], (function(_this) { - return function(v, k) { - if (v.id === item.id) { - _this.metrics[jobid][nodeid].splice(k, 1); - if (k < index) { - return index = index - 1; - } - } - }; - })(this)); - this.metrics[jobid][nodeid].splice(index, 0, item); - return this.saveSetup(); - }; - this.getMetricsSetup = (function(_this) { - return function(jobid, nodeid) { - return { - names: _.map(_this.metrics[jobid][nodeid], function(value) { - if (_.isString(value)) { - return { - id: value, - size: "small" - }; - } else { - return value; - } - }) - }; - }; - })(this); - this.getAvailableMetrics = (function(_this) { - return function(jobid, nodeid) { - var deferred; - _this.setupLSFor(jobid, nodeid); - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "jobs/" + jobid + "/vertices/" + nodeid + "/metrics").success(function(data) { - var results; - results = []; - angular.forEach(data, function(v, k) { - var i; - i = _this.metrics[jobid][nodeid].indexOf(v.id); - if (i === -1) { - i = _.findIndex(_this.metrics[jobid][nodeid], { - id: v.id - }); - } - if (i === -1) { - return results.push(v); - } - }); - return deferred.resolve(results); - }); - return deferred.promise; - }; - })(this); - this.getAllAvailableMetrics = (function(_this) { - return function(jobid, nodeid) { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "jobs/" + jobid + "/vertices/" + nodeid + "/metrics").success(function(data) { - return deferred.resolve(data); - }); - return deferred.promise; - }; - })(this); - this.getMetrics = function(jobid, nodeid, metricIds) { - var deferred, ids; - deferred = $q.defer(); - ids = metricIds.join(","); - $http.get(flinkConfig.jobServer + "jobs/" + jobid + "/vertices/" + nodeid + "/metrics?get=" + ids).success((function(_this) { - return function(data) { - var newValue, result; - result = {}; - angular.forEach(data, function(v, k) { - return result[v.id] = parseInt(v.value); - }); - newValue = { - timestamp: Date.now(), - values: result - }; - _this.saveValue(jobid, nodeid, newValue); - return deferred.resolve(newValue); - }; - })(this)); - return deferred.promise; - }; - this.setupLS(); - return this; -}]); - -angular.module('flinkApp').controller('OverviewController', ["$scope", "OverviewService", "JobsService", "$interval", "flinkConfig", function($scope, OverviewService, JobsService, $interval, flinkConfig) { - var refresh; - $scope.jobObserver = function() { - $scope.runningJobs = JobsService.getJobs('running'); - return $scope.finishedJobs = JobsService.getJobs('finished'); - }; - JobsService.registerObserver($scope.jobObserver); - $scope.$on('$destroy', function() { - return JobsService.unRegisterObserver($scope.jobObserver); - }); - $scope.jobObserver(); - OverviewService.loadOverview().then(function(data) { - return $scope.overview = data; - }); - refresh = $interval(function() { - return OverviewService.loadOverview().then(function(data) { - return $scope.overview = data; - }); - }, flinkConfig["refresh-interval"]); - return $scope.$on('$destroy', function() { - return $interval.cancel(refresh); - }); -}]); - -angular.module('flinkApp').service('OverviewService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) { - var overview; - overview = {}; - this.loadOverview = function() { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "overview").success(function(data, status, headers, config) { - overview = data; - return deferred.resolve(data); - }); - return deferred.promise; - }; - return this; -}]); - -angular.module('flinkApp').controller('JobSubmitController', ["$scope", "JobSubmitService", "$interval", "flinkConfig", "$state", "$location", function($scope, JobSubmitService, $interval, flinkConfig, $state, $location) { - var refresh; - $scope.yarn = $location.absUrl().indexOf("/proxy/application_") !== -1; - $scope.loadList = function() { - return JobSubmitService.loadJarList().then(function(data) { - $scope.address = data.address; - $scope.noaccess = data.error; - return $scope.jars = data.files; - }); - }; - $scope.defaultState = function() { - $scope.plan = null; - $scope.error = null; - return $scope.state = { - selected: null, - parallelism: "", - savepointPath: "", - allowNonRestoredState: false, - 'entry-class': "", - 'program-args': "", - 'plan-button': "Show Plan", - 'submit-button': "Submit", - 'action-time': 0 - }; - }; - $scope.defaultState(); - $scope.uploader = {}; - $scope.loadList(); - refresh = $interval(function() { - return $scope.loadList(); - }, flinkConfig["refresh-interval"]); - $scope.$on('$destroy', function() { - return $interval.cancel(refresh); - }); - $scope.selectJar = function(id) { - if ($scope.state.selected === id) { - return $scope.defaultState(); - } else { - $scope.defaultState(); - return $scope.state.selected = id; - } - }; - $scope.deleteJar = function(event, id) { - if ($scope.state.selected === id) { - $scope.defaultState(); - } - angular.element(event.currentTarget).removeClass("fa-remove").addClass("fa-spin fa-spinner"); - return JobSubmitService.deleteJar(id).then(function(data) { - angular.element(event.currentTarget).removeClass("fa-spin fa-spinner").addClass("fa-remove"); - if (data.error != null) { - return alert(data.error); - } - }); - }; - $scope.loadEntryClass = function(name) { - return $scope.state['entry-class'] = name; - }; - $scope.getPlan = function() { - var action; - if ($scope.state['plan-button'] === "Show Plan") { - action = new Date().getTime(); - $scope.state['action-time'] = action; - $scope.state['submit-button'] = "Submit"; - $scope.state['plan-button'] = "Getting Plan"; - $scope.error = null; - $scope.plan = null; - return JobSubmitService.getPlan($scope.state.selected, { - 'entry-class': $scope.state['entry-class'], - parallelism: $scope.state.parallelism, - 'program-args': $scope.state['program-args'] - }).then(function(data) { - if (action === $scope.state['action-time']) { - $scope.state['plan-button'] = "Show Plan"; - $scope.error = data.error; - return $scope.plan = data.plan; - } - }); - } - }; - $scope.runJob = function() { - var action; - if ($scope.state['submit-button'] === "Submit") { - action = new Date().getTime(); - $scope.state['action-time'] = action; - $scope.state['submit-button'] = "Submitting"; - $scope.state['plan-button'] = "Show Plan"; - $scope.error = null; - return JobSubmitService.runJob($scope.state.selected, { - 'entry-class': $scope.state['entry-class'], - parallelism: $scope.state.parallelism, - 'program-args': $scope.state['program-args'], - savepointPath: $scope.state['savepointPath'], - allowNonRestoredState: $scope.state['allowNonRestoredState'] - }).then(function(data) { - if (action === $scope.state['action-time']) { - $scope.state['submit-button'] = "Submit"; - $scope.error = data.error; - if (data.jobid != null) { - return $state.go("single-job.plan.subtasks", { - jobid: data.jobid - }); - } - } - }); - } - }; - $scope.nodeid = null; - $scope.changeNode = function(nodeid) { - if (nodeid !== $scope.nodeid) { - $scope.nodeid = nodeid; - $scope.vertex = null; - $scope.subtasks = null; - $scope.accumulators = null; - return $scope.$broadcast('reload'); - } else { - $scope.nodeid = null; - $scope.nodeUnfolded = false; - $scope.vertex = null; - $scope.subtasks = null; - return $scope.accumulators = null; - } - }; - $scope.clearFiles = function() { - return $scope.uploader = {}; - }; - $scope.uploadFiles = function(files) { - $scope.uploader = {}; - if (files.length === 1) { - $scope.uploader['file'] = files[0]; - return $scope.uploader['upload'] = true; - } else { - return $scope.uploader['error'] = "Did ya forget to select a file?"; - } - }; - return $scope.startUpload = function() { - var formdata, xhr; - if ($scope.uploader['file'] != null) { - formdata = new FormData(); - formdata.append("jarfile", $scope.uploader['file']); - $scope.uploader['upload'] = false; - $scope.uploader['success'] = "Initializing upload..."; - xhr = new XMLHttpRequest(); - xhr.upload.onprogress = function(event) { - $scope.uploader['success'] = null; - return $scope.uploader['progress'] = parseInt(100 * event.loaded / event.total); - }; - xhr.upload.onerror = function(event) { - $scope.uploader['progress'] = null; - return $scope.uploader['error'] = "An error occurred while uploading your file"; - }; - xhr.upload.onload = function(event) { - $scope.uploader['progress'] = null; - return $scope.uploader['success'] = "Saving..."; - }; - xhr.onreadystatechange = function() { - var response; - if (xhr.readyState === 4) { - response = JSON.parse(xhr.responseText); - if (response.error != null) { - $scope.uploader['error'] = response.error; - return $scope.uploader['success'] = null; - } else { - return $scope.uploader['success'] = "Uploaded!"; - } - } - }; - xhr.open("POST", "/jars/upload"); - return xhr.send(formdata); - } else { - return console.log("Unexpected Error. This should not happen"); - } - }; -}]).filter('getJarSelectClass', function() { - return function(selected, actual) { - if (selected === actual) { - return "fa-check-square"; - } else { - return "fa-square-o"; - } - }; -}); - -angular.module('flinkApp').service('JobSubmitService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) { - this.loadJarList = function() { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "jars/").success(function(data, status, headers, config) { - return deferred.resolve(data); - }); - return deferred.promise; - }; - this.deleteJar = function(id) { - var deferred; - deferred = $q.defer(); - $http["delete"](flinkConfig.jobServer + "jars/" + encodeURIComponent(id)).success(function(data, status, headers, config) { - return deferred.resolve(data); - }); - return deferred.promise; - }; - this.getPlan = function(id, args) { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "jars/" + encodeURIComponent(id) + "/plan", { - params: args - }).success(function(data, status, headers, config) { - return deferred.resolve(data); - }); - return deferred.promise; - }; - this.runJob = function(id, args) { - var deferred; - deferred = $q.defer(); - $http.post(flinkConfig.jobServer + "jars/" + encodeURIComponent(id) + "/run", {}, { - params: args - }).success(function(data, status, headers, config) { - return deferred.resolve(data); - }); - return deferred.promise; - }; - return this; -}]); - -angular.module('flinkApp').controller('AllTaskManagersController', ["$scope", "TaskManagersService", "$interval", "flinkConfig", function($scope, TaskManagersService, $interval, flinkConfig) { - var refresh; - TaskManagersService.loadManagers().then(function(data) { - return $scope.managers = data; - }); - refresh = $interval(function() { - return TaskManagersService.loadManagers().then(function(data) { - return $scope.managers = data; - }); - }, flinkConfig["refresh-interval"]); - return $scope.$on('$destroy', function() { - return $interval.cancel(refresh); - }); -}]).controller('SingleTaskManagerController', ["$scope", "$stateParams", "SingleTaskManagerService", "$interval", "flinkConfig", function($scope, $stateParams, SingleTaskManagerService, $interval, flinkConfig) { - var refresh; - $scope.metrics = {}; - SingleTaskManagerService.loadMetrics($stateParams.taskmanagerid).then(function(data) { - return $scope.metrics = data[0]; - }); - refresh = $interval(function() { - return SingleTaskManagerService.loadMetrics($stateParams.taskmanagerid).then(function(data) { - return $scope.metrics = data[0]; - }); - }, flinkConfig["refresh-interval"]); - return $scope.$on('$destroy', function() { - return $interval.cancel(refresh); - }); -}]).controller('SingleTaskManagerLogsController', ["$scope", "$stateParams", "SingleTaskManagerService", "$interval", "flinkConfig", function($scope, $stateParams, SingleTaskManagerService, $interval, flinkConfig) { - $scope.log = {}; - $scope.taskmanagerid = $stateParams.taskmanagerid; - SingleTaskManagerService.loadLogs($stateParams.taskmanagerid).then(function(data) { - return $scope.log = data; - }); - return $scope.reloadData = function() { - return SingleTaskManagerService.loadLogs($stateParams.taskmanagerid).then(function(data) { - return $scope.log = data; - }); - }; -}]).controller('SingleTaskManagerStdoutController', ["$scope", "$stateParams", "SingleTaskManagerService", "$interval", "flinkConfig", function($scope, $stateParams, SingleTaskManagerService, $interval, flinkConfig) { - $scope.stdout = {}; - $scope.taskmanagerid = $stateParams.taskmanagerid; - SingleTaskManagerService.loadStdout($stateParams.taskmanagerid).then(function(data) { - return $scope.stdout = data; - }); - return $scope.reloadData = function() { - return SingleTaskManagerService.loadStdout($stateParams.taskmanagerid).then(function(data) { - return $scope.stdout = data; - }); - }; -}]); - -angular.module('flinkApp').service('TaskManagersService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) { - this.loadManagers = function() { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "taskmanagers").success(function(data, status, headers, config) { - return deferred.resolve(data['taskmanagers']); - }); - return deferred.promise; - }; - return this; -}]).service('SingleTaskManagerService', ["$http", "flinkConfig", "$q", function($http, flinkConfig, $q) { - this.loadMetrics = function(taskmanagerid) { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "taskmanagers/" + taskmanagerid + "/metrics").success(function(data, status, headers, config) { - return deferred.resolve(data['taskmanagers']); - }); - return deferred.promise; - }; - this.loadLogs = function(taskmanagerid) { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "taskmanagers/" + taskmanagerid + "/log").success(function(data, status, headers, config) { - return deferred.resolve(data); - }); - return deferred.promise; - }; - this.loadStdout = function(taskmanagerid) { - var deferred; - deferred = $q.defer(); - $http.get(flinkConfig.jobServer + "taskmanagers/" + taskmanagerid + "/stdout").success(function(data, status, headers, config) { - return deferred.resolve(data); - }); - return deferred.promise; - }; - return this; -}]); - -//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmNvZmZlZSIsImluZGV4LmpzIiwiY29tbW9uL2RpcmVjdGl2ZXMuY29mZmVlIiwiY29tbW9uL2RpcmVjdGl2ZXMuanMiLCJjb21tb24vZmlsdGVycy5jb2ZmZWUiLCJjb21tb24vZmlsdGVycy5qcyIsImNvbW1vbi9zZXJ2aWNlcy5jb2ZmZWUiLCJjb21tb24vc2VydmljZXMuanMiLCJtb2R1bGVzL2pvYm1hbmFnZXIvam9ibWFuYWdlci5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvam9ibWFuYWdlci9qb2JtYW5hZ2VyLmN0cmwuanMiLCJtb2R1bGVzL2pvYm1hbmFnZXIvam9ibWFuYWdlci5zdmMuY29mZmVlIiwibW9kdWxlcy9qb2JtYW5hZ2VyL2pvYm1hbmFnZXIuc3ZjLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuY3RybC5jb2ZmZWUiLCJtb2R1bGVzL2pvYnMvam9icy5jdHJsLmpzIiwibW9kdWxlcy9qb2JzL2pvYnMuZGlyLmNvZmZlZSIsIm1vZHVsZXMvam9icy9qb2JzLmRpci5qcyIsIm1vZHVsZXMvam9icy9qb2JzLnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL2pvYnMvam9icy5zdmMuanMiLCJtb2R1bGVzL2pvYnMvbWV0cmljcy5kaXIuY29mZmVlIiwibW9kdWxlcy9qb2JzL21ldHJpY3MuZGlyLmpzIiwibW9kdWxlcy9qb2JzL21ldHJpY3Muc3ZjLmNvZmZlZSIsIm1vZHVsZXMvam9icy9tZXRyaWNzLnN2Yy5qcyIsIm1vZHVsZXMvb3ZlcnZpZXcvb3ZlcnZpZXcuY3RybC5jb2ZmZWUiLC Jtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LmN0cmwuanMiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL292ZXJ2aWV3L292ZXJ2aWV3LnN2Yy5qcyIsIm1vZHVsZXMvc3VibWl0L3N1Ym1pdC5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvc3VibWl0L3N1Ym1pdC5jdHJsLmpzIiwibW9kdWxlcy9zdWJtaXQvc3VibWl0LnN2Yy5jb2ZmZWUiLCJtb2R1bGVzL3N1Ym1pdC9zdWJtaXQuc3ZjLmpzIiwibW9kdWxlcy90YXNrbWFuYWdlci90YXNrbWFuYWdlci5jdHJsLmNvZmZlZSIsIm1vZHVsZXMvdGFza21hbmFnZXIvdGFza21hbmFnZXIuY3RybC5qcyIsIm1vZHVsZXMvdGFza21hbmFnZXIvdGFza21hbmFnZXIuc3ZjLmNvZmZlZSIsIm1vZHVsZXMvdGFza21hbmFnZXIvdGFza21hbmFnZXIuc3ZjLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQWtCQSxRQUFRLE9BQU8sWUFBWSxDQUFDLGFBQWEsaUJBQWlCLGFBSXpELG1CQUFJLFNBQUMsWUFBRDtFQUNILFdBQVcsaUJBQWlCO0VDckI1QixPRHNCQSxXQUFXLGNBQWMsV0FBQTtJQUN2QixXQUFXLGlCQUFpQixDQUFDLFdBQVc7SUNyQnhDLE9Ec0JBLFdBQVcsZUFBZTs7SUFJN0IsTUFBTSxlQUFlO0VBQ3BCLFdBQVc7RUFFWCxvQkFBb0I7R0FLckIsK0RBQUksU0FBQyxhQUFhLGFBQWEsYUFBYSxXQUF4QztFQzVCSCxPRDZCQSxZQUFZLGFBQWEsS0FBSyxTQUFDLFFBQUQ7SUFDNUIsUUFBUSxPQUFPLGFBQWE7SUFFNUIsWUFBWTtJQzd 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
<TRUNCATED>
