OwlCyberSecurity - MANAGER
Edit File: work.xhtml
<!DOCTYPE html> <html xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1" t:render="main" > <head> <title><t:slot name="title" /></title> <link t:render="stylesheet" /> <style> #work_queue_chart { display: block; margin-left: auto; margin-right: auto; } .bar { fill: steelblue; } .bar:hover { fill: brown; } .axis text { font: 10px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .x.axis path { display: none; } .y.axis text:hover { fill: brown; } #work_item_details { display: none; } #debug { display: none; } </style> <script src="https://d3js.org/d3.v3.min.js"></script> <script> //<![CDATA[ var debug = false; var workTypeDescriptions = { // Scheduling "SCHEDULE_ORGANIZER_WORK": "Schedule Organizer", "SCHEDULE_REPLY_WORK": "Schedule Reply", "SCHEDULE_REPLY_CANCEL_WORK": "Schedule Reply (Cancel)", "SCHEDULE_REFRESH_WORK": "Schedule Refresh", "SCHEDULE_AUTO_REPLY_WORK": "Schedule Auto-reply", // iMIP scheduling "IMIP_POLLING_WORK": "iMIP Poll", "IMIP_INVITATION_WORK": "iMIP Invitation", "IMIP_REPLY_WORK": "iMIP Reply", // Group cacher "GROUP_CACHER_POLLING_WORK": "Group Cache Poll", "GROUP_REFRESH_WORK": "Group Refresh", "GROUP_ATTENDEE_RECONCILIATION_WORK": "Group Attendee Reconciliation", // Push notifications "PUSH_NOTIFICATION_WORK": "Push Notification", // Event splitting "CALENDAR_OBJECT_SPLITTER_WORK": "Event Split", // Inbox cleanup "INBOX_CLEANUP_WORK": "Inbox Cleanup", "CLEANUP_ONE_INBOX_WORK": "Inbox Cleanup: One", // Revision cleanup "REVISION_CLEANUP_WORK": "Revision Cleanup", "FIND_MIN_VALID_REVISION_WORK": "Revision Cleanup: Find Minimum", }; var jobAttributeDescriptions = { // Job management "job_jobID": "Job ID", "job_priority": "Job Priority", "job_weight": "Job Weight", "job_notBefore": "Not Before", // Work item management "work_workID": "Work ID", "work_notBefore": "Not Before (W)", "work_group": "Work Group", // Push "work_pushID": "Push ID", "work_priority": "Priority", // Scheduling "work_icalendarUid": "iCalendar UID", "work_attendeeCount": "Attendee Count", "work_organizer": "Organizer", "work_attendee": "Attendee", } // Function to return keys from an object in order, followed by keys // from another which were not in the first. // We use this in case we get a JSON dictionary with keys that haven't // been mapped; the mapped keys will be in a known order and the rest // will be in whatever order they came in over the wire. function keysInKindOfKnownOrder(obj1, obj2) { var keys = []; for (var key in obj1) { if (key in obj2) { keys.push(key); } } for (var key in obj2) { if (! key in obj1) { keys.push(key); } } return keys; } function valueOrKey(obj, key) { if (key in obj) { return obj[key]; } else { return key; } } var maxSeen = 30; function drawChart(data) { var items = []; var keys = keysInKindOfKnownOrder(workTypeDescriptions, data); for (var i in keys) { var key = keys[i]; // OMG JavaScript, seriously? if (key in data) { items.push({ name: key, count: data[key], description: valueOrKey(workTypeDescriptions, key), }); } } var outerWidth = 960; var outerHeight = 500; var margin = { top: 20, right: 30, bottom: 30, left: 160 }; var innerWidth = outerWidth - margin.left - margin.right; var innerHeight = outerHeight - margin.top - margin.bottom; var xInset = 2; var max = d3.max(items, function(i) { return i.count; }); if (max > maxSeen) { maxSeen = max; } var xLocation = d3.scale.log() .domain([1, maxSeen]) .range([xInset, innerWidth]) ; function xLocationWithZero(value) { if (value < 1) { return 0; } else { return xLocation(value); } } var yLocation = d3.scale.ordinal() .domain(items.map(function(i) { return i.description; })) .rangeRoundBands([0, innerHeight], 0.1) ; var xAxis = d3.svg.axis() .scale(xLocation) .orient("bottom") .ticks(20, ",.0f") ; var yAxis = d3.svg.axis() .scale(yLocation) .orient("left") ; // Select chart var chart = d3.select("#work_queue_chart") .attr("width", outerWidth) .attr("height", outerHeight) ; // Select inner var inner = chart.selectAll(".inner") .data([0]) ; // Enter inner selection inner.enter().append("g"); // Update inner selection inner .attr("class", "inner") .attr("transform", "translate(" + margin.left + "," + margin.top + ")") ; // Select, enter, update, exit x-axis var x_axis = inner.selectAll(".x.axis").data([0]); x_axis.enter().append("g"); x_axis .attr("class", "x axis") .attr("transform", "translate(" + xInset + "," + innerHeight + ")") .call(xAxis) ; x_axis.exit().remove(); // Select, enter, update, exit y-axis var y_axis = inner.selectAll(".y.axis").data([0]); y_axis.enter().append("g"); y_axis .attr("class", "y axis") .call(yAxis) ; // y_axis // .selectAll(".tick") // .on("click", function(x) { alert(x); } ) // ; y_axis.exit().remove(); // Select bars var bars = inner.selectAll(".bar") .data(items) ; // Enter bars selection bars.enter().append("rect"); // Update bars selection bars .attr("class", "bar") .attr("width", function(i) { return xLocationWithZero(i.count); }) .attr("height", yLocation.rangeBand()) .attr( "transform", function(i) { return "translate(" + xInset + "," + yLocation(i.description) + ")"; } ) .attr("onclick", function(i) { return 'showJobDetails("' + i.name + '");'; }) ; // Exit bars selection bars.exit().remove(); // Select labels var labels = inner.selectAll(".label") .data(items) ; // Enter labels selection labels.enter().append("text"); // Update labels selection labels .attr("class", "label") .attr("x", function(i) { return xLocationWithZero(i.count) + 4; }) .attr("y", function(i) { return yLocation(i.description) + (yLocation.rangeBand() / 2); }) .attr("dy", "0.32em") .text( function(i) { if (i.count < 1) { return ""; } return i.count; } ) ; // Exit labels selection labels.exit().remove(); // Exit inner selection inner.exit().remove(); } function initChart() { var data = {}; for (var key in workTypeDescriptions) { data[key] = 0.1; } drawChart(data); } var eventSource; function registerForEvents() { eventSource = new EventSource("/webadmin/work/events"); eventSource.addEventListener( "work-total", function(e) { // container = document.getElementById("event_debug"); // container.innerHTML = e.data; drawChart(JSON.parse(e.data)); }, false ); } var itemTypeEventListener; var itemTypeEventListenerWorkType; function showJobDetails(workType) { var debuggingContainer = document.getElementById("debug"); if (debug) { debuggingContainer.style.display = "block"; var detailsDebug = document.getElementById("work_item_debug"); detailsDebug.innerHTML = workType; var eventDebug = document.getElementById("event_debug"); eventDebug.innerHTML = ""; } else { debuggingContainer.style.display = "none"; } // Get description if (workType in workTypeDescriptions) { var description = workTypeDescriptions[workType]; } else { var description = workType; } // Look up elements var detailsTable = document.getElementById("work_item_details"); var detailsCaption = document.getElementById("work_item_details_caption"); var detailsHeader = document.getElementById("work_item_details_header"); var detailsBody = document.getElementById("work_item_details_body"); // Unregister existing details listener if (typeof itemTypeEventListener !== "undefined") { eventSource.removeEventListener(itemTypeEventListenerWorkType, itemTypeEventListener) } // Reset the details elements detailsTable.style.display = "none"; detailsCaption.innerHTML = "Work Item Details: " + description; detailsHeader.innerHTML = ""; detailsBody.innerHTML = ""; // Register requested details listener itemTypeEventListenerWorkType = workType; itemTypeEventListener = function(e) { if (debug) { eventDebug.innerHTML = e.data; } var jobItems = JSON.parse(e.data); drawJobDetails(jobItems, detailsTable, detailsCaption, detailsHeader, detailsBody); } eventSource.addEventListener(workType, itemTypeEventListener) } function drawJobDetails(jobItems, detailsTable, detailsCaption, detailsHeader, detailsBody) { // Reset the rows; we're going to refill the table below detailsBody.innerHTML = ""; for (var i in jobItems) { var jobItem = jobItems[i]; // OMG JavaScript is so stupid. // Unhide the table detailsTable.style.display = "block"; var attributes = keysInKindOfKnownOrder(jobAttributeDescriptions, jobItem); // Add the table headers if they aren't already there if (detailsHeader.innerHTML == "") { var row = document.createElement("tr"); for (var i in attributes) { var attribute = attributes[i]; // OMG JavaScript, wow you so dumb attributeDescription = valueOrKey(jobAttributeDescriptions, attribute) addCellToRow(row, "th", attributeDescription) } detailsHeader.appendChild(row); } // Add a table row for this job item var row = document.createElement("tr"); for (var i in attributes) { var attribute = attributes[i]; // OMG JavaScript the lameness var value = jobItem[attribute] if (value === null) { value = ""; } addCellToRow(row, "td", value) } detailsBody.appendChild(row); } } function addCellToRow(row, tag, text) { var cell = document.createElement(tag); var content = document.createTextNode(text); cell.appendChild(content); row.appendChild(cell); } window.onload = function() { initChart(); registerForEvents(); }; //]]> </script> </head> <body> <h1><t:slot name="title" /></h1> <svg id="work_queue_chart" /> <table id="work_item_details"> <caption id="work_item_details_caption">Work Item Details</caption> <thead id="work_item_details_header"> <tr> <th>Job ID</th> <th>Priority</th> <th>Weight</th> <th>Not Before</th> </tr> </thead> <tbody id="work_item_details_body" /> </table> <div id="debug"> <hr /> <div id="work_item_debug" /> <div id="event_debug" /> </div> </body> </html>