Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial checkin of Metrics LineGraph Component #1098

Merged
merged 1 commit into from
May 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 105 additions & 13 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;
}
4 changes: 3 additions & 1 deletion resource/ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
<meta charset="utf-8">
<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/rest_explorer.css">
<link rel="stylesheet" href="/css/graph.css">
<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:_mithril.MithrilPromise<Models.Metrics.QueryResultSet>;
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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugg... do you prefer some of these weird js operations?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like !!

vm.key = key;
}
return m.component(LineGraph, vm)
}
}
}
}
Loading