aboutsummaryrefslogblamecommitdiff
path: root/static/assets/js/monitor.js
blob: 3a670c361adff42d6a6ab3537f666c373d1c7749 (plain) (tree)


























































                                                                     


                                             




                                          




                                                                      
                          
                                                                                    

 

                                                                


                                                     
                                   
                                





                                              




                                                               





                                                
                                   
                                  
                         







                                                 
                                   








                                                                                                   

                                        


                         
                                   
                                         
                                          
     


                                       





                                                                                             
         






                                     







                                                     















                                                                                  
         

            



                                                                            



























                                                                                        
         
     



                                                     
                     

                                                                        







                                                                         

                                                   
                                   
 
                                                       
                                                  

                                             













                                                                                        
                                                     

                                                                          



                                                                


                                
                                                              

                          
                                         
                                  
                       

                                        
 

                    

                                     
                                                                                   


                                               



                                                  
                                                                                 


















                                                                                         



























































































                                                                                                    


















                                                                                 
var ajax_site = 'ajax';
var refresh_rate = 3000;
var send_timeout = 20000;
var retry_rate = 3000;

function Blocker(sleeper) {
    this.counter = 0;
    this.waiting = false;
    this.sleeper = sleeper;
}

Blocker.prototype.setHook = function() {
    var blocker = this;
    return function() {
        blocker.counter++;
    }
}
Blocker.prototype.setNotifier = function(cb) {
    var blocker = this;
    return function() {
        cb.apply(this, arguments);
        if (--blocker.counter == 0 && blocker.waiting)
            blocker.sleeper();
    }
};

Blocker.prototype.go = function() {
    if (this.counter == 0)
        this.sleeper();
    this.waiting = true;
}

function send_request(on_success) {
    $.ajax({
            url: ajax_site,
            type: 'GET',
            cache: false,
            dataType: 'json',
            async: true,
            success: on_success,
            timeout: send_timeout,
            error: function(a, b, c) {
                console.log("failed while connecting, reason: " + b);
                setTimeout(function() {
                        send_request(on_success);
                }, retry_rate);
            }
    });
}

function hex(x) {
    return ("0" + parseInt(x).toString(16)).slice(-2);
}

function random_range(min, max) {
    return Math.random() * (max - min) + min;
}

function random_color() {
    var r = (random_range(0, 200) + 150) / 2;
    var g = (random_range(0, 200) + 150) / 2;
    var b = (random_range(0, 200) + 150) / 2;
    return "#" + hex(r) + hex(g) + hex(b);
}

function LineGraph() {}

LineGraph.prototype.recalc_y_domain = function(mdata) {
    var min = d3.min($.map(mdata, function (data) {
                return d3.min(data, function(d) { return d.y; }); }));
    var max = d3.max($.map(mdata, function (data) {
                return d3.max(data, function(d) { return d.y; }); }));
    var delta = max - min;
    this.y.domain([min - Math.max(delta / 3.0, 1), max + Math.max(delta / 3.0, 1)]);
}

LineGraph.prototype.recalc_x_domain = function(data) {
    this.x.domain(d3.extent(data, function(d) { return d.x; }));
}

LineGraph.prototype.recalc_domain = function(mdata) {
    this.recalc_x_domain(mdata[0]);
    this.recalc_y_domain(mdata);
}

LineGraph.prototype.parse_data = function(d) {
    var data = [];
    for (var i = 0; i < d.records.length; i++)
    {
        var row = d.records[i];
        while (data.length < row.rec.length)
            data.push([]);
        for (var j = 0; j < row.rec.length; j++)
            data[j].push({x: i, y: +row.rec[j], rid: row.rid});
    }
    return data;
}

LineGraph.prototype.update = function(d, i, b) {
    var graph = this;
    var mdata = this.parse_data(d);
    if (mdata.length == 0) return;
    var recalced = false;
    var svg = d3.select(this.elem).select("svg");
    this.recalc_y_domain(mdata);
    var ya = svg.select(".y.axis");
    ya.transition()
        .duration(750)
        .call(this.yAxis);
    var maxw = ya.node().getBBox().width;
    svg.select("g")
        .transition().duration(750)
        .attr("transform", "translate(" + (maxw + this.margin.left) + "," + this.margin.top + ")");
    var niw = this.width - maxw;
    if (Math.abs(niw - this.inner_width) > 1e-6)
    {
        this.inner_width = niw;
        this.x = d3.scale.linear().range([0, this.inner_width]);
        this.xAxis = d3.svg.axis().scale(this.x)
                        .orient("bottom").ticks(5).tickFormat(d3.format("d"));
    }
    this.recalc_x_domain(this.pdata[0]);
    svg.transition().duration(750)
       .select(".x.axis")
       .call(this.xAxis);
    svg.select("rect")
        .transition().duration(750)
        .attr("width", this.inner_width);
    for (var i = 0; i < mdata.length; i++)
    {
        var data = mdata[i];
        if (data.length == 0) continue;
        var pdata = this.pdata[i];
        var path = this.line[i];

        var shift = pdata.length ? data[0].rid - pdata[0].rid : 0;
        var add = pdata.length ? data[data.length - 1].rid - pdata[pdata.length - 1].rid : 0;
        if (pdata.length == 0 || pdata[pdata.length - 1].rid < data[0].rid ||
            pdata[0] > data[data.length - 1].rid || shift < 0 || add < 0)
        {
            if (data.length == 1)
            {
                data = [];
                console.log("clear");
            }
            if (!recalced)
            {
                this.recalc_x_domain(data);
                svg.transition()
                   .duration(750)
                   .select(".x.axis")
                   .call(this.xAxis);
                svg.select("rect")
                    .transition().duration(750)
                    .attr("width", this.inner_width);
                recalced = true;
            }
            path.transition()
                .duration(200)
                .style("opacity", 1e-6)
                .transition()
                .attr("d", graph.valueline(data))
                .transition()
                .duration(200)
                .style("opacity", 1)
                .each(b.setHook())
                .each("end", b.setNotifier(function(i, data) { return function() {
                    graph.pdata[i] = data;
                    console.log("refresh complete");
                }}(i, data)));
            console.log("refresh started");
        }
        else
        {
            //console.log("shift: " + shift + "add: " + add);
            data = pdata.concat(data.slice(data.length - add, data.length));
            for (var j = 0; j < data.length; j++)
                data[j].x = j;
            if (shift > 0)
                path.transition().duration(750)
                .attr("d", graph.valueline(data))
                .transition()
                .duration(750)
                .attr("transform", "translate(" + this.x(-shift) + ")")
                .each(b.setHook())
                .each("end", b.setNotifier(function(i, data) { return function() {
                    for (var j = 0; j < shift; j++)
                        data.shift();
                    for (var j = 0; j < data.length; j++)
                        data[j].x = j;
                    graph.pdata[i] = data;
                    d3.select(this).attr("d", graph.valueline(data))
                                    .attr("transform", "translate(" + graph.x(0) + ")");
                }}(i, data)))
            else
            {
                //console.log(data);
                this.pdata[i] = data;
                /* Note: the added data is delayed from showing for the sake of
                 * more decent transition animation */
                path.transition().duration(750)
                .attr("d", graph.valueline(data))
                .transition()
                .each(b.setHook())
                .each("end", b.setNotifier(function(){}));
            }
        }
    }
}

LineGraph.prototype.setup = function(elem, d, i, b) {
    var graph = this;
    this.elem = elem;
    var margin = this.margin = {top: 10, right: 0, bottom: 50, left: 0};
    var clippath_name = "clip" + d.jid;
    // Adds the svg canvas
    var svg = d3.select(this.elem)
                .append("svg")
                .attr("width", "100%")
                .attr("height", "100%")
                .append("g")
                .attr("transform",
                    "translate(" + margin.left + "," + margin.top + ")");
    this.width = 500 - margin.left - margin.right;
    this.height = 250 - margin.top - margin.bottom;
    var mdata = this.parse_data(d);

    this.y = d3.scale.linear().range([this.height, 0]);
    if (mdata.length) this.recalc_y_domain(mdata);
    this.yAxis = d3.svg.axis().scale(this.y)
                    .orient("left").ticks(5);

    // Add the Y Axis
    var ya = svg.append("g")
        .attr("class", "y axis")
        .call(this.yAxis);
    /*
    ya.selectAll("text").each(function() {
        if(this.getBBox().width > maxw) maxw = this.getBBox().width;
    });
    */
    var maxw = ya.node().getBBox().width;
    svg.attr("transform", "translate(" + (maxw + margin.left) + "," + margin.top + ")");
    this.inner_width = this.width - maxw;
    this.x = d3.scale.linear().range([0, this.inner_width]);
    if (mdata.length) this.recalc_x_domain(mdata[0]);
    this.xAxis = d3.svg.axis().scale(this.x)
                    .orient("bottom").ticks(5).tickFormat(d3.format("d"));
    // Define the line
    this.valueline = d3.svg.line()
                        .x(function(d) { return this.x(d.x); })
                        .y(function(d) { return this.y(d.y); });
    // Add the X Axis
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + this.height + ")")
        .call(this.xAxis);

    svg.append("defs").append("clipPath")
        .attr("id", clippath_name)
        .append("rect")
        .attr("width", this.inner_width)
        .attr("height", this.height);

    this.pdata = [];
    this.line = [];
    d3.select(this.elem).transition()
        .duration(750)
        .style("height", this.height + this.margin.top + this.margin.bottom + "px")
        .each(b.setHook())
        .each("end", b.setNotifier(function() {
                    // Get the data
            for (var i = 0; i < mdata.length; i++)
            {
                var data = mdata[i];
                var path = svg.append("g")
                                .attr("clip-path", "url(#" + clippath_name + ")")
                                .append("path");

                path.attr("class", "line")
                    .style("stroke", random_color)
                    .attr("d", graph.valueline(data));
                var totalLength = path.node().getTotalLength();
                path.attr("stroke-dasharray", totalLength + " " + totalLength)
                    .attr("stroke-dashoffset", totalLength)
                    .transition()
                    .duration(2000)
                    .ease("linear")
                    .attr("stroke-dashoffset", 0)
                    .each(b.setHook())
                    .each("end", b.setNotifier(function(data, path) { return function() {
                        path.attr("stroke-dasharray", "");
                        graph.line.push(path);
                    }}(data, path)));
            }
            graph.pdata = mdata;
        }));
}

function ListGraph() {}

ListGraph.prototype.parse_data = function(d) {
    var data = [];
    for (var i = 0; i < d.records.length; i++)
    {
        var rec = d.records[i];
        data.push({rid: rec[0], rec: rec});
    }
    return data;
}

ListGraph.prototype.setup = function(elem, d, i, b) {
    var data = this.parse_data(d);
    var div = d3.select(elem);
    this.elem = elem;
    /*
    div.append("div")
        .style("text-align", "center")
        .append("a")
        .style("font-size", "16px")
        .style("text-decoration", "none")
        .text(d.name);
        */
    var table = d3.select(elem)
                .append("table")
                .attr("class", "listgraph table")
                .style("width", "100%")
                .style("height", "100%");
    this.table = table;
    table.selectAll("tr").data(data)
        .enter().append("tr")
        .selectAll("td").data(function(d) { return d.rec; })
        .enter().append("td")
        .style("opacity", 0)
        .transition().duration(750)
        .style("opacity", 1)
        .text(function(d) { return d; })
        .each(b.setHook())
        .each("end", b.setNotifier(function(){}));
    d3.select(elem)
        //.transition().duration(750)
        .style("height", null);
}

ListGraph.prototype.update = function(d, i, b) {
    var graph = this;
    var data = this.parse_data(d);
    var items = this.table.selectAll("tr").data(data, function(d) { return d.rid; });
    b.setHook()();
    var b0 = new Blocker(b.setNotifier(function() {
        items.order();
    }));

    items.selectAll("td").text(function(d) { return d; });
    items.enter()
        .append("tr")
        .selectAll("td")
        .data(function(d) { return d.rec; })
        .enter()
        .append("td")
        .text(function(d) { return d; });
    items.exit().remove();
    b0.go();
}

var graph_handler = {"linegraph": LineGraph, "listgraph": ListGraph};

function update_jobs(resp, after) {
    resp = Object.keys(resp).map(function(k) { return resp[k] });
    var jobs = d3.select("#jobs").selectAll(".job_graph").data(resp, function(d) { return d.jid; });
    var b = new Blocker(after);
    jobs.each(function(d, i) {
        d3.select(this).select(".job_content").node().handler.update(d, i, b);
    });
    var outer = jobs.enter()
        .append("div")
        .attr("class", "job_graph panel panel-info")
        .style("width", "540px");
    outer.append("div")
        .attr("class", "panel-heading")
        .text(function(d) { return d.name; });
    outer.append("div")
        .attr("class", "job_content panel-body")
        .style("height", "0px")
        .each(function(d, i) {
            this.handler = new graph_handler[d.metadata.type]();
            this.handler.setup(this, d, i, b);
        });
    jobs.exit()
        .style("height", function(d) { return d3.select(this).style("height"); })
        .transition().duration(750)
        .style("height", "0px")
        .each(b.setHook())
        .each("end",b.setNotifier(function(){}))
        .remove();
    b.go();
}

function graph_tick() {
    send_request(function(resp) {
        update_jobs(resp, function() {
            setTimeout(graph_tick, refresh_rate);
        });
    });
}

graph_tick();