Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit 890b2464ff42f9d28b443dc4f7c641fe724b2a67 @jgallen23 committed May 25, 2012
@@ -0,0 +1 @@
+node_modules
@@ -0,0 +1,50 @@
+#!/usr/bin/env node
+
+var opt = require('optimist')
+ .usage('Start dashboard server\n$0 <config.json>')
+ .demand(1)
+ .options('h', {
+ desc: 'Show help info',
+ alias: 'help',
+ type: 'boolean'
+ })
+ .options('p', {
+ desc: 'Port to run server',
+ alias: 'port',
+ default: 8000
+ });
+
+var argv = opt.argv;
+
+if (argv.help) {
+ opt.showHelp();
+}
+
+var static = require('node-static');
+var path = require('path');
+var fs = require('fs');
+
+var publicPath = path.join(__dirname, '../public');
+var file = new(static.Server)(publicPath);
+
+var configFile = argv._[0];
+
+var startServer = function(err, configStr) {
+ if (err) throw err;
+ var config = JSON.parse(configStr);
+ require('http').createServer(function (request, response) {
+ if (request.url == '/config.json') {
+ response.setHeader('Content-Type', 'application/json');
+ response.end(configStr);
+ } else if (config.icon && request.url == '/ui/images/icon.png') {
+ fs.createReadStream(config.icon).pipe(response);
+ } else {
+ request.addListener('end', function () {
+ file.serve(request, response);
+ });
+ }
+ }).listen(argv.port);
+ console.log('Server started on port ' + argv.port);
+};
+
+fs.readFile(configFile, 'utf8', startServer);
@@ -0,0 +1,22 @@
+{
+ "name": "cube-dashboard",
+ "author": "Greg Allen <@jgaui> (http://jga.me)",
+ "description": "a dashboard for cube",
+ "homepage": "https://github.com/jgallen23/cube-dashboard",
+ "version": "0.0.1",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/jgallen23/cube-dashboard.git"
+ },
+ "dependencies": {
+ "node-static": "*",
+ "optimist": "*"
+ },
+ "devDependencies": {
+ },
+ "engines": {
+ "node": "*"
+ },
+ "bin": { "cube-dashboard": "./bin/dashboard.js" },
+ "keywords": ["cube", "dashboard", "report"]
+}
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Dashboard</title>
+ <meta name="apple-mobile-web-app-capable" content="yes" />
+ <meta name="apple-mobile-web-app-status-bar-style" content="black" />
+ <link rel="apple-touch-icon-precomposed" href="/ui/images/icon.png"/>
+ <meta name="viewport" content="width = device-width, initial-scale = 1.0, user-scalable = no" />
+ <!-- just for dev -->
+ <script src="/ui/vendor/live.js"></script>
+ <link rel="stylesheet" href="/ui/stylesheets/dashboard.css"/>
+ </head>
+ <body>
+ <header>
+ <select id="dashboards">
+ </select>
+ <select id="step">
+ <option value="1e4">10 seconds</option>
+ <option value="6e4">1 minute</option>
+ <option value="3e5">5 minutes</option>
@cwarden

cwarden Feb 1, 2013

Contributor

Any reason to not include 1 hour and 1 day as options?

@jgallen23

jgallen23 via email Feb 2, 2013

Owner
+ </select>
+ </header>
+ <div id="dashboard"></div>
+ <script src="/ui/vendor/d3.v2.min.js"></script>
+ <script src="/ui/vendor/cubism.v1.min.js"></script>
+ <script src="/ui/vendor/aug.min.js"></script>
+ <script src="/ui/scripts/dashboards.js"></script>
+ <script src="/ui/scripts/dashboard.js"></script>
+ <script>
+ d3.json('/config.json', function(config) {
+ window.dashboards = new DashboardSelect('#dashboards', config.dashboards);
+ window.dashboard = new Dashboard("#dashboard", config.host, dashboards.getCurrentDashboard());
+ });
+ </script>
+
+ </body>
+</html>
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,18 @@
+var metrics = [
+ { title: 'Requests', metric: cube.metric("sum(request)") },
+ { title: 'Searches', metric: cube.metric("sum(search)") },
+ { title: 'Logins', metric: cube.metric("sum(login)") },
+ { title: 'New Users', metric: cube.metric("sum(newUser)") },
+ { title: 'New Items', metric: cube.metric("sum(newItem)") },
+ { title: 'Applications', metric: cube.metric("sum(comment)") },
+ { title: 'Likes', metric: cube.metric("sum(likeItem)") },
+ { title: 'User Selected', metric: cube.metric("sum(userSelected)") },
+ { title: 'Item Accepted', metric: cube.metric("sum(itemAccepted)") },
+ { title: 'Item Expired', metric: cube.metric("sum(itemExpired)") },
+ { title: 'Messages', metric: cube.metric("sum(message)") },
+ { title: 'Releases', metric: cube.metric("sum(release)") },
+ { title: '500s', metric: cube.metric("sum(error.eq(type, '500'))") },
+ { title: '404s', metric: cube.metric("sum(error.eq(type, '404'))") },
+ { title: 'JS Errors', metric: cube.metric("sum(error.eq(type, 'js'))") }
+];
+setDashboardMetrics(metrics, true);
@@ -0,0 +1,105 @@
+var Dashboard = function(elementSelector, host, options) {
+ this.selector = elementSelector;
+ this.host = host;
+ this.options = aug(true, {}, Dashboard.defaults, options);
+ this.setup();
+ if (options.metrics) {
+ this.setMetrics(options.metrics);
+ }
+};
+
+Dashboard.defaults = {
+ host: '',
+ showTotals: false,
+ height: 35
+};
+
+Dashboard.prototype.setup = function() {
+ var step = +cubism.option("step", 1e4);
+ var context = cubism.context()
+ .step(step)
+ .size(window.innerWidth - 4);
+
+ //$("window").resize(function() {
+ //window.location.reload();
+ //});;
+
+ this.cube = context.cube(this.host);
+ this.horizon = context.horizon();
+
+ // Add top and bottom axes to display the time.
+ d3.select(this.selector).selectAll(".axis")
+ .data(["top", "bottom"])
+ .enter().append("div")
+ .attr("class", function(d) { return d + " axis"; })
+ .each(function(d) { d3.select(this).call(context.axis().ticks(12).orient(d)); });
+
+ // Add a mouseover rule.
+ d3.select(this.selector)
+ .append("div")
+ .attr("class", "rule")
+ .call(context.rule());
+
+ //
+
+ // On mousemove, reposition the chart values to match the rule.
+ context.on("focus", function(i) {
+ d3.selectAll(".value").style("right", i == null ? null : context.size() - i + 5 + "px");
+ });
+
+ d3.selectAll("#step option").property("selected", function() {
+ return this.value == step;
+ });
+
+ d3.select("#step").on("change", function() {
+ window.location = "?step=" + this.value + "&" + location.search.replace(/[?&]step=[^&]*(&|$)/g, "$1").substring(1);
+ });
+};
+
+Dashboard.prototype.fetchTotals = function(metrics) {
+ var self = this;
+
+ var getTotal = function(index, expression, start, stop) {
+ var format = d3.time.format.iso;
+ var url = self.host+'/1.0/metric?expression='+expression+'&start='+format(start)+'&stop='+format(stop)+'&step=3600000&cachebuster='+ (+new Date());
+ d3.json(url, function(response) {
+ var val = 0;
+ for (var i = 0, c = response.length; i < c; i++) {
+ var res = response[i];
+ val += res.value;
+ }
+ var el = d3.selectAll('.horizon .title .totals')[0][index];
+ el.innerHTML = val;
+ });
+ };
+
+ var start = d3.time.day.floor(new Date());
+ var stop = d3.time.day.offset(start, 1);
+
+ for (var i = 0, c = metrics.length; i < c; i++) {
+ var metric = metrics[i];
+ var expression = metric.expression.toString();
+ getTotal(i, expression, start, stop);
+ }
+
+ setTimeout(this.fetchTotals, 60*1000);
+};
+
+Dashboard.prototype.setMetrics = function(metrics) {
+ var self = this;
+ d3.select(this.selector)
+ .insert("div", ".bottom")
+ .selectAll(".horizon")
+ .data(metrics)
+ .enter().append("div")
+ .attr("class", "horizon")
+ .call(self.horizon
+ .height(self.options.height)
+ .title(function(d) { return d.title; })
+ .metric(function(d) { return self.cube.metric(d.expression); }));
+
+ if (this.options.showTotals) {
+ d3.selectAll('.horizon .title').append('span').attr('class', 'totals');
+ this.fetchTotals(metrics);
+ }
+};
@@ -0,0 +1,31 @@
+var DashboardSelect = function(selector, dashboards) {
+ this.selector = selector;
+ this.dashboards = dashboards;
+
+ this.currentIndex = cubism.option("dashboard", 0);
+ this.setup();
+};
+
+DashboardSelect.prototype.setup = function() {
+
+ var select = d3.select(this.selector)[0][0];
+ for (var i = 0, c = this.dashboards.length; i < c; i++) {
+ var dashboard = this.dashboards[i];
+
+ var option = document.createElement('option');
+ option.setAttribute('value', i);
+ option.innerHTML = dashboard.name;
+ select.appendChild(option);
+ }
+ select.selectedIndex = this.currentIndex;
+ select.addEventListener('change', function() {
+ window.location = "?dashboard=" + select.value + "&" + location.search.replace(/[?&]dashboard=[^&]*(&|$)/g, "$1").substring(1);
+ });
+
+ document.title = this.getCurrentDashboard().name + ' Dashboard';
+
+};
+
+DashboardSelect.prototype.getCurrentDashboard = function() {
+ return this.dashboards[this.currentIndex];
+};
Oops, something went wrong.

0 comments on commit 890b246

Please sign in to comment.