Skip to content
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
2 changes: 1 addition & 1 deletion constants/role.constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const Constants = {
const mongoose = require("mongoose");

const accountRole = {
"_id": mongoose.Types.ObjectId(0),
"_id": mongoose.Types.ObjectId.createFromTime(0),
"name": "account",
"routes": [
Constants.Routes.authRoutes.login,
Expand Down
10 changes: 10 additions & 0 deletions constants/routes.constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,22 @@ const volunteerRoutes = {
},
};


const staffRoutes = {
"hackerStats": {
requestType: Constants.REQUEST_TYPES.GET,
uri: "/api/hacker/stats",
}
}

const allRoutes = {
"Auth": authRoutes,
"Account": accountRoutes,
"Hacker": hackerRoutes,
"Sponsor": sponsorRoutes,
"Team": teamRoutes,
"Volunteer": volunteerRoutes,
"Staff": staffRoutes,
};

/**
Expand Down Expand Up @@ -201,6 +210,7 @@ module.exports = {
sponsorRoutes: sponsorRoutes,
teamRoutes: teamRoutes,
volunteerRoutes: volunteerRoutes,
staffRoutes: staffRoutes,
allRoutes: allRoutes,
listAllRoutes: listAllRoutes,
};
11 changes: 11 additions & 0 deletions controllers/hacker.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,22 @@ function downloadedResume(req, res) {
});
}

function gotStats(req, res) {
return res.status(200).json({
message: "Retrieved stats",
data: {
stats: req.body.stats,
}
});

}

module.exports = {
updatedHacker: updatedHacker,
findById: Util.asyncMiddleware(findById),
createdHacker: createdHacker,
uploadedResume: uploadedResume,
downloadedResume: downloadedResume,
showHacker: showHacker,
gotStats: gotStats,
};
37 changes: 37 additions & 0 deletions docs/api/api_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,43 @@ define({
"filename": "routes/api/hacker.js",
"groupTitle": "Hacker"
},
{
"type": "get",
"url": "/hacker/stats",
"title": "Gets the stats of all of the hackers who have applied.",
"name": "getHackerStats",
"group": "Hacker",
"version": "0.0.9",
"success": {
"fields": {
"Success 200": [{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "message",
"description": "<p>Success message</p>"
},
{
"group": "Success 200",
"type": "object",
"optional": false,
"field": "data",
"description": "<p>Hacker object</p>"
}
]
},
"examples": [{
"title": "Success-Response: ",
"content": "{\n \"message\": \"Retrieved stats\",\n \"data\": {\n \"stats\" : {\n \"total\": 10,\n \"status\": { \"Applied\": 10 },\n \"school\": { \"McGill University\": 3, \"Harvard University\": 7 },\n degree: { \"Undergraduate\": 10 },\n gender: { \"Male\": 1, \"Female\": 9 },\n needsBus: { \"true\": 7, \"false\": 3 },\n ethnicity: { \"White\": 10, },\n jobInterest: { \"Internship\": 10 },\n major: { \"Computer Science\": 10 },\n graduationYear: { \"2019\": 10 },\n dietaryRestrictions: { \"None\": 10 },\n shirtSize: { \"M\": 3, \"XL\": 7 },\n age: { \"22\": 10 }\n }\n }\n}",
"type": "object"
}]
},
"filename": "routes/api/hacker.js",
"groupTitle": "Hacker",
"sampleRequest": [{
"url": "https://api.mchacks.ca/api/hacker/stats"
}]
},
{
"type": "patch",
"url": "/hacker/:id",
Expand Down
37 changes: 37 additions & 0 deletions docs/api/api_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,43 @@
"filename": "routes/api/hacker.js",
"groupTitle": "Hacker"
},
{
"type": "get",
"url": "/hacker/stats",
"title": "Gets the stats of all of the hackers who have applied.",
"name": "getHackerStats",
"group": "Hacker",
"version": "0.0.9",
"success": {
"fields": {
"Success 200": [{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "message",
"description": "<p>Success message</p>"
},
{
"group": "Success 200",
"type": "object",
"optional": false,
"field": "data",
"description": "<p>Hacker object</p>"
}
]
},
"examples": [{
"title": "Success-Response: ",
"content": "{\n \"message\": \"Retrieved stats\",\n \"data\": {\n \"stats\" : {\n \"total\": 10,\n \"status\": { \"Applied\": 10 },\n \"school\": { \"McGill University\": 3, \"Harvard University\": 7 },\n degree: { \"Undergraduate\": 10 },\n gender: { \"Male\": 1, \"Female\": 9 },\n needsBus: { \"true\": 7, \"false\": 3 },\n ethnicity: { \"White\": 10, },\n jobInterest: { \"Internship\": 10 },\n major: { \"Computer Science\": 10 },\n graduationYear: { \"2019\": 10 },\n dietaryRestrictions: { \"None\": 10 },\n shirtSize: { \"M\": 3, \"XL\": 7 },\n age: { \"22\": 10 }\n }\n }\n}",
"type": "object"
}]
},
"filename": "routes/api/hacker.js",
"groupTitle": "Hacker",
"sampleRequest": [{
"url": "https://api.mchacks.ca/api/hacker/stats"
}]
},
{
"type": "patch",
"url": "/hacker/:id",
Expand Down
2 changes: 1 addition & 1 deletion docs/api/api_project.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ define({
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-12-11T06:22:15.249Z",
"time": "2018-12-17T03:13:26.391Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
Expand Down
2 changes: 1 addition & 1 deletion docs/api/api_project.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-12-11T06:22:15.249Z",
"time": "2018-12-17T03:13:26.391Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
Expand Down
7 changes: 7 additions & 0 deletions middlewares/hacker.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,12 @@ async function findSelf(req, res, next) {
}
}

async function getStats(req, res, next) {
const stats = await Services.Hacker.getStats();
req.body.stats = stats;
next();
}

module.exports = {
parsePatch: parsePatch,
parseHacker: parseHacker,
Expand All @@ -480,4 +486,5 @@ module.exports = {
parseConfirmation: parseConfirmation,
createHacker: Middleware.Util.asyncMiddleware(createHacker),
findSelf: Middleware.Util.asyncMiddleware(findSelf),
getStats: Middleware.Util.asyncMiddleware(getStats)
};
13 changes: 11 additions & 2 deletions models/account.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,17 @@ AccountSchema.methods.comparePassword = function (password) {
/**
* Returns if the accountType corresponds to a sponsor
*/
AccountSchema.methods.isSponsor = function(){
AccountSchema.methods.isSponsor = function () {
return Constants.SPONSOR_TIERS.includes(this.accountType) || this.accountType == Constants.SPONSOR;
}
};
/**
* Calculates the user's age
*/
AccountSchema.methods.getAge = function () { // birthday is a date
var ageDifMs = Date.now() - this.birthDate.getTime();
var ageDate = new Date(ageDifMs); // miliseconds from epoch
return Math.abs(ageDate.getUTCFullYear() - 1970);
};

//export the model
module.exports = mongoose.model("Account", AccountSchema);
9 changes: 7 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"express-winston": "^2.5.1",
"handlebars": "^4.0.12",
"jsonwebtoken": "^8.1.0",
"memory-cache": "^0.2.0",
"mongoose": "^5.1.0",
"multer": "^1.3.1",
"passport": "^0.4.0",
Expand All @@ -47,4 +48,4 @@
"mocha": "^5.2.0",
"nodemon": "^1.17.3"
}
}
}
39 changes: 39 additions & 0 deletions routes/api/hacker.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,45 @@ module.exports = {
Middleware.Hacker.sendAppliedStatusEmail,
Controllers.Hacker.createdHacker
);

/**
* @api {get} /hacker/stats
* Gets the stats of all of the hackers who have applied.
* @apiName getHackerStats
* @apiGroup Hacker
* @apiVersion 0.0.9
* @apiSuccess {string} message Success message
* @apiSuccess {object} data Hacker object
* @apiSuccessExample {object} Success-Response:
* {
* "message": "Retrieved stats",
* "data": {
* "stats" : {
* "total": 10,
"status": { "Applied": 10 },
"school": { "McGill University": 3, "Harvard University": 7 },
degree: { "Undergraduate": 10 },
gender: { "Male": 1, "Female": 9 },
needsBus: { "true": 7, "false": 3 },
ethnicity: { "White": 10, },
jobInterest: { "Internship": 10 },
major: { "Computer Science": 10 },
graduationYear: { "2019": 10 },
dietaryRestrictions: { "None": 10 },
shirtSize: { "M": 3, "XL": 7 },
age: { "22": 10 }
}
* }
* }
*
*/
hackerRouter.route("/stats").get(
Middleware.Auth.ensureAuthenticated(),
Middleware.Auth.ensureAuthorized(),
Middleware.Hacker.getStats,
Controllers.Hacker.gotStats
);

/**
* @api {patch} /hacker/status/:id update a hacker's status
* @apiName patchHackerStatus
Expand Down
67 changes: 64 additions & 3 deletions services/hacker.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
const Hacker = require("../models/hacker.model");
const logger = require("./logger.service");

const cache = require("memory-cache");

const Constants = require("../constants/general.constant");
/**
* @function createHacker
* @param {{_id: ObjectId, accountId: ObjectId, school: string, gender: string, needsBus: boolean, application: {Object}}} hackerDetails
Expand Down Expand Up @@ -56,11 +59,10 @@ async function findIds(queries) {
const TAG = `[Hacker Service # findIds ]:`;
let ids = [];

queries.forEach(async (query) => {
for (const query of queries) {
let currId = await Hacker.findOne(query, "_id", logger.queryCallbackFactory(TAG, "hacker", query));
ids.push(currId);
});

}
return ids;
}

Expand All @@ -79,10 +81,69 @@ function findByAccountId(accountId) {
return Hacker.findOne(query, logger.updateCallbackFactory(TAG, "hacker"));
}

async function getStats() {
const TAG = `[ hacker Service # getHackerStats ]`;
const CACHE_KEY = "hackerStats";
if (cache.get(CACHE_KEY) !== null) {
logger.info(`${TAG} Getting cached stats`);
return cache.get(CACHE_KEY);
}
const allHackers = await Hacker.find({}, logger.updateCallbackFactory(TAG, "hacker")).populate({
path: "accountId",
});
const stats = {
total: 0,
status: {},
school: {},
degree: {},
gender: {},
needsBus: {},
ethnicity: {},
jobInterest: {},
major: {},
graduationYear: {},
dietaryRestrictions: {},
shirtSize: {},
age: {}
};

allHackers.forEach((hacker) => {
if (!hacker.accountId) {
// user is no longer with us for some reason :(
return;
}
stats.total += 1;
stats.status[hacker.status] = (stats.status[hacker.status]) ? stats.status[hacker.status] + 1 : 1;
stats.school[hacker.school] = (stats.school[hacker.school]) ? stats.school[hacker.school] + 1 : 1;
stats.degree[hacker.degree] = (stats.degree[hacker.degree]) ? stats.degree[hacker.degree] + 1 : 1;
stats.gender[hacker.gender] = (stats.gender[hacker.gender]) ? stats.gender[hacker.gender] + 1 : 1;
stats.needsBus[hacker.needsBus] = (stats.needsBus[hacker.needsBus]) ? stats.needsBus[hacker.needsBus] + 1 : 1;

for (const ethnicity of hacker.ethnicity) {
stats.ethnicity[ethnicity] = (stats.ethnicity[ethnicity]) ? stats.ethnicity[ethnicity] + 1 : 1;
}

stats.jobInterest[hacker.application.jobInterest] = (stats.jobInterest[hacker.application.jobInterest]) ? stats.jobInterest[hacker.application.jobInterest] + 1 : 1;
stats.major[hacker.major] = (stats.major[hacker.major]) ? stats.major[hacker.major] + 1 : 1;
stats.graduationYear[hacker.graduationYear] = (stats.graduationYear[hacker.graduationYear]) ? stats.graduationYear[hacker.graduationYear] + 1 : 1;

for (const dietaryRestrictions of hacker.accountId.dietaryRestrictions) {
stats.dietaryRestrictions[dietaryRestrictions] = (stats.dietaryRestrictions[dietaryRestrictions]) ? stats.dietaryRestrictions[dietaryRestrictions] + 1 : 1;
}
stats.shirtSize[hacker.accountId.shirtSize] = (stats.shirtSize[hacker.accountId.shirtSize]) ? stats.shirtSize[hacker.accountId.shirtSize] + 1 : 1;
const age = hacker.accountId.getAge();
stats.age[age] = (stats.age[age]) ? stats.age[age] + 1 : 1;
});
cache.put(CACHE_KEY, stats, 5 * 60 * 1000); //set a time-out of 5 minutes
return stats;
}


module.exports = {
createHacker: createHacker,
findById: findById,
updateOne: updateOne,
findIds: findIds,
findByAccountId: findByAccountId,
getStats: getStats
};
Loading