Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Implement monthly stats #56

Merged
merged 1 commit into from

2 participants

Brian J Brennan Chris McAvoy
Brian J Brennan

Issue #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.

Brian J Brennan brianloveswords 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.
568333a
Brian J Brennan

Page is super spartan:

Imgur

@cmcavoy take a look and if that's the idea, feel free to merge. We'll also have to work with @simonwex to get this deployed.

Chris McAvoy cmcavoy commented on the diff
@@ -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);
Chris McAvoy Owner
cmcavoy added a note

I'd really like to document this pattern somehow...I like it, it keeps the actual routes clean, but it took me a while to figure out. Is there a name for it? We need a Node styleguide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Chris McAvoy cmcavoy merged commit 5253e69 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 3, 2013
  1. Brian J Brennan

    Implement monthly stats [closes #55]

    brianloveswords authored
    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.
This page is out of date. Refresh to see the latest.
4 app.js
View
@@ -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);
Chris McAvoy Owner
cmcavoy added a note

I'd really like to document this pattern somehow...I like it, it keeps the actual routes clean, but it took me a while to figure out. Is there a name for it? We need a Node styleguide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+
// Badge listing
// -------------
var indexMiddleware = [badge.findAll, behavior.findAll];
21 models/stats.js
View
@@ -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;
11 routes/admin.js
View
@@ -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', {});
12 routes/stats.js
View
@@ -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();
+ });
+};
60 tests/stats-model.test.js
View
@@ -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();
+ });
+})
17 views/admin/stats.html
View
@@ -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 %}
Something went wrong with that request. Please try again.