Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement monthly stats [closes #55]

This implements a fake model that aggregates statistics about badge
issuances and exposes that information at `/admin/stats`. Right now
the information given is low fidelity -- it only gives a total count of
badges per month, not a breakdown of total per badge. We could improve
upon this later if needed.
  • Loading branch information...
commit 568333ac07f7b873f57408d4b73bdbffaa561f80 1 parent 0bd0576
@brianloveswords brianloveswords authored
View
4 app.js
@@ -13,6 +13,7 @@ var admin = require('./routes/admin');
var issuer = require('./routes/issuer');
var api = require('./routes/api');
var debug = require('./routes/debug');
+var stats = require('./routes/stats');
var app = express();
var logger = app.logger = require('./lib/logger');
@@ -69,6 +70,9 @@ app.configure('production', function () {
app.get('/admin/config', admin.configure);
app.post('/admin/config', issuer.update);
+app.get('/admin/stats', [stats.monthly], admin.stats);
+
+
// Badge listing
// -------------
var indexMiddleware = [badge.findAll, behavior.findAll];
View
21 models/stats.js
@@ -0,0 +1,21 @@
+const BadgeInstance = require('./badge-instance');
+
+const Stats = {};
+
+function isoMonthDay(date) {
+ return date.toISOString().slice(0, 7);
+}
+
+Stats.monthly = function monthlyStats(callback) {
+ const months = {};
+ BadgeInstance.find({}, function (err, instances) {
+ if (err) return callback(err);
+ instances.forEach(function (instance) {
+ const month = isoMonthDay(instance.issuedOn);
+ months[month] = (months[month] || 0) + 1;
+ });
+ callback(null, months);
+ });
+};
+
+module.exports = Stats;
View
11 routes/admin.js
@@ -140,6 +140,17 @@ exports.userList = function userList(req, res, next) {
});
};
+exports.stats = function stats(req, res, next) {
+ return res.render('admin/stats.html', {
+ page: 'stats',
+ issuer: req.issuer,
+ stats: req.stats,
+ user: req.session.user,
+ csrf: req.session._csrf,
+ users: req.users
+ });
+};
+
exports.notFound = function notFound(req, res, next) {
res.status(404)
return res.render('public/404.html', {});
View
12 routes/stats.js
@@ -0,0 +1,12 @@
+const Stats = require('../models/stats');
+
+exports.monthly = function monthly(req, res, next) {
+ Stats.monthly(function (err, stats) {
+ if (err) return next(err);
+ const months = Object.keys(stats).map(function (month) {
+ return { month: month, count: stats[month] };
+ }).reverse();
+ req.stats = months;
+ return next();
+ });
+};
View
60 tests/stats-model.test.js
@@ -0,0 +1,60 @@
+const test = require('./');
+const db = require('../models');
+const BadgeInstance = require('../models/badge-instance');
+const Stats = require('../models/stats');
+const util = require('../lib/util');
+
+test.applyFixtures({
+ 'instance': new BadgeInstance({
+ issuedOn: new Date('2012-10-15'),
+ user: 'brian@example.org',
+ hash: 'hash',
+ badge: 'link-advanced',
+ assertion: '{ "assertion" : "yep" }',
+ }),
+ 'instance2': new BadgeInstance({
+ issuedOn: new Date('2012-11-15'),
+ user: 'brian@example.org',
+ hash: 'hash',
+ badge: 'link-hyper-advanced',
+ assertion: '{ "assertion" : "yep" }',
+ }),
+ 'instance3': new BadgeInstance({
+ issuedOn: new Date('2012-12-15'),
+ user: 'brian-delete@example.org',
+ hash: 'hash',
+ badge: 'link-hyper-advanced',
+ assertion: '{ "assertion" : "yep" }',
+ }),
+ 'instance4': new BadgeInstance({
+ issuedOn: new Date('2013-01-15'),
+ user: 'brian-delete@example.org',
+ hash: 'otherhash',
+ badge: 'link-turbo-advanced',
+ assertion: '{ "assertion" : "yep" }',
+ }),
+ 'instance5': new BadgeInstance({
+ issuedOn: new Date('2013-01-16'),
+ user: 'brian-delete@example.org',
+ hash: 'otherhash',
+ badge: 'link-mega-advanced',
+ assertion: '{ "assertion" : "yep" }',
+ }),
+}, function (fixtures) {
+ test('Stats.monthly', function (t) {
+ Stats.monthly(function (err, stats) {
+ t.notOk(err, 'should not have an error');
+ t.same(stats['2011-01'], undefined, 'should not have badges for jan 2011');
+ t.same(stats['2012-10'], 1, 'should have one badge');
+ t.same(stats['2012-11'], 1, 'should have one badge');
+ t.same(stats['2012-12'], 1, 'should have one badge');
+ t.same(stats['2013-01'], 2, 'should have two badges');
+ t.end();
+ });
+ });
+
+ // necessary to stop the test runner
+ test('shutting down #', function (t) {
+ db.close(); t.end();
+ });
+})
View
17 views/admin/stats.html
@@ -0,0 +1,17 @@
+{% extends "admin/base.html" %}
+{% block main %}
+
+<h1>Statistics</h1>
+
+<ul>
+{% for stat in stats %}
+ <li>
+ <dl>
+ <dt>{{ stat.month }}</dt>
+ <dd>{{ stat.count }} badges issued</dd>
+ </dl>
+ </li>
+{% endfor %}
+</ul>
+
+{% endblock %}
Please sign in to comment.
Something went wrong with that request. Please try again.