Commit 8f5081b2 authored by Leo Iannacone's avatar Leo Iannacone

Merge remote-tracking branch 'github/master' into portable

parents f8da1df2 50091ba0
...@@ -31,7 +31,7 @@ app.set("view engine", "ejs") ...@@ -31,7 +31,7 @@ app.set("view engine", "ejs")
# index page # index page
app.get("/", routes.index) app.get("/", routes.index)
# distibution page # distribution page
app.get(config.routes.distribution, routes.distribution) app.get(config.routes.distribution, routes.distribution)
# preferences page # preferences page
......
...@@ -143,6 +143,10 @@ class Client ...@@ -143,6 +143,10 @@ class Client
stats.get_all_packages (packages) => stats.get_all_packages (packages) =>
@socket.emit e.history, packages @socket.emit e.history, packages
@socket.on e.disk_usage, () =>
stats.get_disk_usage (result) =>
@socket.emit e.disk_usage, result
# on client disconnection close all watchers # on client disconnection close all watchers
@socket.on "disconnect", => @socket.on "disconnect", =>
socket_watchers = @socket.watchers socket_watchers = @socket.watchers
......
...@@ -90,6 +90,15 @@ config.debomatic.excluded_files = [ ...@@ -90,6 +90,15 @@ config.debomatic.excluded_files = [
"json" "json"
] ]
###
List of subdirectories in distributions to show in disk usage
###
config.debomatic.disk_usage_subdirs = [
"aptcache"
"pool"
"chroot"
]
### ###
The routes, that are the pages urls The routes, that are the pages urls
### ###
...@@ -118,6 +127,7 @@ config.events.client.file = "c.file" ...@@ -118,6 +127,7 @@ config.events.client.file = "c.file"
config.events.client.file_newcontent = "c.file_newcontent" config.events.client.file_newcontent = "c.file_newcontent"
config.events.client.status = "c.status" config.events.client.status = "c.status"
config.events.client.history = "c.history" config.events.client.history = "c.history"
config.events.client.disk_usage = "c.disk_usage"
### ###
The status according with JSONLogger.py module The status according with JSONLogger.py module
......
fs = require('fs') fs = require('fs')
glob = require('glob') glob = require('glob')
exec = require('child_process').exec
config = require('./config') config = require('./config')
utils = require('./utils') utils = require('./utils')
...@@ -13,7 +14,7 @@ get_all_packages = (cb) -> ...@@ -13,7 +14,7 @@ get_all_packages = (cb) ->
glob "#{config.debomatic.path}/*/pool/*/*.json", {}, (err, files) -> glob "#{config.debomatic.path}/*/pool/*/*.json", {}, (err, files) ->
if err? if err?
utils.errors_handler "history:get_all_packages", err utils.errors_handler "stats:get_all_packages", err
return return
packages = [] packages = []
for f in files for f in files
...@@ -30,4 +31,46 @@ get_all_packages = (cb) -> ...@@ -30,4 +31,46 @@ get_all_packages = (cb) ->
cb(packages) cb(packages)
get_disk_usage = (cb) ->
exec "du -BM -d 2 #{config.debomatic.path}", (error, stdout, stderr) ->
if error?
if stderr?
error = '\n\t' + stderr.replace(/\n/g, '\n\t')
utils.errors_handler "disk usage error:", error
return
result = {}
others = 0
for line in stdout.split('\n')
continue if line == ''
info = line.split(/\t+/g)
size = parseInt(info[0])
dirs = info[1].replace("#{config.debomatic.path}", '').split('/')
# case total size for debomatic incoming
if dirs.length <= 1
result['size'] = size
continue
distribution = dirs[1]
if not result[distribution]?
result[distribution] = {}
# case total size for distribution
if dirs.length == 2
result[distribution]['size'] = size
result[distribution]['others'] = others
others = 0
continue
# case size for distribution/subdir
subdir = dirs[2]
subdir = "chroot" if distribution == subdir
if subdir in config.debomatic.disk_usage_subdirs
result[distribution][subdir] = size
else
others += size
cb(result)
module.exports.get_all_packages = get_all_packages module.exports.get_all_packages = get_all_packages
module.exports.get_disk_usage = get_disk_usage
...@@ -109,8 +109,12 @@ watch_path_onsocket = (event_name, socket, data, watch_path, callback) -> ...@@ -109,8 +109,12 @@ watch_path_onsocket = (event_name, socket, data, watch_path, callback) ->
errors_handler = (from, err, socket) -> errors_handler = (from, err, socket) ->
from = "NO SOCKET: " + from unless socket from = "NO SOCKET: " + from unless socket
console.error from, err.message if err.message?
socket.emit config.events.error, err.message if socket msg = err.message
else
msg = err
console.error from, msg
socket.emit config.events.error, message if socket
return return
Tail::watchEvent = (e) -> Tail::watchEvent = (e) ->
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"coffee-script": "*",
"express": "4.x", "express": "4.x",
"serve-index": "*", "serve-index": "*",
"serve-static": "*", "serve-static": "*",
...@@ -15,7 +16,7 @@ ...@@ -15,7 +16,7 @@
}, },
"scripts": { "scripts": {
"install": "bash scripts/install.sh", "install": "bash scripts/install.sh",
"start": "coffee debomatic-webui", "start": "./node_modules/coffee-script/bin/coffee debomatic-webui",
"test": "cd test; mocha -b -R spec --compilers coffee:coffee-script/register --require should tests.coffee" "test": "cd test; mocha -b -R spec --compilers coffee:coffee-script/register --require should tests.coffee"
}, },
"bin": { "bin": {
......
...@@ -105,6 +105,36 @@ function Page_History() { ...@@ -105,6 +105,36 @@ function Page_History() {
} }
// add tooltip to graphs
function _create_graph_tooltip(graph, element, suffix_value) {
var $chart = $(graph);
var $toolTip = $chart
.append('<div class="tooltip fade top in" role="tooltip">' +
'<div class="tooltip-arrow"></div> ' +
'<div class="tooltip-inner"></div>' +
'</div>').find('.tooltip').hide();
$chart.on('mousemove', function (event) {
$toolTip.css({
left: event.offsetX - $toolTip.width() / 2,
top: event.offsetY - $toolTip.height() - 20
});
});
$chart.on('mouseenter', element, function () {
var $point = $(this),
value = $point.attr('ct:value'),
seriesName = $point.parent().attr('ct:series-name');
if (suffix_value)
value = value + " " + suffix_value;
$toolTip.find('.tooltip-inner').html(seriesName + ' (' + value + ')');
$toolTip.show();
});
$chart.on('mouseleave', element, function () {
$toolTip.hide();
});
}
function _create_graph_distributions() { function _create_graph_distributions() {
// build the distribution Pie graph // build the distribution Pie graph
var distributions_data = { var distributions_data = {
...@@ -147,44 +177,87 @@ function Page_History() { ...@@ -147,44 +177,87 @@ function Page_History() {
}); });
} }
Chartist.Line('#days-chart', days_data); Chartist.Line('#days-chart', days_data);
_create_graph_tooltip("#days-chart", '.ct-point');
var $chart = $('#days-chart');
var effect = function (x, t, b, c, d) { var effect = function (x, t, b, c, d) {
return -c * (t /= d) * (t - 2) + b; return -c * (t /= d) * (t - 2) + b;
}; };
var $chart = $('#days-chart');
var $toolTip = $chart
.append('<div class="tooltip fade top in" role="tooltip">' +
'<div class="tooltip-arrow"></div> ' +
'<div class="tooltip-inner"></div>' +
'</div>').find('.tooltip').hide();
$chart.on('mouseenter', '.ct-point', function () { $chart.on('mouseenter', '.ct-point', function () {
var $point = $(this), $(this).stop().animate({
value = $point.attr('ct:value'),
seriesName = $point.parent().attr('ct:series-name');
$point.stop().animate({
'stroke-width': '20px' 'stroke-width': '20px'
}, 200, effect); }, 200, effect);
$toolTip.find('.tooltip-inner').html(seriesName + ' (' + value + ')');
$toolTip.show();
}); });
$chart.on('mouseleave', '.ct-point', function () { $chart.on('mouseleave', '.ct-point', function () {
var $point = $(this); $(this).stop().animate({
$point.stop().animate({
'stroke-width': '10px' 'stroke-width': '10px'
}, 150, effect); }, 150, effect);
$toolTip.hide();
}); });
}
$chart.on('mousemove', function (event) { function _create_graph_disk(socket_data) {
$toolTip.css({ var distributions = [];
left: event.offsetX - $toolTip.width() / 2, var subdirs = [];
top: event.offsetY - $toolTip.height() - 20 var data = {};
var total_sizes = {};
var series = [];
var labels = [];
for (var distribution in socket_data) {
if (distribution == 'size') {
var total_size_in_gb = Number(socket_data.size / 1000).toFixed(1);
$("#disk-usage .total-size").text(total_size_in_gb + ' GB');
continue;
}
distributions.push(distribution);
}
distributions.sort();
for (var i = 0; i < distributions.length; i++) {
distribution = distributions[i];
for (var subdir in socket_data[distribution]) {
if (subdir == 'size') {
total_sizes[distribution] = socket_data[distribution].size;
continue;
}
if (!data.hasOwnProperty(subdir)) {
subdirs.push(subdir);
data[subdir] = [];
}
data[subdir].push(socket_data[distribution][subdir]);
}
}
for (i = 0; i < subdirs.length; i++) {
series.push({
name: subdirs[i],
data: data[subdirs[i]]
}); });
}
var options = {
seriesBarDistance: 12
};
Chartist.Bar('#disk-chart', {
labels: distributions,
series: series
}, options);
_create_graph_tooltip("#disk-chart", '.ct-bar', "MB");
// WORKAROUND: add total spaces to label
// wating for support multilines for label in chartist-js
// https://github.com/gionkunz/chartist-js/issues/25
$('#disk-chart svg').height("+=20");
$('#disk-chart .ct-label.ct-horizontal').each(function (index, elem) {
var size = document.createElementNS("http://www.w3.org/2000/svg", 'text');
var currentY = Number(elem.getAttribute('dy'));
size.setAttribute('dx', Number(elem.getAttribute('dx')));
size.setAttribute('dy', currentY + 15);
size.setAttribute('class', 'ct-label ct-horizontal ct-size');
size.textContent = total_sizes[elem.textContent] + " MB";
elem.parentNode.appendChild(size);
}); });
} }
function _exportTableToCSV($table, filename) { function _exportTableToCSV($table, filename) {
...@@ -257,10 +330,20 @@ function Page_History() { ...@@ -257,10 +330,20 @@ function Page_History() {
$('.body').fadeIn("fast"); $('.body').fadeIn("fast");
}); });
socket.on(config.events.client.disk_usage, function (socket_data) {
debug_socket('received', config.events.client.disk_usage, socket_data);
_create_graph_disk(socket_data);
});
debug_socket('emit', config.events.client.history, ''); debug_socket('emit', config.events.client.history, '');
socket.emit(config.events.client.history); socket.emit(config.events.client.history);
debug_socket('emit', config.events.client.disk_usage, '');
socket.emit(config.events.client.disk_usage);
}; };
// active downlaod tooltip
$("[data-toggle='popover']").popover();
$('#download').on('click', function () { $('#download').on('click', function () {
_exportTableToCSV.apply(this, [$('#history'), 'history.csv']); _exportTableToCSV.apply(this, [$('#history'), 'history.csv']);
}); });
......
...@@ -262,7 +262,7 @@ footer .info { ...@@ -262,7 +262,7 @@ footer .info {
/* History page */ /* History page */
.ct-chart { .ct-chart {
height: 180px; height: 180px;
width: 50%; width: 100%;
float: left; float: left;
margin-bottom: 30px; margin-bottom: 30px;
position: relative; position: relative;
...@@ -279,6 +279,7 @@ footer .info { ...@@ -279,6 +279,7 @@ footer .info {
#history { #history {
margin-top: 15px; margin-top: 15px;
max-width: 1000px;
} }
#history .alt-row { #history .alt-row {
...@@ -289,6 +290,10 @@ footer .info { ...@@ -289,6 +290,10 @@ footer .info {
white-space: nowrap; white-space: nowrap;
} }
#disk-usage .ct-label.ct-size {
font-size: 0.72em;
}
/* add more colors to graphs */ /* add more colors to graphs */
.ct-series-e .ct-bar, .ct-series-e .ct-bar,
.ct-chart .ct-series.ct-series-e .ct-line, .ct-chart .ct-series.ct-series-e .ct-line,
......
<% include header.ejs %> <% include header.ejs %>
<article class="page"> <article class="">
<header><h2>History</h2></header> <header><h2>History</h2></header>
<div class="row" id="charts">
<div class="col-md-4">
<p class="lead text-muted"> <p class="lead text-muted">
The log of packages The log of builds
</p> </p>
<div id="charts">
<div id="days-chart" class="ct-chart"></div> <div id="days-chart" class="ct-chart"></div>
</div>
<div class="col-md-4">
<p class="lead text-muted">
Total packages
</p>
<div id="distributions-chart" class="ct-chart"></div> <div id="distributions-chart" class="ct-chart"></div>
</div> </div>
<div id="disk-usage" class="col-md-4">
<p class="lead text-muted">
Disk usage <span class="total-size"></span>
</p>
<div id="disk-chart" class="ct-chart"></div>
</div>
</div>
<div class="body" style="display: none"> <div class="body" style="display: none">
<a id="download" class="btn btn-primary">Download current view</a> <a id="download" class="btn btn-primary" data-trigger="hover" data-toggle="popover" title="Export to CSV" data-placement="right" data-content="Save the packages displaied in the table in format CSV. You can filter out packages by selecting distributions, a status, specifying a date or an uploader.">Download current view</a>
<table id="history" class="tablesorter"> <table id="history" class="tablesorter">
<thead> <thead>
<tr> <tr>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment