Skip to content
This repository has been archived by the owner on Jun 21, 2019. It is now read-only.

Commit

Permalink
Merge pull request #64 from HackIllinois/staging
Browse files Browse the repository at this point in the history
Version 0.0.3
  • Loading branch information
nmagerko authored Jan 29, 2017
2 parents 7b078be + d2b31e3 commit 53223b6
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 4 deletions.
38 changes: 38 additions & 0 deletions api/v1/controllers/PermissionController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
var bodyParser = require('body-parser');
var middleware = require('../middleware');
var router = require('express').Router();
var _Promise = require('bluebird');

var errors = require('../errors');
var config = require('../../config');
var requests = require('../requests');
var roles = require('../utils/roles');

var PermissionService = require('../services/PermissionService');


function isOrganizer(req, res, next) {
var user = req.user;
PermissionService.isOrganizer(user)
.then(function (isOrganizer) {
res.body = {};
res.body.allowed = isOrganizer;

next();
return null;
})
.catch(function (error) {
next(error);
return null;
});
}

router.use(bodyParser.json());
router.use(middleware.auth);

router.get('/organizer', middleware.permission(roles.ALL), isOrganizer);

router.use(middleware.response);
router.use(middleware.errors);

module.exports.router = router;
36 changes: 36 additions & 0 deletions api/v1/controllers/StatsController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
var bodyParser = require('body-parser');
var middleware = require('../middleware');
var router = require('express').Router();
var _Promise = require('bluebird');

var errors = require('../errors');
var config = require('../../config');
var requests = require('../requests');
var roles = require('../utils/roles');

var StatsService = require('../services/StatsService');

function getStats(req, res, next) {
StatsService.fetchStats()
.then(function (stats) {
res.body = stats;

next();
return null;
})
.catch(function (error) {
next(error);
return null;
});
}


router.use(bodyParser.json());
router.use(middleware.auth);

router.get('/', middleware.permission(roles.ORGANIZERS), getStats);

router.use(middleware.response);
router.use(middleware.errors);

module.exports.router = router;
4 changes: 3 additions & 1 deletion api/v1/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module.exports = {
UploadController: require('./UploadController.js'),
UserController: require('./UserController.js'),
RegistrationController: require('./RegistrationController.js'),
PermissionController: require('./PermissionController.js'),
ProjectController: require('./ProjectController.js'),
HealthController: require('./HealthController.js')
HealthController: require('./HealthController.js'),
StatsController: require('./StatsController.js')
};
2 changes: 2 additions & 0 deletions api/v1/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ v1.use('/auth', controllers.AuthController.router);
v1.use('/user', controllers.UserController.router);
v1.use('/upload', controllers.UploadController.router);
v1.use('/registration', controllers.RegistrationController.router);
v1.use('/permission', controllers.PermissionController.router);
v1.use('/project', controllers.ProjectController.router);
v1.use('/ecosystem', controllers.EcosystemController.router);
v1.use('/health', controllers.HealthController.router);
v1.use('/stats', controllers.StatsController.router);

// log any outgoing response for debugging
v1.use(function (req, res, next) {
Expand Down
22 changes: 21 additions & 1 deletion api/v1/services/PermissionService.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,24 @@ module.exports.canCreateProject = function (creator) {

var message = "A project cannot be created with the provided credentials";
return _Promise.reject(new errors.UnauthorizedError(message));
}
};


/**
* Checks to see if a requestor is an organizer (or superuser)
* @param {User} user to check roles for
* @return {Promise} resolving to true if the user is an organizer, false otherwise
*/
module.exports.isOrganizer = function (user){
if (user.hasRole(roles.SUPERUSER)) {
// the superuser is allowed
return _Promise.resolve(true);
}
if (user.hasRoles(roles.ORGANIZERS)) {
// the user is an organizer
return _Promise.resolve(true);
}
// return false
return _Promise.resolve(false);

}
144 changes: 144 additions & 0 deletions api/v1/services/StatsService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
var _Promise = require('bluebird');
var _ = require('lodash');

var database = require('../../database');

var Attendee = require('../models/Attendee');
var AttendeeEcosystemInterest = require('../models/AttendeeEcosystemInterest')
var Ecosystem = require('../models/Ecosystem');
var User = require('../models/User');

var errors = require('../errors');
var utils = require('../utils');
var cache = utils.cache;

/**
* Returns a function that takes a query result and populates a stats object
* @param {String} key the key to use to nest the stats
* @param {Object} stats the stats object to be populated
* @return {Function} The generated function
*/
function _populateStats(key, stats){
return function(result){
stats[key] = {};
_.forEach(result.models, function(model){
stats[key][model.attributes.name] = model.attributes.count;
});
};
}

/**
* Returns a function that takes a query result and populates a stats object
* Differs from above in that it doesn't process a collection
* @param {String} key the key to use to map a count result
* @param {Object} stats the stats object to be populated
* @return {Function} The generated function
*/
function _populateStatsField(key, stats){
return function(result){
stats[key] = result.attributes.count;
};
}

/**
* Queries Attendee ecosystems interests and performs a callback on the results
* @param {Function} cb the function to process the query results with
* @return {Promise} resolving to the return value of the callback
*/
function _populateEcosystems(cb){
return AttendeeEcosystemInterest.query(function(qb){
qb.select('e.name').count('ecosystem_id as count').from('attendee_ecosystem_interests as aei').innerJoin('ecosystems as e', 'e.id', 'aei.ecosystem_id').groupBy('aei.ecosystem_id');
})
.fetchAll()
.then(cb);
}

/**
* Queries an attendee attribute and counts the unique entries
* @param {String} attribute the attribute to query for
* @param {Function} cb the function to process the query results with
* @return {Promise} resolving to the return value of the callback
*/
function _populateAttendeeAttribute(attribute, cb){
return Attendee.query(function(qb){
qb.select(attribute + ' as name').count(attribute + ' as count').from('attendees').groupBy(attribute);
})
.fetchAll()
.then(cb);
}

/**
* Queries the total number of attendees
* @param {Function} cb the function to process the query results with
* @return {Promise} resolving to the return value of the callback
*/
function _populateAttendees(cb){
return Attendee.query(function(qb){
qb.count('id as count');
})
.fetch()
.then(cb);
}

/**
* Fetches the current stats, requerying them if not cached
* @return {Promise<Object>} resolving to key-value pairs of stats
*/
module.exports.fetchStats = function () {
return cache.hasKey('stats')
.then(function(hasKey){
if(hasKey) {
return cache.getString('stats')
.then(function(object){
return JSON.parse(object);
});
}
else {
var stats = {}
var queries = [];

var ecosystemsQuery = _populateEcosystems(_populateStats('ecosystems', stats));
queries.push(ecosystemsQuery);

var schoolQuery = _populateAttendeeAttribute('school', _populateStats('school', stats));
queries.push(schoolQuery);

var transportationQuery = _populateAttendeeAttribute('transportation', _populateStats('transportation', stats));
queries.push(transportationQuery);

var dietQuery = _populateAttendeeAttribute('diet', _populateStats('diet', stats));
queries.push(dietQuery);

var shirtSizeQuery = _populateAttendeeAttribute('shirt_size', _populateStats('shirtSize', stats));
queries.push(shirtSizeQuery);

var genderQuery = _populateAttendeeAttribute('gender', _populateStats('gender', stats));
queries.push(genderQuery);

var graduationYearQuery = _populateAttendeeAttribute('graduation_year', _populateStats('graduationYear', stats));
queries.push(graduationYearQuery);

var majorQuery = _populateAttendeeAttribute('major', _populateStats('major', stats));
queries.push(majorQuery);

var isNoviceQuery = _populateAttendeeAttribute('is_novice', _populateStats('isNovice', stats));
queries.push(isNoviceQuery);

var attendeeQuery = _populateAttendees(_populateStatsField('attendees', stats));
queries.push(attendeeQuery);

return _Promise.all(queries)
.then(function(){
return cache.storeString('stats', JSON.stringify(stats))
.then(function(){
var tenMinutesFromNow = (10*60);
return cache.expireKey('stats', tenMinutesFromNow)
.then(function(){
return stats;
});
});
});
}
});

};
5 changes: 3 additions & 2 deletions api/v1/services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module.exports = {
PermissionService: require('./PermissionService'),
ProjectService: require('./ProjectService'),
RegistrationService: require('./RegistrationService'),
StatsService: require('./StatsService'),
StorageService: require('./StorageService'),
UserService: require('./UserService'),
TokenService: require('./TokenService')
TokenService: require('./TokenService'),
UserService: require('./UserService')
};

0 comments on commit 53223b6

Please sign in to comment.