diff --git a/mrq/dashboard/app.py b/mrq/dashboard/app.py index 793d73d2..40d0828e 100644 --- a/mrq/dashboard/app.py +++ b/mrq/dashboard/app.py @@ -1,8 +1,10 @@ from __future__ import print_function from future import standard_library + standard_library.install_aliases() from future.utils import iteritems from gevent import monkey + monkey.patch_all() from flask import Flask, request, render_template @@ -14,6 +16,7 @@ from bson import ObjectId import json import argparse +import datetime from werkzeug.serving import run_simple sys.path.insert(0, os.getcwd()) @@ -46,24 +49,32 @@ def root(): return render_template("index.html", MRQ_CONFIG={ k: v for k, v in iteritems(cfg) if k in WHITELISTED_MRQ_CONFIG_KEYS - }) + }) @app.route('/api/datatables/taskexceptions') @requires_auth def api_task_exceptions(): - stats = list(connections.mongodb_jobs.mrq_jobs.aggregate([ - {"$match": {"status": "failed"}}, - {"$group": {"_id": {"path": "$path", "exceptiontype": "$exceptiontype"}, - "jobs": {"$sum": 1}}}, - ])) + group_criteria = [] + group_criteria.append({"$match": {"status": "failed"}}) + + if (request.args.get("name")): + group_criteria.append({"$match": {"path": request.args.get("name")}}) + + if (request.args.get("exception")): + group_criteria.append({"$match": {"exceptiontype": request.args.get("exception")}}) + + group_criteria.append({"$group": {"_id": {"path": "$path", "exceptiontype": "$exceptiontype"}, + "jobs": {"$sum": 1}}}) + + stats = list(connections.mongodb_jobs.mrq_jobs.aggregate(group_criteria)) stats.sort(key=lambda x: -x["jobs"]) - start = int(request.args.get("iDisplayStart", 0)) - end = int(request.args.get("iDisplayLength", 20)) + start + # start = int(request.args.get("iDisplayStart", 0)) + # end = int(request.args.get("iDisplayLength", 20)) + start data = { - "aaData": stats[start:end], + "aaData": stats, "iTotalDisplayRecords": len(stats) } @@ -96,10 +107,20 @@ def api_jobstatuses(): @app.route('/api/datatables/taskpaths') @requires_auth def api_taskpaths(): - stats = list(connections.mongodb_jobs.mrq_jobs.aggregate([ - {"$sort": {"path": 1}}, # https://jira.mongodb.org/browse/SERVER-11447 - {"$group": {"_id": "$path", "jobs": {"$sum": 1}}} - ])) + if request.args.get("name"): + name = request.args.get("name") + stats = list(connections.mongodb_jobs.mrq_jobs.aggregate([ + {"$sort": {"path": 1}}, # https://jira.mongodb.org/browse/SERVER-11447 + {"$match": {"path": name}}, + {"$group": {"_id": "$path", "jobs": {"$sum": 1}}}, + + ])) + else: + stats = list(connections.mongodb_jobs.mrq_jobs.aggregate([ + {"$sort": {"path": 1}}, # https://jira.mongodb.org/browse/SERVER-11447 + {"$group": {"_id": "$path", "jobs": {"$sum": 1}}}, + + ])) stats.sort(key=lambda x: -x["jobs"]) @@ -158,13 +179,43 @@ def build_api_datatables_query(req): except Exception as e: # pylint: disable=broad-except print("Error will converting form JSON: %s" % e) + # time filter + filter_by_date = False + if (request.args.get("startTime")): + filter_by_date = True + datetime_arr = request.args.get("startTime").split("T") + date_arr = datetime_arr[0].split("-") + year = int(date_arr[0]) + month = int(date_arr[1]) + day = int(date_arr[2]) + time_arr = datetime_arr[1].split(".")[0].split(":") + hours = int(time_arr[0]) + minutes = int(time_arr[1]) + + date_start = datetime.datetime(year, month, day, hours, minutes) + + if (request.args.get("endTime")): + filter_by_date = True + datetime_arr = request.args.get("endTime").split("T") + date_arr = datetime_arr[0].split("-") + year = int(date_arr[0]) + month = int(date_arr[1]) + day = int(date_arr[2]) + time_arr = datetime_arr[1].split(".")[0].split(":") + hours = int(time_arr[0]) + minutes = int(time_arr[1]) + + date_end = datetime.datetime(year, month, day, hours, minutes) + + if (filter_by_date): + query["datestarted"] = {"$gt": date_start, "$lt": date_end} + return query @app.route('/api/datatables/') @requires_auth def api_datatables(unit): - # import time # time.sleep(5) @@ -232,10 +283,51 @@ def api_datatables(unit): if request.args.get("showstopped"): query = {} + # time filter + filter_by_date = False + if (request.args.get("startTime")): + filter_by_date = True + datetime_arr = request.args.get("startTime").split("T") + date_arr = datetime_arr[0].split("-") + year = int(date_arr[0]) + month = int(date_arr[1]) + day = int(date_arr[2]) + time_arr = datetime_arr[1].split(".")[0].split(":") + hours = int(time_arr[0]) + minutes = int(time_arr[1]) + + date_start = datetime.datetime(year, month, day, hours, minutes) + + if (request.args.get("endTime")): + filter_by_date = True + datetime_arr = request.args.get("endTime").split("T") + date_arr = datetime_arr[0].split("-") + year = int(date_arr[0]) + month = int(date_arr[1]) + day = int(date_arr[2]) + time_arr = datetime_arr[1].split(".")[0].split(":") + hours = int(time_arr[0]) + minutes = int(time_arr[1]) + + date_end = datetime.datetime(year, month, day, hours, minutes) + + if (filter_by_date): + query["datereported"] = {"$gt": date_start, "$lt": date_end} + + elif unit == "scheduled_jobs": collection = connections.mongodb_jobs.mrq_scheduled_jobs fields = None query = {} + if (request.args.get("name")): + query["path"] = request.args.get("name") + if (request.args.get("interval")): + query["interval"] = request.args.get("interval") + if (request.args.get("params")): + query["params"] = request.args.get("params") + + # elif unit == "ops": + # collection = connections.mongodb_jobs.mrq_jobs elif unit == "jobs": @@ -256,11 +348,11 @@ def api_datatables(unit): if sort: cursor.sort(sort) - if skip is not None: - cursor.skip(skip) - - if limit is not None: - cursor.limit(limit) + # if skip is not None: + # cursor.skip(skip) + # + # if limit is not None: + # cursor.limit(limit) data = { "aaData": list(cursor), diff --git a/mrq/dashboard/static/css/mrq-dashboard.css b/mrq/dashboard/static/css/mrq-dashboard.css index d2d95a15..e9443313 100644 --- a/mrq/dashboard/static/css/mrq-dashboard.css +++ b/mrq/dashboard/static/css/mrq-dashboard.css @@ -94,4 +94,16 @@ div.dataTables_info { to { transform: rotate(360deg); } +} + +label.time-filter-tag{ + cursor: pointer; + color: #4d7dff; + transition: all 0.3s ease-in-out; + width: 150px; + font-size: 12px; +} + +label.time-filter-tag:hover{ + color: #002da5; } \ No newline at end of file diff --git a/mrq/dashboard/static/js/main.js b/mrq/dashboard/static/js/main.js index 0e50815e..33651599 100644 --- a/mrq/dashboard/static/js/main.js +++ b/mrq/dashboard/static/js/main.js @@ -12,106 +12,105 @@ require([ Backbone.Model.prototype.idAttribute = '_id'; // http://datatables.net/plug-ins/api#fnSetFilteringDelay - $.fn.dataTableExt.oApi.fnSetFilteringDelay = function ( oSettings, iDelay ) { - var _that = this; - - if ( iDelay === undefined ) { - iDelay = 250; - } - - this.each( function ( i ) { - $.fn.dataTableExt.iApiIndex = i; - var - $this = this, - oTimerId = null, - sPreviousSearch = null, - anControl = $( 'input', _that.fnSettings().aanFeatures.f ); - - anControl.unbind( 'keyup' ).bind( 'keyup', function() { - var $$this = $this; - - if (sPreviousSearch === null || sPreviousSearch != anControl.val()) { - window.clearTimeout(oTimerId); - sPreviousSearch = anControl.val(); - oTimerId = window.setTimeout(function() { - $.fn.dataTableExt.iApiIndex = i; - _that.fnFilter( anControl.val() ); - }, iDelay); - } - }); - - return this; - } ); - return this; - }; - - $.fn.dataTableExt.oApi.fnReloadAjax = function ( oSettings, sNewSource, fnCallback, bStandingRedraw ) - { - // DataTables 1.10 compatibility - if 1.10 then versionCheck exists. - // 1.10s API has ajax reloading built in, so we use those abilities - // directly. - if ( $.fn.dataTable.versionCheck ) { - var api = new $.fn.dataTable.Api( oSettings ); - - if ( sNewSource ) { - api.ajax.url( sNewSource ).load( fnCallback, !bStandingRedraw ); - } - else { - api.ajax.reload( fnCallback, !bStandingRedraw ); - } - return; - } - - if ( sNewSource !== undefined && sNewSource !== null ) { - oSettings.sAjaxSource = sNewSource; - } - - // Server-side processing should just call fnDraw - if ( oSettings.oFeatures.bServerSide ) { - this.fnDraw(); - return; - } - - this.oApi._fnProcessingDisplay( oSettings, true ); - var that = this; - var iStart = oSettings._iDisplayStart; - var aData = []; - - this.oApi._fnServerParams( oSettings, aData ); - - oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aData, function(json) { - /* Clear the old information from the table */ - that.oApi._fnClearTable( oSettings ); - - /* Got the data - add it to the table */ - var aData = (oSettings.sAjaxDataProp !== "") ? - that.oApi._fnGetObjectDataFn( oSettings.sAjaxDataProp )( json ) : json; - - for ( var i=0 ; i'+ - ''+ - ''+ - ''+ - '' - }, - "sPaginationType": "bs_full", - "bProcessing": true, - "bServerSide": true, - "bDeferRender": true, - "bDestroy": true, - "sAjaxSource": "/api/datatables/"+unit_name, - "fnServerData": function (sSource, aoData, fnCallback) { - self.loading = true; - _.each(self.filters, function(v, k) { - aoData.push({"name": k, "value": v}); - }); - - $.getJSON( sSource, aoData, function (json) { - self.dataTableRawData = json; - fnCallback(json); - }).always(function() { - self.loading = false; - self.trigger("loaded"); - }); - } - }; - }, - + initFilters: function () { + // Overload me! + }, - initDataTable:function(config) { - - var self = this; - - this.dataTable = this.$(".js-datatable").dataTable(config); - - this.dataTableRawData = []; - - // SEARCH - Add the placeholder for Search and Turn this into in-line form control - var search_input = this.dataTable.closest('.dataTables_wrapper').find('div[id$=_filter] input'); - search_input.attr('placeholder', 'Search'); - search_input.addClass('form-control input-sm'); - // LENGTH - Inline-Form control - var length_sel = this.dataTable.closest('.dataTables_wrapper').find('div[id$=_length] select'); - length_sel.addClass('form-control input-sm'); + getCommonDatatableConfig: function (unit_name) { + + var self = this; + + return { + "sPaginationType": "full_numbers", + "iDisplayLength": 20, + "fnDrawCallback": function () { + //self.$('.js-datatable .tooltip-top').tooltip(); + //self.$(".dataTables_filter input").prop("type","search").attr("results","10").attr("placeholder","Search products..."); + }, + //"bLengthChange":false, + //"aLengthMenu": [[25, 50, 100], [25, 50, 100]], + "sDom": "iprtipl", + "oLanguage": { + "sInfo": "Showing _START_ to _END_ of _TOTAL_ " + unit_name, + "sEmptyTable": "No " + unit_name, + "sInfoEmpty": "Showing 0 " + unit_name, + "sInfoFiltered": "", + "sLengthMenu": '' + }, + "aoColumnDefs": [{ + 'bSortable': true, + 'aTargets': [0] + }], + "sPaginationType": "bs_full", + "bProcessing": true, + "bDeferRender": true, + "bDestroy": true, + // "sAjaxSource": "/api/datatables/" + unit_name, + // "fnServerData": function (sSource, aoData, fnCallback) { + // self.loading = true; + // _.each(self.filters, function (v, k) { + // aoData.push({"name": k, "value": v}); + // }); + // + // $.getJSON(sSource, aoData, function (json) { + // self.dataTableRawData = json; + // fnCallback(json); + // console.log(sSource); + // $('#my-table').dataTable().fnAddData([ + // { + // c1: "dsdsd", + // c2: "aaaaaa", + // c3: "qqqqqq" + // } + // ]); + // //$('.datatable').dataTable().fnSort([[0, 'desc']]); + // }).always(function () { + // self.loading = false; + // self.trigger("loaded"); + // }); + // } + }; + }, - if (this.col) { - this.col.on("remove",function(m,c,options) { - noop(m,c); //required for jshint :/ - if (this.dataTable) { - this.dataTable.fnDeleteRow(options.index); - } - },this); - this.col.on("add",function(m,c/*,options*/) { - noop(c); //required for jshint :/ - if (this.dataTable) { - this.dataTable.fnAddData([m.toJSON()]); - } - },this); - } + initDataTable: function (config) { + + var self = this; + + this.dataTable = this.$(".js-datatable").dataTable(config); + + this.dataTableRawData = []; + + //SEARCH - Add the placeholder for Search and Turn this into in-line form control + // var search_input = this.dataTable.closest('.dataTables_wrapper').find('div[id$=_filter] input'); + // search_input.attr('placeholder', 'Search'); + // search_input.addClass('form-control input-sm'); + // // LENGTH - Inline-Form control + // var length_sel = this.dataTable.closest('.dataTables_wrapper').find('div[id$=_length] select'); + // length_sel.addClass('form-control input-sm'); + + if (this.col) { + this.col.on("remove", function (m, c, options) { + noop(m, c); //required for jshint :/ + if (this.dataTable) { + this.dataTable.fnDeleteRow(options.index); + } + }, this); + this.col.on("add", function (m, c/*,options*/) { + noop(c); //required for jshint :/ + if (this.dataTable) { + this.dataTable.fnAddData([m.toJSON()]); + } + }, this); + } + + //this.dataTable.fnSetFilteringDelay(); + + setTimeout(function () { + self.refreshDataTable(true); + }, 1000); + + self.updateTableData(); + + $('.datatable th').on('click', function () { + sortTableType = $(this).hasClass('sorting_asc') ? 'desc' : 'asc'; + sortTableIndex = ''; + var classList = this.classList; + for (var i = 0; i < classList.length; i++) { + if (classList[i].contains('sorted-by')) { + sortTableIndex = classList[i].split('-')[2]; + break; + } + } + }); + }, - this.dataTable.fnSetFilteringDelay(); + getRefreshInterval: function () { - setTimeout(function() { - self.refreshDataTable(true); - }, 1000); + var interval = parseInt($(".js-autorefresh").val(), 10) * 1000; - }, + if (!this.app.rootView.isTabVisible) { + interval = 0; + } - getRefreshInterval:function() { + return interval; - var interval = parseInt($(".js-autorefresh").val(), 10) * 1000; + }, - if (!this.app.rootView.isTabVisible) { - interval = 0; - } + queueDataTableRefresh: function () { - return interval; + var self = this; - }, + var interval = self.getRefreshInterval(); - queueDataTableRefresh:function() { + if (!interval) return console.log("cancel queue"); - var self = this; + clearTimeout(self.refreshDataTableTimeout); + self.refreshDataTableTimeout = setTimeout(function () { + self.refreshDataTable(); + }, interval); - var interval = self.getRefreshInterval(); + }, - if (!interval) return console.log("cancel queue"); + refreshDataTable: function (justQueue) { + if (!this.dataTable) return this.flush(); + var self = this; - clearTimeout(self.refreshDataTableTimeout); - self.refreshDataTableTimeout = setTimeout(function() { - self.refreshDataTable(); - }, interval); + var el = self.$(".js-datatable"); - }, + // We may have navigated away in the meantime + if (!el.is(":visible")) return; - refreshDataTable:function(justQueue) { + // Don't reload when a modal is shown + if ($(".modal:visible").length) { + $(".modal:visible").trigger("poll"); + justQueue = true; + } - if (!this.dataTable) return this.flush(); + // Don't reload when user is selecting text + if (window.getSelection && window.getSelection().extentOffset > 0 && window.getSelection().type == "Range") { + justQueue = true; + } - var self = this; + // Don't do multiple ajax requests at the same time + if (self.loading) { + justQueue = true; + } - var el = self.$(".js-datatable"); + if (justQueue) { + self.queueDataTableRefresh(); + } else { - // We may have navigated away in the meantime - if (!el.is(":visible")) return; + // this.once("loaded", function () { + // + // }); + self.queueDataTableRefresh(); + // This will call fnDraw which will reload the data + this.dataTable.fnAdjustColumnSizing(); + this.updateTableData(); + } - // Don't reload when a modal is shown - if ($(".modal:visible").length) { - $(".modal:visible").trigger("poll"); - justQueue = true; - } + }, - // Don't reload when user is selecting text - if (window.getSelection && window.getSelection().extentOffset > 0 && window.getSelection().type == "Range") { - justQueue = true; - } + renderFilters: function () { + var self = this; - // Don't do multiple ajax requests at the same time - if (self.loading) { - justQueue = true; - } + if (!this._rendered) return; - if (justQueue) { - self.queueDataTableRefresh(); - } else { + // if (this.filters["manufacturer"]) { + // this.$('.js-filter-manufacturer .js-filter-txt').html("Manufacturer: "+this.filters["manufacturer"]["name"]); + // } - this.once("loaded", function() { - self.queueDataTableRefresh(); - }); + }, - // This will call fnDraw which will reload the data - this.dataTable.fnAdjustColumnSizing(); - } + filterschanged: function (evt) { - }, + var self = this; - renderFilters: function() { - var self = this; + if (evt) { + evt.preventDefault(); + evt.stopPropagation(); + } - if (!this._rendered) return; + _.each(self.filters, function (v, k) { + var field = self.$(".js-datatable-filters-" + k); + if (field.is(':checkbox')) { + self.filters[k] = field.is(':checked') ? "1" : ""; + } else { + self.filters[k] = field.val(); + } + }); - // if (this.filters["manufacturer"]) { - // this.$('.js-filter-manufacturer .js-filter-txt').html("Manufacturer: "+this.filters["manufacturer"]["name"]); - // } + this.refreshDataTable(); + }, - }, + render: function () { + this.renderTemplate({"filters": this.filters || {}}); - filterschanged:function(evt) { + this.renderFilters(); - var self = this; + return this; + }, - if (evt) { - evt.preventDefault(); - evt.stopPropagation(); - } + setTableData: function (url, filterData) { + var self = this; + filterData.iDisplayStart = self.dataTable.fnSettings()._iDisplayStart; + filterData.iDisplayLength = self.dataTable.fnSettings()._iDisplayLength; + self.loading = true; + $('.ox-loader').show(); + $.getJSON(url, filterData, function (json) { + self.dataTable.fnClearTable(); + self.dataTable.fnAddData(json.aaData); + }).always(function () { + self.loading = false; + $('.ox-loader').hide(); + // this.trigger("loaded"); + }); + }, - _.each(self.filters, function(v, k) { - var field = self.$(".js-datatable-filters-"+k); - if (field.is(':checkbox')) { - self.filters[k] = field.is(':checked')?"1":""; - } else { - self.filters[k] = field.val(); + updateTableData: function () { + //overload me! } - }); - - this.refreshDataTable(); - }, - - render: function() { - this.renderTemplate({"filters": this.filters||{}}); - - this.renderFilters(); - - return this; - } - /* + /* - TODO + TODO - this.col.on("change",function() { - if (this.dataTable) this.dataTable.fnReloadAjax(); - },this); - */ + this.col.on("change",function() { + if (this.dataTable) this.dataTable.fnReloadAjax(); + },this); + */ - }); + }); - return dataTablePage; + return dataTablePage; }); diff --git a/mrq/dashboard/static/js/views/io.js b/mrq/dashboard/static/js/views/io.js index 6d1260d0..6cc0c443 100644 --- a/mrq/dashboard/static/js/views/io.js +++ b/mrq/dashboard/static/js/views/io.js @@ -20,7 +20,7 @@ define(["jquery", "underscore", "views/generic/datatablepage", "models"],functio { "sTitle": "Type", - "sClass": "col-type", + "sClass": "col-type sorted-by-type", "sType": "string", "sWidth":"150px", "mData":function(source, type, val) { @@ -132,7 +132,16 @@ define(["jquery", "underscore", "views/generic/datatablepage", "models"],functio }; this.initDataTable(datatableConfig); - } + }, + getFilterData: function(){ + return { + sEcho: 1 + }; + }, + + updateTableData: function(){ + this.setTableData('/api/datatables/ops', this.getFilterData()); + } }); }); diff --git a/mrq/dashboard/static/js/views/jobs.js b/mrq/dashboard/static/js/views/jobs.js index db96de54..fbec912e 100644 --- a/mrq/dashboard/static/js/views/jobs.js +++ b/mrq/dashboard/static/js/views/jobs.js @@ -1,444 +1,696 @@ -define(["jquery", "underscore", "views/generic/datatablepage", "models"],function($, _, DataTablePage, Models) { - - return DataTablePage.extend({ - - el: '.js-page-jobs', - - template:"#tpl-page-jobs", - - events:{ - "click .js-datatable-filters-submit": "filterschanged", - "click .js-datatable .js-actions button": "row_jobaction", - "click button.js-jobs-groupaction": "groupaction" - }, - - initFilters: function() { - this.filters = { - "worker": this.options.params.worker||"", - "queue": this.options.params.queue||"", - "path": this.options.params.path||"", - "status": this.options.params.status||"", - "exceptiontype": this.options.params.exceptiontype||"", - "params": this.options.params.params||"", - "id": this.options.params.id||"", - }; - }, - - setOptions:function(options) { - this.options = options; - this.initFilters(); - this.flush(); - }, - - refresh_logs:function(job_id) { - - var self = this; - - $.ajax("/api/logs?job="+job_id+"&last_log_id="+self.last_log_id, { - "type": "GET", - "success": function(data) { - if (!self.last_log_id) { - self.$(".js-jobs-modal .js-jobs-modal-content").html(""); - } - self.$(".js-jobs-modal .js-jobs-modal-content")[0].innerHTML += _.escape(data.logs); - self.last_log_id = data.last_log_id; +define(["jquery", "underscore", "views/generic/datatablepage", "models"], function ($, _, DataTablePage, Models) { + + return DataTablePage.extend({ + + el: '.js-page-jobs', + + template: "#tpl-page-jobs", + + events: { + "click .js-datatable-filters-submit": "filterschanged", + "click .js-datatable .js-actions button": "row_jobaction", + "click button.js-jobs-groupaction": "groupaction", + "click .hide-time-filter": "hidetimefilter", + "click .show-time-filter": "showtimefilter", }, - "error": function(xhr, status, error) { - alert("Error: "+error); - } - }); - - }, - - refreshStackTrace: function(jobId) { - var self = this; - - $.ajax("/api/job/"+jobId+"/traceback", { - "type": "GET", - "success": function(data) { - if (data["traceback"]) { - var stack = self.format_traceback(data["traceback"]); - self.$(".js-jobs-modal .js-jobs-modal-content").html(stack); - } - else { - var stack = self.format_traceback_history(data["traceback_history"]); - self.$(".js-jobs-modal .js-jobs-modal-content").html(stack); - } - self.$(".js-jobs-modal h4").html("Stack Trace"); - self.$(".js-jobs-modal").modal({}); + + initFilters: function () { + this.filters = { + "worker": this.options.params.worker || "", + "queue": this.options.params.queue || "", + "path": this.options.params.path || "", + "status": this.options.params.status || "", + "exceptiontype": this.options.params.exceptiontype || "", + "params": this.options.params.params || "", + "id": this.options.params.id || "", + "startTime": this.options.params.startTime || "", + "endTime": this.options.params.endTime || "" + }; + this.initTimeFilter(); }, - "error": function(xhr, status, error) { - alert("Error: "+error); - } - }); - }, + initTimeFilter: function () { + var self = this; + $('.time-filter-group').click(function () { + self.timeFilter.typeChanged(this); + }); + $('.time-filter-tag').click(function () { + self.filterRequest(this); + }); + }, - refreshCallStack: function(jobId) { - var self = this; - self.app.getJobsDataFromWorkers(function(err, jobs) { + hidetimefilter: function () { + $(".time-filter-container").css({"position": "relative"}); + $(".time-filter-container").animate({ + bottom: "+265", + }, { + duration: 300, + complete: function () { + $('.hide-time-filter').hide(); + $('.show-time-filter').show(); + } + }); + }, - if (err) { - self.$(".js-jobs-modal-callstack-outdated").show(); - } + showtimefilter: function () { + $(".time-filter-container").css({"position": "relative"}); + $(".time-filter-container").animate({ + bottom: "0", + }, { + duration: 300, + complete: function () { + $('.hide-time-filter').show(); + $('.show-time-filter').hide(); + $(".time-filter-container").css({"position": "static"}); + } + }); + }, - var job_data = _.find(jobs, function(job) { - return job.id == jobId; - }); + setOptions: function (options) { + this.options = options; + this.initFilters(); + this.flush(); + }, - if (job_data && job_data["stack"]) { - var stack = self.format_traceback((job_data["stack"] || []).join("")); - self.$(".js-jobs-modal .js-jobs-modal-content").html(stack); - } else { - self.$(".js-jobs-modal-callstack-outdated").show(); - } + refresh_logs: function (job_id) { + + var self = this; + + $.ajax("/api/logs?job=" + job_id + "&last_log_id=" + self.last_log_id, { + "type": "GET", + "success": function (data) { + if (!self.last_log_id) { + self.$(".js-jobs-modal .js-jobs-modal-content").html(""); + } + self.$(".js-jobs-modal .js-jobs-modal-content")[0].innerHTML += _.escape(data.logs); + self.last_log_id = data.last_log_id; + }, + "error": function (xhr, status, error) { + alert("Error: " + error); + } + }); - }); + }, - }, - groupaction: function(evt) { - evt.preventDefault(); - evt.stopPropagation(); + refreshStackTrace: function (jobId) { + var self = this; + + $.ajax("/api/job/" + jobId + "/traceback", { + "type": "GET", + "success": function (data) { + if (data["traceback"]) { + var stack = self.format_traceback(data["traceback"]); + self.$(".js-jobs-modal .js-jobs-modal-content").html(stack); + } + else { + var stack = self.format_traceback_history(data["traceback_history"]); + self.$(".js-jobs-modal .js-jobs-modal-content").html(stack); + } + self.$(".js-jobs-modal h4").html("Stack Trace"); + self.$(".js-jobs-modal").modal({}); + }, + "error": function (xhr, status, error) { + alert("Error: " + error); + } + }); + }, - var self = this; - var action = $(evt.target).data("action"); - var data = _.clone(this.filters); + refreshCallStack: function (jobId) { + var self = this; + self.app.getJobsDataFromWorkers(function (err, jobs) { - data["action"] = action; - self.jobaction(evt, data); + if (err) { + self.$(".js-jobs-modal-callstack-outdated").show(); + } - }, + var job_data = _.find(jobs, function (job) { + return job.id == jobId; + }); - format_traceback: function(stack) { + if (job_data && job_data["stack"]) { + var stack = self.format_traceback((job_data["stack"] || []).join("")); + self.$(".js-jobs-modal .js-jobs-modal-content").html(stack); + } else { + self.$(".js-jobs-modal-callstack-outdated").show(); + } - // Escape it to avoid XSS - stack = _.escape(stack.replace(/\\n/g, "
")); + }); - // Try to insert links to source code - var online_repositories = window.MRQ_CONFIG.dashboard_autolink_repositories || []; + }, + groupaction: function (evt) { + evt.preventDefault(); + evt.stopPropagation(); - if (online_repositories.length) { + var self = this; - stack = stack.replace(/File "(.+?)", line ([0-9]+)/gm, function(m, file_path, line) { - for (var i=0;i"+file_path+"", line "+line+""; - } - } - return m; - }); - - } - - return stack; - }, - format_traceback_history: function(stacks) { - var self = this; - var full_history = ""; - _.each(stacks, function(stack) { - full_history += "
" + stack["date"] + "
"; - if (stack["original_traceback"]) { - full_history += "Original trace"; - full_history += self.format_traceback(stack["original_traceback"] || ""); - } - full_history += "Trace" + "
"; - full_history += self.format_traceback(stack["traceback"] || ""); - full_history += "---------------------------------------------------------------------" + "
"; - }); - return full_history; - }, - row_jobaction:function(evt) { - evt.preventDefault(); - evt.stopPropagation(); + var action = $(evt.target).data("action"); + var data = _.clone(this.filters); - var self = this; + data["action"] = action; + self.jobaction(evt, data); - var job_id = $(evt.currentTarget).closest(".js-actions").data("jobid"); - var action = $(evt.currentTarget).data("action"); + }, - self.$(".js-jobs-modal").unbind(); + format_traceback: function (stack) { - if (action == "viewresult") { + // Escape it to avoid XSS + stack = _.escape(stack.replace(/\\n/g, "
")); - $.ajax("/api/job/"+job_id+"/result", { - "type": "GET", - "success": function(data) { - self.$(".js-jobs-modal .js-jobs-modal-content").html(_.escape(JSON.stringify(data, null, 2))); - self.$(".js-jobs-modal h4").html("Job result"); - self.$(".js-jobs-modal").modal({}); - }, - "error": function(xhr, status, error) { - alert("Error: "+error); - } - }); + // Try to insert links to source code + var online_repositories = window.MRQ_CONFIG.dashboard_autolink_repositories || []; - } else if (action == "viewlogs") { + if (online_repositories.length) { - self.last_log_id = ""; + stack = stack.replace(/File "(.+?)", line ([0-9]+)/gm, function (m, file_path, line) { + for (var i = 0; i < online_repositories.length; i++) { + var regex = new RegExp(online_repositories[i][0]); + if (file_path.match(regex)) { + var file_url = file_path.replace(regex, online_repositories[i][1]); + return "File "" + file_path + "", line " + line + ""; + } + } + return m; + }); - self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading..."); - self.$(".js-jobs-modal h4").html("Job logs"); - self.$(".js-jobs-modal").modal({}); + } - // These poll events are sent by the generic datatable refresh() method - self.$(".js-jobs-modal").on("poll", function() { - self.refresh_logs(job_id); - }); - self.refresh_logs(job_id); + return stack; + }, + format_traceback_history: function (stacks) { + var self = this; + var full_history = ""; + _.each(stacks, function (stack) { + full_history += "
" + stack["date"] + "
"; + if (stack["original_traceback"]) { + full_history += "Original trace"; + full_history += self.format_traceback(stack["original_traceback"] || ""); + } + full_history += "Trace" + "
"; + full_history += self.format_traceback(stack["traceback"] || ""); + full_history += "---------------------------------------------------------------------" + "
"; + }); + return full_history; + }, + row_jobaction: function (evt) { + evt.preventDefault(); + evt.stopPropagation(); - } else if (action == "copycommand") { + var self = this; - var html = ""; + var job_id = $(evt.currentTarget).closest(".js-actions").data("jobid"); + var action = $(evt.currentTarget).data("action"); - var job_data = self.jobData[job_id]; + self.$(".js-jobs-modal").unbind(); - self.$(".js-jobs-modal .js-jobs-modal-content").html(html); - self.$(".js-jobs-modal h4").html("Command-line for this job"); - self.$(".js-jobs-modal").modal({}); + if (action == "viewresult") { - self.$(".js-jobs-modal textarea")[0].value = "mrq-run " + job_data.path + " '" + JSON.stringify(job_data.params).replace("'","\\'") + "'"; + $.ajax("/api/job/" + job_id + "/result", { + "type": "GET", + "success": function (data) { + self.$(".js-jobs-modal .js-jobs-modal-content").html(_.escape(JSON.stringify(data, null, 2))); + self.$(".js-jobs-modal h4").html("Job result"); + self.$(".js-jobs-modal").modal({}); + }, + "error": function (xhr, status, error) { + alert("Error: " + error); + } + }); - } else if (action == "viewtraceback") { + } else if (action == "viewlogs") { - self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading..."); - self.$(".js-jobs-modal h4").html("Stack Trace"); - self.$(".js-jobs-modal").modal({}); + self.last_log_id = ""; - self.$(".js-jobs-modal").on("poll", function() { - self.refreshStackTrace(job_id); - }); - self.refreshStackTrace(job_id); + self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading..."); + self.$(".js-jobs-modal h4").html("Job logs"); + self.$(".js-jobs-modal").modal({}); - } else if (action == "viewcallstack") { + // These poll events are sent by the generic datatable refresh() method + self.$(".js-jobs-modal").on("poll", function () { + self.refresh_logs(job_id); + }); + self.refresh_logs(job_id); - self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading..."); - self.$(".js-jobs-modal h4").html("Current call stack"); - self.$(".js-jobs-modal").modal({}); + } else if (action == "copycommand") { - self.$(".js-jobs-modal").on("poll", function() { - self.refreshCallStack(job_id); - }); - self.refreshCallStack(job_id); + var html = ""; - } else { + var job_data = self.jobData[job_id]; - self.jobaction(evt, { - "id": job_id, - "action": action - }); + self.$(".js-jobs-modal .js-jobs-modal-content").html(html); + self.$(".js-jobs-modal h4").html("Command-line for this job"); + self.$(".js-jobs-modal").modal({}); - } + self.$(".js-jobs-modal textarea")[0].value = "mrq-run " + job_data.path + " '" + JSON.stringify(job_data.params).replace("'", "\\'") + "'"; - }, + } else if (action == "viewtraceback") { - jobaction:function(evt, data) { + self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading..."); + self.$(".js-jobs-modal h4").html("Stack Trace"); + self.$(".js-jobs-modal").modal({}); - $(evt.target).find(".glyphicon").addClass("spin"); + self.$(".js-jobs-modal").on("poll", function () { + self.refreshStackTrace(job_id); + }); + self.refreshStackTrace(job_id); - $.ajax("/api/jobaction", { - "type": "POST", - "data": data, - "success": function(data) { - }, - "error": function(xhr, status, error) { - alert("Error: "+error); - }, - "complete": function() { - setTimeout(function() { - $(evt.target).find(".glyphicon").removeClass("spin"); - }, 500); - } - }); - }, + } else if (action == "viewcallstack") { - renderDatatable:function() { + self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading..."); + self.$(".js-jobs-modal h4").html("Current call stack"); + self.$(".js-jobs-modal").modal({}); - var self = this; + self.$(".js-jobs-modal").on("poll", function () { + self.refreshCallStack(job_id); + }); + self.refreshCallStack(job_id); - var datatableConfig = self.getCommonDatatableConfig("jobs"); + } else { - _.extend(datatableConfig, { - "aoColumns": [ + self.jobaction(evt, { + "id": job_id, + "action": action + }); - { - "sTitle": "Path & ID", - "sClass": "col-jobs-path", - "sWidth":"35%", - "mDataProp": "path", - "mData": function ( source /*, val */) { - return ""+source.path+""+ - "

"+source._id+""; } - }, - { - "sTitle": "Params", - "sWidth":"65%", - "sClass": "col-jobs-params", - "mDataProp": "params", - "mData": function ( source /*, val */) { - return "
"+_.escape(JSON.stringify(source.params, null, 2))+"
"; - } - }, - { - "sTitle": "Status", - "sType":"string", - "sWidth":"100px", - "sClass": "col-jobs-status", - "mData":function(source, type/*, val*/) { - if (type == "display") { - - var status_classes = { - 'started': "label-success", - 'success': "label-success", - 'timeout': "label-danger", - 'failed': "label-danger", - 'maxretries': "label-danger", - 'interrupt': "label-danger", - 'cancel': "label-warning", - 'abort': "label-warning", - 'retry': "label-warning" - }; - var css_class = status_classes[source.status] || "label-info"; - - html = "
" + "" + (source.status || "queued") + ""; - html += "

"; - - if (source.progress) { - var progress = (Math.round(source.progress*10000)/100); - html += '
'+progress+'%
'; - } - html += ""; - html += "

"; - html += "
"; - return (html); - } else { - return source.status || "queued"; - } - } - }, - { - "sTitle": "Time", - "sType":"string", - "sWidth":"100px", - "sClass": "col-jobs-time", - "mData":function(source, type/*, val*/) { - - if (type == "display") { - var display = [ - "queued "+moment.utc(1000 * parseInt(source._id.substring(0, 8), 16)).fromNow() - //"updated "+moment.utc(source.dateupdated).fromNow() - ]; - - if (source.datestarted) { - display.push("started "+moment.utc(source.datestarted).fromNow()); - } - if (source.totaltime) { - display.push("totaltime "+String(source.totaltime).substring(0,6)+"s"); + }, + + jobaction: function (evt, data) { + + $(evt.target).find(".glyphicon").addClass("spin"); + + $.ajax("/api/jobaction", { + "type": "POST", + "data": data, + "success": function (data) { + }, + "error": function (xhr, status, error) { + alert("Error: " + error); + }, + "complete": function () { + setTimeout(function () { + $(evt.target).find(".glyphicon").removeClass("spin"); + }, 500); } - if (source.time) { - display.push("cputime "+String(source.time).substring(0,6)+"s ("+source.switches+" switches)"); + }); + }, + + renderDatatable: function () { + var self = this; + this.initFilters(); + + var datatableConfig = self.getCommonDatatableConfig("jobs"); + + _.extend(datatableConfig, { + "aoColumns": [ + + { + "sTitle": "Path & ID", + "sClass": "col-jobs-path", + "sWidth": "35%", + "mDataProp": "path", + "mData": function (source /*, val */) { + return "" + source.path + "" + + "

" + source._id + ""; + } + }, + { + "sTitle": "Params", + "sWidth": "65%", + "sClass": "col-jobs-params", + "mDataProp": "params", + "mData": function (source /*, val */) { + return "
" + _.escape(JSON.stringify(source.params, null, 2)) + "
"; + } + }, + { + "sTitle": "Status", + "sType": "string", + "sWidth": "100px", + "sClass": "col-jobs-status", + "mData": function (source, type/*, val*/) { + if (type == "display") { + + var status_classes = { + 'started': "label-success", + 'success': "label-success", + 'timeout': "label-danger", + 'failed': "label-danger", + 'maxretries': "label-danger", + 'interrupt': "label-danger", + 'cancel': "label-warning", + 'abort': "label-warning", + 'retry': "label-warning" + }; + var css_class = status_classes[source.status] || "label-info"; + + html = "
" + "" + (source.status || "queued") + ""; + html += "

"; + + if (source.progress) { + var progress = (Math.round(source.progress * 10000) / 100); + html += '
' + progress + '%
'; + } + + html += ""; + html += "

"; + html += "
"; + return (html); + } else { + return source.status || "queued"; + } + } + }, + { + "sTitle": "Time", + "sType": "string", + "sWidth": "100px", + "sClass": "col-jobs-time", + "mData": function (source, type/*, val*/) { + + if (type == "display") { + var display = [ + "queued " + moment.utc(1000 * parseInt(source._id.substring(0, 8), 16)).fromNow() + //"updated "+moment.utc(source.dateupdated).fromNow() + ]; + + if (source.datestarted) { + display.push("started " + moment.utc(source.datestarted).fromNow()); + } + if (source.totaltime) { + display.push("totaltime " + String(source.totaltime).substring(0, 6) + "s"); + } + if (source.time) { + display.push("cputime " + String(source.time).substring(0, 6) + "s (" + source.switches + " switches)"); + } + + return "" + display.join("
") + "
"; + + } else { + return source.datestarted || ""; + } + } + }, + { + "sTitle": "Queue", + "sType": "string", + "sWidth": "100px", + "sClass": "col-jobs-queue", + "mData": function (source, type/*, val*/) { + if (type == "display") { + return source.queue ? ("" + source.queue + "") : ""; + } else { + return source.queue || ""; + } + + } + }, + { + "sTitle": "Worker", + "sType": "string", + "sWidth": "140px", + "sClass": "col-jobs-worker", + "mData": function (source, type/*, val*/) { + if (type == "display") { + return source.worker ? ("" + source.worker + "") : ""; + } else { + return source.worker || ""; + } + } + }, + { + "sTitle": "Actions", + "sType": "string", + "sWidth": "200px", + "sClass": "col-jobs-action", + "mData": function (source, type) { + if (type == "display") { + return "
" + + "" + + "" + + "

" + + "" + + "

" + + "" + + "" + + "
"; + } + return ""; + } + } + + + ], + + "fnDrawCallback": function (oSettings) { + + self.jobData = {}; + + _.each(oSettings.aoData, function (row) { + var oData = row._aData; + self.jobData[oData["_id"]] = oData; + }); } + }); - return "" + display.join("
") + "
"; + this.initDataTable(datatableConfig); - } else { - return source.datestarted || ""; - } + if (!_.any(this.filters, function (v, k) { + return v; + })) { + this.$(".js-jobs-groupactions").hide(); } - }, - { - "sTitle": "Queue", - "sType":"string", - "sWidth":"100px", - "sClass": "col-jobs-queue", - "mData":function(source, type/*, val*/) { - if (type == "display") { - return source.queue?(""+source.queue+""):""; - } else { - return source.queue || ""; - } - } - }, - { - "sTitle": "Worker", - "sType":"string", - "sWidth":"140px", - "sClass": "col-jobs-worker", - "mData":function(source, type/*, val*/) { - if (type == "display") { - return source.worker?(""+source.worker+""):""; - } else { - return source.worker || ""; - } - } - }, - { - "sTitle": "Actions", - "sType":"string", - "sWidth":"200px", - "sClass": "col-jobs-action", - "mData":function(source, type) { - if (type == "display") { - return "
"+ - ""+ - ""+ - "

"+ - ""+ - "

"+ - ""+ - ""+ - "
"; - } - return ""; - } - } + }, + filterschanged: function (evt) { - ], - "aaSorting":[ [0,'asc'] ], + var self = this; - "fnDrawCallback": function (oSettings) { + if (evt) { + evt.preventDefault(); + evt.stopPropagation(); + } - self.jobData = {}; + _.each(self.filters, function (v, k) { + self.filters[k] = self.$(".js-datatable-filters-" + k).val(); + }); - _.each(oSettings.aoData,function(row) { - var oData = row._aData; - self.jobData[oData["_id"]] = oData; - }); - } - }); - this.initDataTable(datatableConfig); - - if (!_.any(this.filters, function(v, k) { - return v; - })) { - this.$(".js-jobs-groupactions").hide(); - } + self.updateTableData(); + window.location = "/#jobs?" + $.param(self.filters, true).replace(/\+/g, "%20"); + }, - }, + getFilterData: function () { + //this.updateTimeFilter(); + return { + id: $('#jobs-form-id').val(), + queue: $('#jobs-form-queue').val(), + worker: $('#jobs-form-worker').val(), + path: $('#jobs-form-path').val(), + status: $('#jobs-form-status').val(), + params: $('#jobs-form-params').val(), + exceptiontype: $('#jobs-form-exceptiontype').val(), + startTime: this.filters.startTime, + endTime: this.filters.endTime, + sEcho: 1 + }; + }, - filterschanged:function(evt) { + updateTableData: function () { + this.setTableData('/api/datatables/jobs', this.getFilterData()); + }, - var self = this; + timefiltergroupchanged: function () { + this.timeFilter.typeChanged(); + }, - if (evt) { - evt.preventDefault(); - evt.stopPropagation(); - } + filterRequest: function (component) { + var result = this.timeFilter.filterRequest(component); + this.filters.startTime = result[0]; + this.filters.endTime = result[1]; + this.updateTableData(); + }, - _.each(self.filters, function(v, k) { - self.filters[k] = self.$(".js-datatable-filters-"+k).val(); - }); + timeFilter: { + show: function () { + + }, + hide: function () { + + }, + typeChanged: function (component) { + $('.time-filter-group').removeClass('active'); + $(component).addClass('active'); + + }, + filterRequest: function (component) { + var criteria = $(component).attr('data-filter'); + var dateStart = new Date(); + var dateEnd = new Date(); + if (criteria != '') { + if (criteria.contains('last')) { + if (criteria.contains('min')) { + var mins = parseInt(criteria.split('_')[1]); + dateStart.setMinutes(dateStart.getMinutes() - mins); + } + if (criteria.contains('days')) { + var days = parseInt(criteria.split('_')[1]); + dateStart.setDate(dateStart.getDate() - days); + } + if (criteria.contains('hours')) { + var hours = parseInt(criteria.split('_')[1]); + dateStart.setHours(dateStart.getHours() - hours); + } + } + if (criteria == 'today') { + dateStart = this.getBeginingOfDay(dateStart); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == 'yesterday') { + dateStart.setDate(dateStart.getDate() - 1); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() - 1); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == '2_days_ago') { + dateStart.setDate(dateStart.getDate() - 2); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() - 2); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == '7_days_ago') { + dateStart.setDate(dateStart.getDate() - 7); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() - 7); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == 'this_week') { + dateStart.setDate(dateStart.getDate() + + (0 - dateStart.getDay())); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() + (6 - dateEnd.getDay())); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == 'week_until_now') { + dateStart.setDate(dateStart.getDate() + + (0 - dateStart.getDay())); + dateStart = this.getBeginingOfDay(dateStart); + } + if (criteria == 'prev_week') { + dateStart.setDate(dateStart.getDate() - 7); + dateStart.setDate(dateStart.getDate() + + (0 - dateStart.getDay())); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() - 7); + dateEnd.setDate(dateEnd.getDate() + (6 - dateEnd.getDay())); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == 'this_month') { + dateStart = this.getBeginingOfMonth(dateStart); + + dateEnd = this.getEndOfMonth(dateEnd); + } + if (criteria == 'month_until_now') { + dateStart = this.getBeginingOfMonth(dateStart); + } + if (criteria == 'last_6_months') { + dateStart.setMonth(dateStart.getMonth() - 6); + dateStart = this.getBeginingOfMonth(dateStart); + + dateEnd = this.getEndOfMonth(dateEnd); + } + if (criteria == 'prev_month') { + dateStart.setMonth(dateStart.getMonth() - 1); + dateStart = this.getBeginingOfMonth(dateStart); + + dateEnd.setMonth(dateEnd.getMonth() - 1); + dateEnd = this.getEndOfMonth(dateEnd); + } + if (criteria == 'prev_year') { + dateStart.setFullYear(dateStart.getFullYear() - 1); + dateStart = this.getBeginingOfYear(dateStart); + + dateEnd.setFullYear(dateEnd.getFullYear() - 1); + dateEnd = this.getEndOfYear(dateEnd); + } + if (criteria == 'this_year') { + dateStart = this.getBeginingOfYear(dateStart); + + dateEnd = this.getEndOfYear(dateEnd); + } + if (criteria == 'last_year') { + dateStart.setFullYear(dateStart.getFullYear() - 1); + dateStart = this.getBeginingOfYear(dateStart); + } + if (criteria == 'year_until_now') { + dateStart = this.getBeginingOfYear(dateStart); + } + if (criteria == 'last_2_years') { + dateStart.setFullYear(dateStart.getFullYear() - 2); + dateStart = this.getBeginingOfYear(dateStart); + } + if (criteria == 'last_5_years') { + dateStart.setFullYear(dateStart.getFullYear() - 5); + dateStart = this.getBeginingOfYear(dateStart); + } + if (criteria == 'today_until_now') { + dateStart = this.getBeginingOfDay(dateStart); + } + return [this.getISODate(dateStart), this.getISODate(dateEnd)]; + } + }, + getBeginingOfDay: function (date) { + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); + date.setMilliseconds(0); + return date; + }, + getEndOfDay: function (date) { + date.setHours(23); + date.setMinutes(59); + date.setSeconds(59); + date.setMilliseconds(999); + return date; + }, + getBeginingOfMonth: function (date) { + date.setDate(1); + date = this.getBeginingOfDay(date); + return date; + }, + getEndOfMonth: function (date) { + date.setMonth(date.getMonth() + 1); + date = this.getBeginingOfMonth(date); + date.setMilliseconds(-1); + return date; + }, + getBeginingOfYear: function (date) { + date.setMonth(0); + date = this.getBeginingOfMonth(date); + return date; + }, + getEndOfYear: function (date) { + date.setFullYear(date.getFullYear() + 1); + date = this.getBeginingOfYear(date); + date.setMilliseconds(-1); + return date; + }, + getISODate: function (date) { + return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + + date.getDate() + "T" + date.getHours() + ":" + + date.getMinutes() + ":" + date.getSeconds() + "." + + date.getMilliseconds(); + } - window.location = "/#jobs?"+$.param(self.filters, true).replace(/\+/g, "%20"); - }, + } - }); + }); }); diff --git a/mrq/dashboard/static/js/views/queues.js b/mrq/dashboard/static/js/views/queues.js index a1fe4bb3..5f5d7513 100644 --- a/mrq/dashboard/static/js/views/queues.js +++ b/mrq/dashboard/static/js/views/queues.js @@ -158,7 +158,24 @@ define(["jquery", "underscore", "views/generic/datatablepage", "models"],functio this.initDataTable(datatableConfig); - } + }, + + getFilterData: function(){ + return { + id: $('#jobs-form-id').val(), + queue: $('#jobs-form-queue').val(), + worker: $('#jobs-form-worker').val(), + path: $('#jobs-form-path').val(), + status: $('#jobs-form-status').val(), + params: $('#jobs-form-params').val(), + exceptiontype: $('#jobs-form-exceptiontype').val(), + sEcho: 1 + }; + }, + + updateTableData: function(){ + this.setTableData('/api/datatables/queues', this.getFilterData()); + } }); }); diff --git a/mrq/dashboard/static/js/views/root.js b/mrq/dashboard/static/js/views/root.js index afcbfb5f..a8926719 100644 --- a/mrq/dashboard/static/js/views/root.js +++ b/mrq/dashboard/static/js/views/root.js @@ -33,6 +33,7 @@ define(["views/generic/page", "jquery", var onchange = function(evt) { + var prevVisible = self.isTabVisible; var v = true, h = false, evtMap = { diff --git a/mrq/dashboard/static/js/views/scheduledjobs.js b/mrq/dashboard/static/js/views/scheduledjobs.js index 0698c04f..df7aed42 100644 --- a/mrq/dashboard/static/js/views/scheduledjobs.js +++ b/mrq/dashboard/static/js/views/scheduledjobs.js @@ -1,79 +1,125 @@ -define(["jquery", "underscore", "views/generic/datatablepage", "models"],function($, _, DataTablePage, Models) { +define(["jquery", "underscore", "views/generic/datatablepage", "models"], function ($, _, DataTablePage, Models) { - return DataTablePage.extend({ + return DataTablePage.extend({ - el: '.js-page-scheduledjobs', + el: '.js-page-scheduledjobs', - template:"#tpl-page-scheduledjobs", + template: "#tpl-page-scheduledjobs", - events:{ - }, + events: { + "click .js-datatable-filters-submit": "filterschanged" + }, - renderDatatable:function() { + initFilters: function () { + this.filters = { + "name": this.options.params.name || "", + "interval": this.options.params.interval || "", + "last_queued": this.options.params.last_queued || "", + "params": this.options.params.params || "" + }; + }, - var self = this; + setOptions: function (options) { + this.options = options; + this.initFilters(); + this.flush(); + }, - var datatableConfig = self.getCommonDatatableConfig("scheduled_jobs"); + renderDatatable: function () { - _.extend(datatableConfig, { - "aoColumns": [ + var self = this; - { - "sTitle": "Name", - "sClass": "col-jobs-path", - "mDataProp": "path", - "fnRender": function ( o /*, val */) { - return ""+o.aData.path+""+ - "

"+o.aData._id+""; - } - }, - - { - "sTitle": "Interval", - "sType":"numeric", - "sClass": "col-jobs-interval", - "mData":function(source, type) { - if (type == "display") { - return moment.duration(source.interval*1000).humanize(); - } - return source.interval; - } - }, - // { - // "sTitle": "Daily time", - // "sType":"string", - // "sClass": "col-jobs-dailytime", - // "mData":function(source, type) { - // if (type == "display") { - // return moment.duration(source.interval*1000).humanize(); - // } - // return source.interval; - // } - // }, - { - "sTitle": "Last Queued", - "sType":"string", - "sClass": "col-jobs-lastqueued", - "mData":function(source, type) { - return moment.utc(source.datelastqueued).fromNow(); - } - }, - { - "sTitle": "Params", - "sClass": "col-jobs-params", - "mDataProp": "params", - "fnRender": function ( o /*, val */) { - return "
"+_.escape(JSON.stringify(o.aData.params, null, 2))+"
"; + var datatableConfig = self.getCommonDatatableConfig("scheduled_jobs"); + + _.extend(datatableConfig, { + "aoColumns": [ + + { + "sTitle": "Name", + "sClass": "col-jobs-path", + "mDataProp": "path", + "fnRender": function (o /*, val */) { + return "" + o.aData.path + "" + + "

" + o.aData._id + ""; + } + }, + + { + "sTitle": "Interval", + "sType": "numeric", + "sClass": "col-jobs-interval", + "mData": function (source, type) { + if (type == "display") { + return moment.duration(source.interval * 1000).humanize(); + } + return source.interval; + } + }, + // { + // "sTitle": "Daily time", + // "sType":"string", + // "sClass": "col-jobs-dailytime", + // "mData":function(source, type) { + // if (type == "display") { + // return moment.duration(source.interval*1000).humanize(); + // } + // return source.interval; + // } + // }, + { + "sTitle": "Last Queued", + "sType": "string", + "sClass": "col-jobs-lastqueued", + "mData": function (source, type) { + return moment.utc(source.datelastqueued).fromNow(); + } + }, + { + "sTitle": "Params", + "sClass": "col-jobs-params", + "mDataProp": "params", + "fnRender": function (o /*, val */) { + return "
" + _.escape(JSON.stringify(o.aData.params, null, 2)) + "
"; + } + }, + + ], + "aaSorting": [[0, 'asc']], + }); + + this.initDataTable(datatableConfig); + + }, + + filterschanged: function (evt) { + + var self = this; + + if (evt) { + evt.preventDefault(); + evt.stopPropagation(); } - }, - ], - "aaSorting":[ [0,'asc'] ], - }); + _.each(self.filters, function (v, k) { + self.filters[k] = self.$(".js-datatable-filters-" + k).val(); + }); + this.updateTableData(); + window.location = "/#scheduled_jobs?" + $.param(self.filters, true).replace(/\+/g, "%20"); + }, - this.initDataTable(datatableConfig); + getFilterData: function () { + return { + sEcho: 1, + name: $('#scheduledjobs-form-name').val(), + interval: $('#scheduledjobs-form-interval').val(), + last_queued: $('#scheduledjobs-form-last_queued').val(), + params: $('#scheduledjobs-form-params').val() + }; + }, - } - }); + updateTableData: function () { + this.setTableData('/api/datatables/scheduled_jobs', this.getFilterData()); + } + }); }); diff --git a/mrq/dashboard/static/js/views/status.js b/mrq/dashboard/static/js/views/status.js index 2b6a46c8..ecaa93ed 100644 --- a/mrq/dashboard/static/js/views/status.js +++ b/mrq/dashboard/static/js/views/status.js @@ -1,82 +1,96 @@ -define(["jquery", "underscore", "views/generic/datatablepage", "models", "moment"],function($, _, DataTablePage, Models, moment) { - - return DataTablePage.extend({ - - el: '.js-page-status', - - template:"#tpl-page-status", - - events:{ - }, - - renderDatatable:function() { - - var self = this; - - var datatableConfig = self.getCommonDatatableConfig("status"); - - _.extend(datatableConfig, { - "aoColumns": [ - - { - "sTitle": "Status", - "sClass": "col-status", - "sType":"string", - "sWidth":"150px", - "mData":function(source, type/*, val*/) { - return ""+source._id+""; - } - }, - { - "sTitle": "Jobs", - "sClass": "col-jobs", - "sType":"numeric", - "sWidth":"120px", - "mData":function(source, type/*, val*/) { - var cnt = (source.jobs || 0); - if (type == "display") { - return ""+cnt+"" - + "
" - + ''; - } else { - return cnt; - } - } - }, - { - "sTitle": "Speed", - "sClass": "col-eta", - "sType":"numeric", - "mData":function(source, type, val) { - console.log() - return (Math.round(self.getCounterSpeed("index.status."+source._id) * 100) / 100) + " jobs/second"; - } - }, - { - "sTitle": "ETA", - "sClass": "col-eta", - "sType":"numeric", - "mData":function(source, type, val) { - return self.getCounterEta("index.status."+source._id, source.jobs || 0); - } - } - - ], - "fnDrawCallback": function (oSettings) { - - _.each(oSettings.aoData,function(row) { - var oData = row._aData; - - $(".col-jobs .inlinesparkline", row.nTr).sparkline("html", {"width": "100px", "height": "30px", "defaultPixelsPerValue": 1}); - - }); +define(["jquery", "underscore", "views/generic/datatablepage", "models", "moment"], function ($, _, DataTablePage, Models, moment) { + + return DataTablePage.extend({ + + el: '.js-page-status', + + template: "#tpl-page-status", + + events: { + }, + + renderDatatable: function () { + + var self = this; + + var datatableConfig = self.getCommonDatatableConfig("status"); + + _.extend(datatableConfig, { + "aoColumns": [ + + { + "sTitle": "Status", + "sClass": "col-status sorted-by-_id", + "sType": "string", + "sWidth": "150px", + "mData": function (source, type/*, val*/) { + return "" + source._id + ""; + } + }, + { + "sTitle": "Jobs", + "sClass": "col-jobs sorted-by-", + "sType": "numeric", + "sWidth": "120px", + "mData": function (source, type/*, val*/) { + var cnt = (source.jobs || 0); + if (type == "display") { + return "" + cnt + "" + + "
" + + ''; + } else { + return cnt; + } + } + }, + { + "sTitle": "Speed", + "sClass": "col-eta", + "sType": "numeric", + "mData": function (source, type, val) { + return (Math.round(self.getCounterSpeed("index.status." + source._id) * 100) / 100) + " jobs/second"; + } + }, + { + "sTitle": "ETA", + "sClass": "col-eta", + "sType": "numeric", + "mData": function (source, type, val) { + return self.getCounterEta("index.status." + source._id, source.jobs || 0); + } + } + + ], + "fnDrawCallback": function (oSettings) { + + _.each(oSettings.aoData, function (row) { + var oData = row._aData; + + $(".col-jobs .inlinesparkline", row.nTr).sparkline("html", { + "width": "100px", + "height": "30px", + "defaultPixelsPerValue": 1 + }); + + }); + }, + "aaSorting": [[0, 'asc']], + }); + + this.initDataTable(datatableConfig); + + }, + + getFilterData: function () { + return { + sEcho: 1 + }; }, - "aaSorting":[ [0,'asc'] ], - }); - this.initDataTable(datatableConfig); + updateTableData: function () { + this.setTableData('/api/datatables/status', this.getFilterData()); + } + }); - } - }); }); diff --git a/mrq/dashboard/static/js/views/taskexceptions.js b/mrq/dashboard/static/js/views/taskexceptions.js index 77fbfe4b..a4dd2062 100644 --- a/mrq/dashboard/static/js/views/taskexceptions.js +++ b/mrq/dashboard/static/js/views/taskexceptions.js @@ -1,89 +1,134 @@ -define(["jquery", "underscore", "views/generic/datatablepage", "models"],function($, _, DataTablePage, Models) { +define(["jquery", "underscore", "views/generic/datatablepage", "models"], function ($, _, DataTablePage, Models) { - return DataTablePage.extend({ + return DataTablePage.extend({ - el: '.js-page-taskexceptions', + el: '.js-page-taskexceptions', - template:"#tpl-page-taskexceptions", + template: "#tpl-page-taskexceptions", - events:{ - }, - - renderDatatable:function() { + events: { + "click .js-datatable-filters-submit": "filterschanged" + }, - var self = this; + initFilters: function () { + this.filters = { + "name": this.options.params.name || "", + "exception": this.options.params.exception || "" + }; + }, - var datatableConfig = self.getCommonDatatableConfig("taskexceptions"); + setOptions: function (options) { + this.options = options; + this.initFilters(); + this.flush(); + }, - _.extend(datatableConfig, { - "aoColumns": [ + renderDatatable: function () { + + var self = this; + + var datatableConfig = self.getCommonDatatableConfig("taskexceptions"); + + _.extend(datatableConfig, { + "aoColumns": [ + + { + "sTitle": "Name", + "sClass": "col-name", + "sType": "string", + "mData": function (source, type, val) { + // console.log(source) + return "" + source._id.path + ""; + } + }, + { + "sTitle": "Exception", + "sClass": "col-exception", + "sType": "numeric", + "mData": function (source, type, val) { + return "" + source._id.exceptiontype + "" + } + }, + { + "sTitle": "Jobs", + "sClass": "col-jobs", + "sType": "numeric", + "mData": function (source, type, val) { + var cnt = source.jobs || 0; + + if (type == "display") { + return "" + cnt + "" + + "
" + + ''; + } else { + return cnt; + } + } + }, + { + "sTitle": "Speed", + "sClass": "col-eta", + "sType": "numeric", + "mData": function (source, type, val) { + return (Math.round(self.getCounterSpeed("taskexceptions." + source._id.path + " " + source._id.exceptiontype) * 100) / 100) + " jobs/second"; + } + }, + { + "sTitle": "ETA", + "sClass": "col-eta", + "sType": "numeric", + "mData": function (source, type, val) { + return self.getCounterEta("taskexceptions." + source._id.path + " " + source._id.exceptiontype, source.jobs || 0); + } + } + + ], + "fnDrawCallback": function (oSettings) { + + _.each(oSettings.aoData, function (row) { + var oData = row._aData; + + $(".col-jobs .inlinesparkline", row.nTr).sparkline("html", { + "width": "100px", + "height": "30px", + "defaultPixelsPerValue": 1 + }); + + }); + }, + "aaSorting": [[0, 'asc']], + }); + + this.initDataTable(datatableConfig); - { - "sTitle": "Name", - "sClass": "col-name", - "sType": "string", - "mData":function(source, type, val) { - // console.log(source) - return ""+source._id.path+""; - } - }, - { - "sTitle": "Exception", - "sClass": "col-exception", - "sType":"numeric", - "mData":function(source, type, val) { - return ""+source._id.exceptiontype+"" - } - }, - { - "sTitle": "Jobs", - "sClass": "col-jobs", - "sType":"numeric", - "mData":function(source, type, val) { - var cnt = source.jobs || 0; - - if (type == "display") { - return ""+cnt+"" - + "
" - + ''; - } else { - return cnt; - } - } - }, - { - "sTitle": "Speed", - "sClass": "col-eta", - "sType":"numeric", - "mData":function(source, type, val) { - return (Math.round(self.getCounterSpeed("taskexceptions."+source._id.path+" "+source._id.exceptiontype) * 100) / 100) + " jobs/second"; - } - }, - { - "sTitle": "ETA", - "sClass": "col-eta", - "sType":"numeric", - "mData":function(source, type, val) { - return self.getCounterEta("taskexceptions."+source._id.path+" "+source._id.exceptiontype, source.jobs || 0); - } - } - - ], - "fnDrawCallback": function (oSettings) { + }, - _.each(oSettings.aoData,function(row) { - var oData = row._aData; + filterschanged: function (evt) { + var self = this; - $(".col-jobs .inlinesparkline", row.nTr).sparkline("html", {"width": "100px", "height": "30px", "defaultPixelsPerValue": 1}); + if (evt) { + evt.preventDefault(); + evt.stopPropagation(); + } - }); + _.each(self.filters, function (v, k) { + self.filters[k] = self.$(".js-datatable-filters-" + k).val(); + }); + this.updateTableData(); + window.location = "/#taskexceptions?" + $.param(self.filters, true).replace(/\+/g, "%20"); }, - "aaSorting":[ [0,'asc'] ], - }); - this.initDataTable(datatableConfig); + getFilterData: function () { + return { + sEcho: 1, + name: $('#taskexceptions-form-name').val(), + exception: $('#taskexceptions-form-exception').val() + }; + }, - } - }); + updateTableData: function () { + this.setTableData('/api/datatables/taskexceptions', this.getFilterData()); + } + }); }); diff --git a/mrq/dashboard/static/js/views/taskpaths.js b/mrq/dashboard/static/js/views/taskpaths.js index 5e6a8890..2001412f 100644 --- a/mrq/dashboard/static/js/views/taskpaths.js +++ b/mrq/dashboard/static/js/views/taskpaths.js @@ -1,80 +1,123 @@ -define(["jquery", "underscore", "views/generic/datatablepage", "models"],function($, _, DataTablePage, Models) { +define(["jquery", "underscore", "views/generic/datatablepage", "models"], function ($, _, DataTablePage, Models) { - return DataTablePage.extend({ + return DataTablePage.extend({ - el: '.js-page-taskpaths', + el: '.js-page-taskpaths', - template:"#tpl-page-taskpaths", + template: "#tpl-page-taskpaths", - events:{ - }, - - renderDatatable:function() { - - var self = this; + events: { + "click .js-datatable-filters-submit": "filterschanged" + }, - var datatableConfig = self.getCommonDatatableConfig("taskpaths"); + initFilters: function () { + this.filters = { + "name": this.options.params.name || "", + }; + }, - _.extend(datatableConfig, { - "aoColumns": [ + setOptions: function (options) { + this.options = options; + this.initFilters(); + this.flush(); + }, - { - "sTitle": "Name", - "sClass": "col-name", - "sType": "string", - "mData":function(source, type, val) { - return ""+source._id+""; - } - }, - { - "sTitle": "Jobs", - "sClass": "col-jobs", - "sType":"numeric", - "mData":function(source, type, val) { - var cnt = source.jobs || 0; - - if (type == "display") { - return ""+cnt+"" - + "
" - + ''; - } else { - return cnt; - } - } - }, - { - "sTitle": "Speed", - "sClass": "col-eta", - "sType":"numeric", - "mData":function(source, type, val) { - return (Math.round(self.getCounterSpeed("taskpath."+source._id) * 100) / 100) + " jobs/second"; - } - }, - { - "sTitle": "ETA", - "sClass": "col-eta", - "sType":"numeric", - "mData":function(source, type, val) { - return self.getCounterEta("taskpath."+source._id, source.jobs || 0); - } - } + renderDatatable: function () { + + var self = this; + + var datatableConfig = self.getCommonDatatableConfig("taskpaths"); + + _.extend(datatableConfig, { + "aoColumns": [ + + { + "sTitle": "Name", + "sClass": "col-name", + "sType": "string", + "mData": function (source, type, val) { + return "" + source._id + ""; + } + }, + { + "sTitle": "Jobs", + "sClass": "col-jobs", + "sType": "numeric", + "mData": function (source, type, val) { + var cnt = source.jobs || 0; + + if (type == "display") { + return "" + cnt + "" + + "
" + + ''; + } else { + return cnt; + } + } + }, + { + "sTitle": "Speed", + "sClass": "col-eta", + "sType": "numeric", + "mData": function (source, type, val) { + return (Math.round(self.getCounterSpeed("taskpath." + source._id) * 100) / 100) + " jobs/second"; + } + }, + { + "sTitle": "ETA", + "sClass": "col-eta", + "sType": "numeric", + "mData": function (source, type, val) { + return self.getCounterEta("taskpath." + source._id, source.jobs || 0); + } + } + + ], + "fnDrawCallback": function (oSettings) { + + _.each(oSettings.aoData, function (row) { + var oData = row._aData; + + $(".col-jobs .inlinesparkline", row.nTr).sparkline("html", { + "width": "100px", + "height": "30px", + "defaultPixelsPerValue": 1 + }); + + }); + }, + "aaSorting": [[0, 'asc']], + }); + + this.initDataTable(datatableConfig); - ], - "fnDrawCallback": function (oSettings) { + }, - _.each(oSettings.aoData,function(row) { - var oData = row._aData; + filterschanged: function (evt) { + var self = this; - $(".col-jobs .inlinesparkline", row.nTr).sparkline("html", {"width": "100px", "height": "30px", "defaultPixelsPerValue": 1}); + if (evt) { + evt.preventDefault(); + evt.stopPropagation(); + } - }); + _.each(self.filters, function (v, k) { + self.filters[k] = self.$(".js-datatable-filters-" + k).val(); + }); + this.updateTableData(); + window.location = "/#taskpath?" + $.param(self.filters, true).replace(/\+/g, "%20"); }, - "aaSorting":[ [0,'asc'] ], - }); - this.initDataTable(datatableConfig); + getFilterData: function () { + return { + sEcho: 1, + name: $('#taskpath-form-name').val() + }; + }, - } - }); + updateTableData: function () { + this.setTableData('/api/datatables/taskpaths', this.getFilterData()); + } + }); }); diff --git a/mrq/dashboard/static/js/views/workers.js b/mrq/dashboard/static/js/views/workers.js index fa1f6d1d..4a247fa6 100644 --- a/mrq/dashboard/static/js/views/workers.js +++ b/mrq/dashboard/static/js/views/workers.js @@ -1,189 +1,444 @@ -define(["jquery", "underscore", "views/generic/datatablepage", "models", "moment"],function($, _, DataTablePage, Models, moment) { +define(["jquery", "underscore", "views/generic/datatablepage", "models", "moment"], function ($, _, DataTablePage, Models, moment) { - return DataTablePage.extend({ + return DataTablePage.extend({ - el: '.js-page-workers', + el: '.js-page-workers', - template:"#tpl-page-workers", + template: "#tpl-page-workers", - events:{ - "change .js-datatable-filters-showstopped": "filterschanged", - "click .js-workers-io": "showworkerio", - }, + events: { + "change .js-datatable-filters-showstopped": "filterschanged", + "click .js-workers-io": "showworkerio", + "click .hide-time-filter": "hidetimefilter", + "click .show-time-filter": "showtimefilter", + }, - initFilters: function() { + initFilters: function () { + this.filters = { + "showstopped": this.options.params.showstopped || "", + "startTime": this.options.params.startTime || "", + "endTime": this.options.params.endTime || "" + }; + this.initTimeFilter(); + }, - this.filters = { - "showstopped": this.options.params.showstopped||"" - }; + initTimeFilter: function () { + var self = this; + $('.time-filter-group').click(function () { + self.timeFilter.typeChanged(this); + }); + $('.time-filter-tag').click(function () { + self.filterRequest(this); + }); + }, - }, + hidetimefilter: function () { + $(".time-filter-container").css({"position": "relative"}); + $(".time-filter-container").animate({ + bottom: "+265", + }, { + duration: 300, + complete: function () { + $('.hide-time-filter').hide(); + $('.show-time-filter').show(); + } + }); + }, - setOptions:function(options) { - this.options = options; - this.initFilters(); - this.flush(); - }, + showtimefilter: function () { + $(".time-filter-container").css({"position": "relative"}); + $(".time-filter-container").animate({ + bottom: "0", + }, { + duration: 300, + complete: function () { + $('.hide-time-filter').show(); + $('.show-time-filter').hide(); + $(".time-filter-container").css({"position": "static"}); + } + }); + }, - showworkerio: function(evt) { - var self = this; + setOptions: function (options) { + this.options = options; + this.initFilters(); + this.flush(); + }, - var worker_id = $(evt.currentTarget).data("workerid"); + showworkerio: function (evt) { + var self = this; - var worker_data = _.find(this.dataTableRawData.aaData, function(worker) { - return worker._id == worker_id; - }); + var worker_id = $(evt.currentTarget).data("workerid"); - var html_modal = _.template($("#tpl-modal-workers-io").html())({"worker": worker_data}); + var worker_data = _.find(this.dataTableRawData.aaData, function (worker) { + return worker._id == worker_id; + }); - self.$(".js-workers-modal .js-workers-modal-content").html(html_modal); - self.$(".js-workers-modal h4").html("I/O for this worker, by task & by type"); - self.$(".js-workers-modal").modal({}); + var html_modal = _.template($("#tpl-modal-workers-io").html())({"worker": worker_data}); - return false; - }, + self.$(".js-workers-modal .js-workers-modal-content").html(html_modal); + self.$(".js-workers-modal h4").html("I/O for this worker, by task & by type"); + self.$(".js-workers-modal").modal({}); - renderDatatable:function() { + return false; + }, - var self = this; + renderDatatable: function () { - var datatableConfig = self.getCommonDatatableConfig("workers"); + var self = this; + this.initFilters(); - _.extend(datatableConfig, { - "aoColumns": [ + var datatableConfig = self.getCommonDatatableConfig("workers"); - { - "sTitle": "Name", - "sClass": "col-name", - "sType":"string", - "sWidth":"150px", - "mData":function(source, type/*, val*/) { - return ""+source.name+"
"+source.config.local_ip + " " + source._id+""; - } - }, - { - "sTitle": "Queues", - "sClass": "col-queues", - "sType":"string", - "mData":function(source, type/*, val*/) { - return _.map(source.config.queues||[], function(q) { - return ""+q+""; - }).join(" "); - } - }, - { - "sTitle": "Status", - "sClass": "col-status", - "sType":"string", - "sWidth":"80px", - "mData":function(source, type/*, val*/) { - return source.status; - } - }, - { - "sTitle": "Last report", - "sClass": "col-last-report", - "sType":"string", - "sWidth":"150px", - "mData":function(source, type/*, val*/) { - if (type == "display") { - - return "" + (source.datereported?moment.utc(source.datereported).fromNow():"Never") - + "
" - + "started " + moment.utc(source.datestarted).fromNow() + "
"; - } else { - return source.datereported || ""; - } - } - }, - { - "sTitle": "CPU usr/sys", - "sClass": "col-cpu", - "sType":"string", - "sWidth":"120px", - "mData":function(source, type/*, val*/) { + _.extend(datatableConfig, { + "aoColumns": [ - var usage = (source.process.cpu.user + source.process.cpu.system) * 1000 / (moment.utc(source.datereported || null).valueOf() - moment.utc(source.datestarted).valueOf()); + { + "sTitle": "Name", + "sClass": "col-name", + "sType": "string", + "sWidth": "150px", + "mData": function (source, type/*, val*/) { + return "" + source.name + "
" + source.config.local_ip + " " + source._id + ""; + } + }, + { + "sTitle": "Queues", + "sClass": "col-queues", + "sType": "string", + "mData": function (source, type/*, val*/) { + return _.map(source.config.queues || [], function (q) { + return "" + q + ""; + }).join(" "); + } + }, + { + "sTitle": "Status", + "sClass": "col-status", + "sType": "string", + "sWidth": "80px", + "mData": function (source, type/*, val*/) { + return source.status; + } + }, + { + "sTitle": "Last report", + "sClass": "col-last-report", + "sType": "string", + "sWidth": "150px", + "mData": function (source, type/*, val*/) { + if (type == "display") { + console.log(source); + return "" + (source.datereported ? moment.utc(source.datereported).fromNow() : "Never") + + "
" + + "started " + moment.utc(source.datestarted).fromNow() + "
"; + } else { + return source.datereported || ""; + } + } + }, + { + "sTitle": "CPU usr/sys", + "sClass": "col-cpu", + "sType": "string", + "sWidth": "120px", + "mData": function (source, type/*, val*/) { - var html = Math.round(source.process.cpu.user) + "s / " + Math.round(source.process.cpu.system) + "s" - + "
" - + (Math.round(usage * 100)) + "% use"; + var usage = (source.process.cpu.user + source.process.cpu.system) * 1000 / (moment.utc(source.datereported || null).valueOf() - moment.utc(source.datestarted).valueOf()); - if (((source.io || {}).types || []).length) { - html += "
I/O: "+Math.round(source.io.total)+"s "; - } + var html = Math.round(source.process.cpu.user) + "s / " + Math.round(source.process.cpu.system) + "s" + + "
" + + (Math.round(usage * 100)) + "% use"; - return html; - } - }, - { - "sTitle": "Memory", - "sClass": "col-mem", - "sType":"numeric", - "sWidth":"130px", - "mData":function(source, type/*, val*/) { - if (type == "display") { - - return Math.round((source.process.mem.total / (1024*1024)) *10)/10 + "M" - + "
" - + ''; - } else { - return source.process.mem.total - } - } - }, - { - "sTitle": "Done Jobs", - "sClass": "col-done-jobs", - "sType":"numeric", - "sWidth":"120px", - "mData":function(source, type/*, val*/) { - var cnt = (source.done_jobs || 0); - if (type == "display") { - return ""+cnt+"" - + "
" - + ''; - } else { - return cnt; - } - } - }, - { - "sTitle": "Speed", - "sClass": "col-eta", - "sType":"numeric", - "sWidth":"120px", - "mData":function(source, type, val) { - return (Math.round(self.getCounterSpeed("worker.donejobs."+source._id) * 100) / 100) + " j/s"; - } - }, - { - "sTitle": "Current Jobs", - "sClass": "col-current-jobs", - "sType":"numeric", - "sWidth":"120px", - "mData":function(source, type/*, val*/) { - var cnt = (source.jobs || []).length; - if (type == "display") { - return ""+cnt+" / "+source.config.greenlets - + "
" - + ''; - } else { - return cnt; - } - } - } + if (((source.io || {}).types || []).length) { + html += "
I/O: " + Math.round(source.io.total) + "s "; + } + + return html; + } + }, + { + "sTitle": "Memory", + "sClass": "col-mem", + "sType": "numeric", + "sWidth": "130px", + "mData": function (source, type/*, val*/) { + if (type == "display") { + + return Math.round((source.process.mem.total / (1024 * 1024)) * 10) / 10 + "M" + + "
" + + ''; + } else { + return source.process.mem.total + } + } + }, + { + "sTitle": "Done Jobs", + "sClass": "col-done-jobs", + "sType": "numeric", + "sWidth": "120px", + "mData": function (source, type/*, val*/) { + var cnt = (source.done_jobs || 0); + if (type == "display") { + return "" + cnt + "" + + "
" + + ''; + } else { + return cnt; + } + } + }, + { + "sTitle": "Speed", + "sClass": "col-eta", + "sType": "numeric", + "sWidth": "120px", + "mData": function (source, type, val) { + return (Math.round(self.getCounterSpeed("worker.donejobs." + source._id) * 100) / 100) + " j/s"; + } + }, + { + "sTitle": "Current Jobs", + "sClass": "col-current-jobs", + "sType": "numeric", + "sWidth": "120px", + "mData": function (source, type/*, val*/) { + var cnt = (source.jobs || []).length; + if (type == "display") { + return "" + cnt + " / " + source.config.greenlets + + "
" + + ''; + } else { + return cnt; + } + } + } + + ], + "fnDrawCallback": function (oSettings) { + $(".inlinesparkline", oSettings.nTable).sparkline("html", { + "width": "100px", + "height": "30px", + "defaultPixelsPerValue": 1 + }); + }, + "aaSorting": [[0, 'asc']], + }); + + this.initDataTable(datatableConfig); + + }, + + getFilterData: function () { + return { + id: $('#jobs-form-id').val(), + queue: $('#jobs-form-queue').val(), + worker: $('#jobs-form-worker').val(), + path: $('#jobs-form-path').val(), + status: $('#jobs-form-status').val(), + params: $('#jobs-form-params').val(), + exceptiontype: $('#jobs-form-exceptiontype').val(), + startTime: this.filters.startTime, + endTime: this.filters.endTime, + sEcho: 1 + }; + }, - ], - "fnDrawCallback": function (oSettings) { - $(".inlinesparkline", oSettings.nTable).sparkline("html", {"width": "100px", "height": "30px", "defaultPixelsPerValue": 1}); + timefiltergroupchanged: function () { + this.timeFilter.typeChanged(); }, - "aaSorting":[ [0,'asc'] ], - }); - this.initDataTable(datatableConfig); + filterRequest: function (component) { + var result = this.timeFilter.filterRequest(component); + this.filters.startTime = result[0]; + this.filters.endTime = result[1]; + this.updateTableData(); + }, + + updateTableData: function () { + this.setTableData('/api/datatables/workers', this.getFilterData()); + }, + + timeFilter: { + show: function () { + + }, + hide: function () { + + }, + typeChanged: function (component) { + $('.time-filter-group').removeClass('active'); + $(component).addClass('active'); + + }, + filterRequest: function (component) { + var criteria = $(component).attr('data-filter'); + var dateStart = new Date(); + var dateEnd = new Date(); + if (criteria != '') { + if (criteria.contains('last')) { + if (criteria.contains('min')) { + var mins = parseInt(criteria.split('_')[1]); + dateStart.setMinutes(dateStart.getMinutes() - mins); + } + if (criteria.contains('days')) { + var days = parseInt(criteria.split('_')[1]); + dateStart.setDate(dateStart.getDate() - days); + } + if (criteria.contains('hours')) { + var hours = parseInt(criteria.split('_')[1]); + dateStart.setHours(dateStart.getHours() - hours); + } + } + if (criteria == 'today') { + dateStart = this.getBeginingOfDay(dateStart); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == 'yesterday') { + dateStart.setDate(dateStart.getDate() - 1); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() - 1); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == '2_days_ago') { + dateStart.setDate(dateStart.getDate() - 2); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() - 2); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == '7_days_ago') { + dateStart.setDate(dateStart.getDate() - 7); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() - 7); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == 'this_week') { + dateStart.setDate(dateStart.getDate() + + (0 - dateStart.getDay())); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() + (6 - dateEnd.getDay())); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == 'week_until_now') { + dateStart.setDate(dateStart.getDate() + + (0 - dateStart.getDay())); + dateStart = this.getBeginingOfDay(dateStart); + } + if (criteria == 'prev_week') { + dateStart.setDate(dateStart.getDate() - 7); + dateStart.setDate(dateStart.getDate() + + (0 - dateStart.getDay())); + dateStart = this.getBeginingOfDay(dateStart); + + dateEnd.setDate(dateEnd.getDate() - 7); + dateEnd.setDate(dateEnd.getDate() + (6 - dateEnd.getDay())); + dateEnd = this.getEndOfDay(dateEnd); + } + if (criteria == 'this_month') { + dateStart = this.getBeginingOfMonth(dateStart); + + dateEnd = this.getEndOfMonth(dateEnd); + } + if (criteria == 'month_until_now') { + dateStart = this.getBeginingOfMonth(dateStart); + } + if (criteria == 'last_6_months') { + dateStart.setMonth(dateStart.getMonth() - 6); + dateStart = this.getBeginingOfMonth(dateStart); + + dateEnd = this.getEndOfMonth(dateEnd); + } + if (criteria == 'prev_month') { + dateStart.setMonth(dateStart.getMonth() - 1); + dateStart = this.getBeginingOfMonth(dateStart); + + dateEnd.setMonth(dateEnd.getMonth() - 1); + dateEnd = this.getEndOfMonth(dateEnd); + } + if (criteria == 'prev_year') { + dateStart.setFullYear(dateStart.getFullYear() - 1); + dateStart = this.getBeginingOfYear(dateStart); + + dateEnd.setFullYear(dateEnd.getFullYear() - 1); + dateEnd = this.getEndOfYear(dateEnd); + } + if (criteria == 'this_year') { + dateStart = this.getBeginingOfYear(dateStart); + + dateEnd = this.getEndOfYear(dateEnd); + } + if (criteria == 'last_year') { + dateStart.setFullYear(dateStart.getFullYear() - 1); + dateStart = this.getBeginingOfYear(dateStart); + } + if (criteria == 'year_until_now') { + dateStart = this.getBeginingOfYear(dateStart); + } + if (criteria == 'last_2_years') { + dateStart.setFullYear(dateStart.getFullYear() - 2); + dateStart = this.getBeginingOfYear(dateStart); + } + if (criteria == 'last_5_years') { + dateStart.setFullYear(dateStart.getFullYear() - 5); + dateStart = this.getBeginingOfYear(dateStart); + } + if (criteria == 'today_until_now') { + dateStart = this.getBeginingOfDay(dateStart); + } + console.log(dateStart); + console.log(dateEnd); + return [this.getISODate(dateStart), this.getISODate(dateEnd)]; + } + }, + getBeginingOfDay: function (date) { + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); + date.setMilliseconds(0); + return date; + }, + getEndOfDay: function (date) { + date.setHours(23); + date.setMinutes(59); + date.setSeconds(59); + date.setMilliseconds(999); + return date; + }, + getBeginingOfMonth: function (date) { + date.setDate(1); + date = this.getBeginingOfDay(date); + return date; + }, + getEndOfMonth: function (date) { + date.setMonth(date.getMonth() + 1); + date = this.getBeginingOfMonth(date); + date.setMilliseconds(-1); + return date; + }, + getBeginingOfYear: function (date) { + date.setMonth(0); + date = this.getBeginingOfMonth(date); + return date; + }, + getEndOfYear: function (date) { + date.setFullYear(date.getFullYear() + 1); + date = this.getBeginingOfYear(date); + date.setMilliseconds(-1); + return date; + }, + getISODate: function (date) { + return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + + date.getDate() + "T" + date.getHours() + ":" + + date.getMinutes() + ":" + date.getSeconds() + "." + + date.getMilliseconds(); + } - } - }); + } + }); }); diff --git a/mrq/dashboard/templates/index.html b/mrq/dashboard/templates/index.html index dec6b9d9..4f54c4ef 100644 --- a/mrq/dashboard/templates/index.html +++ b/mrq/dashboard/templates/index.html @@ -1,6 +1,6 @@ - + MRQ Dashboard @@ -8,100 +8,105 @@ - + - - + + -