Skip to content

Commit

Permalink
Initial checkin of Metrics LineGraph Component
Browse files Browse the repository at this point in the history
Commit creates the first version of a reusable LineGraph component, which graphs
the results of cockroach time series query.

It's a bit bare-bones in appearance, but is already reusable and should provide
a good foundation for building our admin monitor pages.
  • Loading branch information
Matt Tracy committed May 21, 2015
1 parent 5b5966d commit e680ee4
Show file tree
Hide file tree
Showing 10 changed files with 556 additions and 14 deletions.
114 changes: 103 additions & 11 deletions resource/embedded.go

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions resource/ui/css/graph.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.line {
fill: none;
stroke-width: 1.5px;
}

.line0 {
stroke:red;
}

.line1 {
stroke:blue;
}

.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
2 changes: 2 additions & 0 deletions resource/ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,900|Source+Code+Pro' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/css/app.css">
<link rel="stylesheet" href="/css/rest_explorer.css"> <!-- TODO(andybons): @import-like behavior -->
<link rel="stylesheet" href="/css/graph.css"> <!-- TODO(andybons): @import-like behavior -->
<script src="/js/libs/d3/3.3.5/d3.min.js"></script>
<script src="/js/libs/mithriljs/0.2.0/mithril.min.js"></script>
<title>Cockroach</title>
Expand All @@ -32,6 +33,7 @@
<a href="#/" class="appNav-link appNav-homeName">Cockroach</a>
<a href="#/monitor" class="appNav-link">Monitor</a>
<a href="#/rest-explorer" class="appNav-link">REST Explorer</a>
<a href="#/graph" class="appNav-link">Graph</a>
</header>
<div id="root" class="fullHeightContainer"></div>
<script src="/js/app.js"></script>
Expand Down
163 changes: 162 additions & 1 deletion resource/ui/js/app.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions resource/ui/ts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/// <reference path="typings/mithriljs/mithril.d.ts" />
/// <reference path="pages/rest_explorer.ts" />
/// <reference path="pages/monitor.ts" />
/// <reference path="pages/graph.ts" />

// Author: Andrew Bonventre (andybons@gmail.com)
// Author: Bram Gruneir (bramgruneir@gmail.com)
Expand All @@ -10,4 +11,5 @@ m.route.mode = "hash";
m.route(document.getElementById("root"), "/rest-explorer", {
"/rest-explorer": AdminViews.RestExplorer.Page,
"/monitor": AdminViews.Monitor.Page,
"/graph": AdminViews.Graph.Page,
});
142 changes: 142 additions & 0 deletions resource/ui/ts/components/metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// source: components/metrics.ts
/// <reference path="../typings/mithriljs/mithril.d.ts" />
/// <reference path="../typings/d3/d3.d.ts" />
/// <reference path="../models/timeseries.ts" />

/**
* Components defines reusable components which may be used on multiple pages,
* or multiple times on the same page.
*/
module Components {
/**
* Metrics contains components used to display metrics data.
*/
export module Metrics {
/**
* LineGraph displays a line graph of the data returned from a time
* series query.
*/
export module LineGraph {
/**
* ViewModel is the model for a specific LineGraph - in addition to
* the backing Query object, it also maintains other per-component
* display options.
*/
interface ViewModel {
width:number;
height:number;
query:Models.Metrics.Query;
key?:number;
}

/**
* Controller maintains the functionality needed for a single
* LineGraph.
*/
class Controller {
margin = {top:20, right:20, bottom: 30, left:60};

// Width and height of the area containing the graph lines.
private chartWidth: number;
private chartHeight: number;

// Scales and Axes.
private timeScale = d3.time.scale();
private valScale = d3.scale.linear();
private timeAxis = d3.svg.axis().scale(this.timeScale).orient("bottom");
private valAxis = d3.svg.axis().scale(this.valScale).orient("left");

// Line data interpreter.
private line = d3.svg.line()
.x((d) => this.timeScale(d.timestamp_nanos/1.0e6))
.y((d) => this.valScale(d.value));

private color = d3.scale.category10();

// Query results.
results;
error = m.prop("");

constructor(public vm:ViewModel) {
this.chartWidth = vm.width - this.margin.left - this.margin.right;
this.chartHeight = vm.height - this.margin.top - this.margin.bottom;

// Initialize axes.
this.timeScale.range([0, this.chartWidth]);
this.valScale.range([this.chartHeight, 0]);

// Query for initial result set.
this.results = vm.query.query().then(null, this.error);
}

/** drawGraph gets direct access to the svg element of the graph
* after it is added to DOM. When the SVG element is first
* initialized, we use D3 to draw the data from the query.
*/
drawGraph = (element:Element, isInitialized:boolean, context:any) => {
if (!isInitialized) {
var data:Models.Metrics.QueryResult[] = this.results().results;

// Scale value axis based on data.
this.valScale.domain([
d3.min(data, (d) => d3.min(d.datapoints, (dp) => dp.value)),
d3.max(data, (d) => d3.max(d.datapoints, (dp) => dp.value))
]);
this.timeScale.domain([this.vm.query.start, this.vm.query.end]);

var svg = d3.select(element)
.attr("width", this.vm.width)
.attr("height", this.vm.height)
.append("g")
.attr("transform",
"translate(" + this.margin.left + "," + this.margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + this.chartHeight + ")")
.call(this.timeAxis);
svg.append("g")
.attr("class", "y axis")
.call(this.valAxis);

// append lines
svg.selectAll(".line")
.data(data)
.enter()
.append("path")
.attr("class", (d,i) => "line line" + i)
.attr("d", (d) => this.line(d.datapoints))
.style("stroke", (d) => this.color(d.name));
}
}
}

export function controller(model:ViewModel) {
return new Controller(model);
}

export function view(ctrl) {
if (ctrl.error()) {
return m("", "error loading graph:" + ctrl.error());
} else if (ctrl.results()) {
return m("svg.graph", {config: ctrl.drawGraph});
} else {
return m("", "loading...");
}
}

/**
* create instantiates a single instance of the LineGraph component
* which displays data from the supplied Metrics.Query object.
*
* @param key The key param is used by mithril to track objects in lists which can be rearranged.
*/
export function create(width:number, height:number, query:Models.Metrics.Query, key?:number){
var vm:ViewModel = {width:width, height:height, query:query}
if (!!key) {
vm.key = key;
}
return m.component(LineGraph, vm)
}
}
}
}
Loading

0 comments on commit e680ee4

Please sign in to comment.