From 0a07c1e3972798ad54696481372707bfeaccdcb9 Mon Sep 17 00:00:00 2001 From: Nathan Leniz Date: Sat, 28 Mar 2015 17:38:11 +0900 Subject: [PATCH] Streaks! --- controllers/courseware.js | 585 +++++++++++++++--------------- controllers/resources.js | 722 +++++++++++++++++++------------------- controllers/user.js | 77 ++-- models/User.js | 29 +- 4 files changed, 721 insertions(+), 692 deletions(-) diff --git a/controllers/courseware.js b/controllers/courseware.js index 0d40f37c8e..b4a08a7e1d 100644 --- a/controllers/courseware.js +++ b/controllers/courseware.js @@ -1,351 +1,356 @@ var _ = require('lodash'), - debug = require('debug')('freecc:cntr:courseware'), - Courseware = require('./../models/Courseware'), - User = require('./../models/User'), - resources = require('./resources'), - R = require('ramda'); + debug = require('debug')('freecc:cntr:courseware'), + Courseware = require('./../models/Courseware'), + User = require('./../models/User'), + resources = require('./resources'), + R = require('ramda'), + moment = require('moment'); /** * Courseware controller */ exports.showAllCoursewares = function(req, res) { - var completedCoursewares = req.user.completedCoursewares.map(function(elem) { - return elem._id; - }); + var completedCoursewares = req.user.completedCoursewares.map(function(elem) { + return elem._id; + }); - var noDuplicatedCoursewares = R.uniq(completedCoursewares); - var data = {}; - data.coursewareList = resources.allCoursewareNames(); - data.completedList = noDuplicatedCoursewares; - res.send(data); + var noDuplicatedCoursewares = R.uniq(completedCoursewares); + var data = {}; + data.coursewareList = resources.allCoursewareNames(); + data.completedList = noDuplicatedCoursewares; + res.send(data); }; exports.returnNextCourseware = function(req, res) { - if (!req.user) { - return res.redirect('../challenges/learn-how-free-code-camp-works'); + if (!req.user) { + return res.redirect('../challenges/learn-how-free-code-camp-works'); + } + var completed = req.user.completedCoursewares.map(function (elem) { + return elem._id; + }); + + req.user.uncompletedCoursewares = resources.allCoursewareIds().filter(function (elem) { + if (completed.indexOf(elem) === -1) { + return elem; } - var completed = req.user.completedCoursewares.map(function (elem) { - return elem._id; - }); + }); + req.user.save(); - req.user.uncompletedCoursewares = resources.allCoursewareIds().filter(function (elem) { - if (completed.indexOf(elem) === -1) { - return elem; - } - }); - req.user.save(); - - var uncompletedCoursewares = req.user.uncompletedCoursewares.shift(); + var uncompletedCoursewares = req.user.uncompletedCoursewares.shift(); - var displayedCoursewares = Courseware.find({'_id': uncompletedCoursewares}); - displayedCoursewares.exec(function(err, courseware) { - if (err) { - next(err); - } + var displayedCoursewares = Courseware.find({'_id': uncompletedCoursewares}); + displayedCoursewares.exec(function(err, courseware) { + if (err) { + next(err); + } - courseware = courseware.pop(); - if (courseware === undefined) { - req.flash('errors', { - msg: "It looks like you've completed all the courses we have available. Good job!" - }); - return res.redirect('../challenges/learn-how-free-code-camp-works'); - } - nameString = courseware.name.toLowerCase().replace(/\s/g, '-'); - return res.redirect('../challenges/' + nameString); - }); + courseware = courseware.pop(); + if (courseware === undefined) { + req.flash('errors', { + msg: "It looks like you've completed all the courses we have available. Good job!" + }); + return res.redirect('../challenges/learn-how-free-code-camp-works'); + } + nameString = courseware.name.toLowerCase().replace(/\s/g, '-'); + return res.redirect('../challenges/' + nameString); + }); }; exports.returnIndividualCourseware = function(req, res, next) { - var dashedName = req.params.coursewareName; + var dashedName = req.params.coursewareName; - coursewareName = dashedName.replace(/\-/g, ' '); + coursewareName = dashedName.replace(/\-/g, ' '); - Courseware.find({"name" : new RegExp(coursewareName, 'i')}, function(err, courseware) { - if (err) { - next(err); - } - // Handle not found - if (courseware.length < 1) { - req.flash('errors', { - msg: "404: We couldn't find a challenge with that name. Please double check the name." + Courseware.find({"name" : new RegExp(coursewareName, 'i')}, function(err, courseware) { + if (err) { + next(err); + } + // Handle not found + if (courseware.length < 1) { + req.flash('errors', { + msg: "404: We couldn't find a challenge with that name. Please double check the name." + }); + return res.redirect('/challenges'); + } + courseware = courseware.pop(); + + // Redirect to full name if the user only entered a partial + var dashedNameFull = courseware.name.toLowerCase().replace(/\s/g, '-'); + if (dashedNameFull != dashedName) { + return res.redirect('../challenges/' + dashedNameFull); + } + + var challengeType = { + 0 : function() { + res.render('coursewares/showHTML', { + title: courseware.name, + dashedName: dashedName, + name: courseware.name, + brief: courseware.description[0], + details: courseware.description.slice(1), + tests: courseware.tests, + challengeSeed: courseware.challengeSeed, + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + coursewareHash: courseware._id, + environment: resources.whichEnvironment() }); - return res.redirect('/challenges'); - } - courseware = courseware.pop(); + }, - // Redirect to full name if the user only entered a partial - var dashedNameFull = courseware.name.toLowerCase().replace(/\s/g, '-'); - if (dashedNameFull != dashedName) { - return res.redirect('../challenges/' + dashedNameFull); - } + 1 : function() { + res.render('coursewares/showJS', { + title: courseware.name, + dashedName: dashedName, + name: courseware.name, + brief: courseware.description[0], + details: courseware.description.slice(1), + tests: courseware.tests, + challengeSeed: courseware.challengeSeed, + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + coursewareHash: courseware._id, - var challengeType = { - 0 : function() { - res.render('coursewares/showHTML', { - title: courseware.name, - dashedName: dashedName, - name: courseware.name, - brief: courseware.description[0], - details: courseware.description.slice(1), - tests: courseware.tests, - challengeSeed: courseware.challengeSeed, - verb: resources.randomVerb(), - phrase: resources.randomPhrase(), - compliment: resources.randomCompliment(), - coursewareHash: courseware._id, - environment: resources.whichEnvironment() - }); - }, + }); + }, - 1 : function() { - res.render('coursewares/showJS', { - title: courseware.name, - dashedName: dashedName, - name: courseware.name, - brief: courseware.description[0], - details: courseware.description.slice(1), - tests: courseware.tests, - challengeSeed: courseware.challengeSeed, - verb: resources.randomVerb(), - phrase: resources.randomPhrase(), - compliment: resources.randomCompliment(), - coursewareHash: courseware._id, + 2: function() { + res.render('coursewares/showVideo', { + title: courseware.name, + dashedName: dashedName, + name: courseware.name, + details: courseware.description, + tests: courseware.tests, + video: courseware.challengeSeed[0], + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + coursewareHash: courseware._id, + challengeType: 'video' + }); + }, - }); - }, + 3: function() { + res.render('coursewares/showVideo', { + title: courseware.name, + dashedName: dashedName, + name: courseware.name, + details: courseware.description, + tests: courseware.tests, + video: courseware.challengeSeed[0], + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + coursewareHash: courseware._id, + challengeType: 'zipline' + }); + }, - 2: function() { - res.render('coursewares/showVideo', { - title: courseware.name, - dashedName: dashedName, - name: courseware.name, - details: courseware.description, - tests: courseware.tests, - video: courseware.challengeSeed[0], - verb: resources.randomVerb(), - phrase: resources.randomPhrase(), - compliment: resources.randomCompliment(), - coursewareHash: courseware._id, - challengeType: 'video' - }); - }, + 4: function() { + res.render('coursewares/showVideo', { + title: courseware.name, + dashedName: dashedName, + name: courseware.name, + details: courseware.description, + tests: courseware.tests, + video: courseware.challengeSeed[0], + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + coursewareHash: courseware._id, + challengeType: 'basejump' + }); + } + }; - 3: function() { - res.render('coursewares/showVideo', { - title: courseware.name, - dashedName: dashedName, - name: courseware.name, - details: courseware.description, - tests: courseware.tests, - video: courseware.challengeSeed[0], - verb: resources.randomVerb(), - phrase: resources.randomPhrase(), - compliment: resources.randomCompliment(), - coursewareHash: courseware._id, - challengeType: 'zipline' - }); - }, + return challengeType[courseware.challengeType](); - 4: function() { - res.render('coursewares/showVideo', { - title: courseware.name, - dashedName: dashedName, - name: courseware.name, - details: courseware.description, - tests: courseware.tests, - video: courseware.challengeSeed[0], - verb: resources.randomVerb(), - phrase: resources.randomPhrase(), - compliment: resources.randomCompliment(), - coursewareHash: courseware._id, - challengeType: 'basejump' - }); - } - }; - - return challengeType[courseware.challengeType](); - - }); + }); }; exports.testCourseware = function(req, res) { - var coursewareName = req.body.name, - coursewareTests = req.body.tests, - coursewareDifficulty = req.body.difficulty, - coursewareDescription = req.body.description, - coursewareEntryPoint = req.body.challengeEntryPoint, - coursewareChallengeSeed = req.body.challengeSeed; - coursewareTests = coursewareTests.split('\r\n'); - coursewareDescription = coursewareDescription.split('\r\n'); - coursewareTests.filter(getRidOfEmpties); - coursewareDescription.filter(getRidOfEmpties); - coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', ''); - res.render('courseware/show', { - completedWith: null, - title: coursewareName, - name: coursewareName, - difficulty: +coursewareDifficulty, - brief: coursewareDescription[0], - details: coursewareDescription.slice(1), - tests: coursewareTests, - challengeSeed: coursewareChallengeSeed, - challengeEntryPoint: coursewareEntryPoint, - cc: req.user ? req.user.coursewaresHash : undefined, - progressTimestamps: req.user ? req.user.progressTimestamps : undefined, - verb: resources.randomVerb(), - phrase: resources.randomPhrase(), - compliment: resources.randomCompliment(), - coursewares: [], - coursewareHash: "test" - }); + var coursewareName = req.body.name, + coursewareTests = req.body.tests, + coursewareDifficulty = req.body.difficulty, + coursewareDescription = req.body.description, + coursewareEntryPoint = req.body.challengeEntryPoint, + coursewareChallengeSeed = req.body.challengeSeed; + coursewareTests = coursewareTests.split('\r\n'); + coursewareDescription = coursewareDescription.split('\r\n'); + coursewareTests.filter(getRidOfEmpties); + coursewareDescription.filter(getRidOfEmpties); + coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', ''); + res.render('courseware/show', { + completedWith: null, + title: coursewareName, + name: coursewareName, + difficulty: +coursewareDifficulty, + brief: coursewareDescription[0], + details: coursewareDescription.slice(1), + tests: coursewareTests, + challengeSeed: coursewareChallengeSeed, + challengeEntryPoint: coursewareEntryPoint, + cc: req.user ? req.user.coursewaresHash : undefined, + progressTimestamps: req.user ? req.user.progressTimestamps : undefined, + verb: resources.randomVerb(), + phrase: resources.randomPhrase(), + compliment: resources.randomCompliment(), + coursewares: [], + coursewareHash: "test" + }); }; function getRidOfEmpties(elem) { - if (elem.length > 0) { - return elem; - } + if (elem.length > 0) { + return elem; + } }; exports.publicGenerator = function(req, res) { - res.render('courseware/public-generator'); + res.render('courseware/public-generator'); }; exports.generateChallenge = function(req, res) { - var coursewareName = req.body.name, - coursewareTests = req.body.tests, - coursewareDifficulty = req.body.difficulty, - coursewareDescription = req.body.description, - coursewareEntryPoint = req.body.challengeEntryPoint, - coursewareChallengeSeed = req.body.challengeSeed; - coursewareTests = coursewareTests.split('\r\n'); - coursewareDescription = coursewareDescription.split('\r\n'); - coursewareTests.filter(getRidOfEmpties); - coursewareDescription.filter(getRidOfEmpties); - coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', ''); + var coursewareName = req.body.name, + coursewareTests = req.body.tests, + coursewareDifficulty = req.body.difficulty, + coursewareDescription = req.body.description, + coursewareEntryPoint = req.body.challengeEntryPoint, + coursewareChallengeSeed = req.body.challengeSeed; + coursewareTests = coursewareTests.split('\r\n'); + coursewareDescription = coursewareDescription.split('\r\n'); + coursewareTests.filter(getRidOfEmpties); + coursewareDescription.filter(getRidOfEmpties); + coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', ''); - var response = { - _id: randomString(), - name: coursewareName, - difficulty: coursewareDifficulty, - description: coursewareDescription, - challengeEntryPoint: coursewareEntryPoint, - challengeSeed: coursewareChallengeSeed, - tests: coursewareTests - }; - res.send(response); + var response = { + _id: randomString(), + name: coursewareName, + difficulty: coursewareDifficulty, + description: coursewareDescription, + challengeEntryPoint: coursewareEntryPoint, + challengeSeed: coursewareChallengeSeed, + tests: coursewareTests + }; + res.send(response); }; exports.completedCourseware = function (req, res, next) { - var isCompletedDate = Math.round(+new Date()); - var coursewareHash = req.body.coursewareInfo.coursewareHash; + var isCompletedDate = Math.round(+new Date()); + var coursewareHash = req.body.coursewareInfo.coursewareHash; - debug('this is the coursewarehash we got', coursewareHash); + debug('this is the coursewarehash we got', coursewareHash); - req.user.completedCoursewares.push({ - _id: coursewareHash, - completedDate: isCompletedDate, - name: req.body.coursewareInfo.coursewareName - }); + req.user.completedCoursewares.push({ + _id: coursewareHash, + completedDate: isCompletedDate, + name: req.body.coursewareInfo.coursewareName + }); - var index = req.user.completedCoursewares.indexOf(coursewareHash); + var index = req.user.completedCoursewares.indexOf(coursewareHash); - if (index === -1) { + if (index === -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedCoursewares.splice(index, 1); + req.user.progressTimestamps.push(Date.now() || 0); + req.user.uncompletedCoursewares.splice(index, 1); + } + + req.user.save(function (err, user, next) { + if (err) { + return next(err); } - - req.user.save(function (err, user, next) { - if (err) { - return next(err); - } - if (user) { - res.send(true); - } - }); + if (user) { + res.send(true); + } + }); }; exports.completedZiplineOrBasejump = function (req, res, next) { - var isCompletedWith = req.body.bonfireInfo.completedWith || undefined; - var isCompletedDate = Math.round(+new Date()); - var coursewareHash = req.body.coursewareInfo.coursewareHash; - var solutionLink = req.body.coursewareInfo.solutionLink; - if(!solutionLink) { - // flash error and redirect - } + var isCompletedWith = req.body.bonfireInfo.completedWith || false; + var isCompletedDate = Math.round(+new Date()); + var coursewareHash = req.body.coursewareInfo.coursewareHash; + var solutionLink = req.body.coursewareInfo.solutionLink; + if (!solutionLink) { + // flash error and redirect + return next(new Error('No solution provided')); + } - if (isCompletedWith) { - var paired = User.find({"profile.username": isCompletedWith.toLowerCase()}).limit(1); - paired.exec(function (err, pairedWith) { - if (err) { - return err; - } else { - var index = req.user.uncompletedBonfires.indexOf(bonfireHash); - if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedBonfires.splice(index, 1) - } - pairedWith = pairedWith.pop(); - - index = pairedWith.uncompletedBonfires.indexOf(bonfireHash); - if (index > -1) { - pairedWith.progressTimestamps.push(Date.now() || 0); - pairedWith.uncompletedBonfires.splice(index, 1); - - } - - pairedWith.completedBonfires.push({ - _id: bonfireHash, - completedWith: req.user._id, - completedDate: isCompletedDate, - solution: isSolution - }); - - req.user.completedBonfires.push({ - _id: bonfireHash, - completedWith: pairedWith._id, - completedDate: isCompletedDate, - solution: isSolution - }); - - req.user.save(function (err, user) { - pairedWith.save(function (err, paired) { - if (err) { - throw err; - } - if (user && paired) { - res.send(true); - } - }) - }); - } - }) - } else { - - req.user.completedBonfires.push({ - _id: bonfireHash, - completedWith: null, - completedDate: isCompletedDate, - solution: isSolution - }); - - var index = req.user.uncompletedCourse.indexOf(bonfireHash); + if (isCompletedWith) { + var paired = User.find({'profile.username': isCompletedWith.toLowerCase()}).limit(1); + paired.exec(function (err, pairedWith) { + if (err) { + return next(err); + } else { + var index = req.user.uncompletedCoursewares.indexOf(coursewareHash); if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); - req.user.uncompletedBonfires.splice(index, 1) + req.user.progressTimestamps.push(Date.now() || 0); + req.user.uncompletedCoursewares.splice(index, 1); + } + pairedWith = pairedWith.pop(); + + index = pairedWith.uncompletedCoursewares.indexOf(coursewareHash); + if (index > -1) { + pairedWith.progressTimestamps.push(Date.now() || 0); + pairedWith.uncompletedCoursewares.splice(index, 1); + } - req.user.save(function (err, user) { - if (err) { - throw err; - } - if (user) { - debug('Saving user'); - res.send(true) - } + pairedWith.completedCoursewares.push({ + _id: coursewareHash, + completedWith: req.user._id, + completedDate: isCompletedDate, + solution: solutionLink }); + + req.user.completedCoursewares.push({ + _id: coursewareHash, + completedWith: pairedWith._id, + completedDate: isCompletedDate, + solution: solutionLink + }); + + req.user.save(function (err, user) { + if (err) { + return next(err); + } + pairedWith.save(function (err, paired) { + if (err) { + return next(err); + } + if (user && paired) { + return res.send(true); + } + }); + }); + } + }); + } else { + + req.user.completedCoursewares.push({ + _id: coursewareHash, + completedWith: null, + completedDate: isCompletedDate, + solution: solutionLink + }); + + var index = req.user.uncompletedCourse.indexOf(coursewareHash); + if (index > -1) { + req.user.progressTimestamps.push(Date.now() || 0); + req.user.uncompletedCoursewares.splice(index, 1); } -}; \ No newline at end of file + + req.user.save(function (err, user) { + if (err) { + return next(err); + } + if (user) { + debug('Saving user'); + return res.send(true); + } + }); + } +}; diff --git a/controllers/resources.js b/controllers/resources.js index b430db79cd..ff66bfeb7e 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -1,20 +1,20 @@ var async = require('async'), - User = require('../models/User'), - Challenge = require('./../models/Challenge'), - Bonfire = require('./../models/Bonfire'), - Story = require('./../models/Story'), - Comment = require('./../models/Comment'), - resources = require('./resources.json'), - steps = resources.steps, - secrets = require('./../config/secrets'), - bonfires = require('../seed_data/bonfires.json'), - coursewares = require('../seed_data/coursewares.json'), - moment = require('moment'), - https = require('https'), - debug = require('debug')('freecc:cntr:resources'), - cheerio = require('cheerio'), - request = require('request'), - R = require('ramda'); + User = require('../models/User'), + Challenge = require('./../models/Challenge'), + Bonfire = require('./../models/Bonfire'), + Story = require('./../models/Story'), + Comment = require('./../models/Comment'), + resources = require('./resources.json'), + steps = resources.steps, + secrets = require('./../config/secrets'), + bonfires = require('../seed_data/bonfires.json'), + coursewares = require('../seed_data/coursewares.json'), + moment = require('moment'), + https = require('https'), + debug = require('debug')('freecc:cntr:resources'), + cheerio = require('cheerio'), + request = require('request'), + R = require('ramda'); /** * GET / @@ -26,371 +26,381 @@ Array.zip = function(left, right, combinerFunction) { results = []; for (counter = 0; counter < Math.min(left.length, right.length); counter++) { - results.push(combinerFunction(left[counter],right[counter])); + results.push(combinerFunction(left[counter], right[counter])); } return results; }; module.exports = { - privacy: function privacy(req, res) { - res.render('resources/privacy', { - title: 'Privacy' - }); - }, + privacy: function privacy(req, res) { + res.render('resources/privacy', { + title: 'Privacy' + }); + }, - sitemap: function sitemap(req, res, next) { - var appUrl = 'http://www.freecodecamp.com'; - var now = moment(new Date()).format('YYYY-MM-DD'); + sitemap: function sitemap(req, res, next) { + var appUrl = 'http://www.freecodecamp.com'; + var now = moment(new Date()).format('YYYY-MM-DD'); - User.find({'profile.username': {'$ne': '' }}, function(err, users) { + User.find({'profile.username': {'$ne': '' }}, function(err, users) { + if (err) { + debug('User err: ', err); + return next(err); + } + Challenge.find({}, function (err, challenges) { + if (err) { + debug('User err: ', err); + return next(err); + } + Bonfire.find({}, function (err, bonfires) { + if (err) { + debug('User err: ', err); + return next(err); + } + Story.find({}, function (err, stories) { if (err) { - debug('User err: ', err); - return next(err); + debug('User err: ', err); + return next(err); } - Challenge.find({}, function (err, challenges) { - if (err) { - debug('User err: ', err); - return next(err); - } - Bonfire.find({}, function (err, bonfires) { - if (err) { - debug('User err: ', err); - return next(err); - } - Story.find({}, function (err, stories) { - if (err) { - debug('User err: ', err); - return next(err); - } - res.header('Content-Type', 'application/xml'); - res.render('resources/sitemap', { - appUrl: appUrl, - now: now, - users: users, - challenges: challenges, - bonfires: bonfires, - stories: stories - }); - }); - }); + res.header('Content-Type', 'application/xml'); + res.render('resources/sitemap', { + appUrl: appUrl, + now: now, + users: users, + challenges: challenges, + bonfires: bonfires, + stories: stories }); + }); }); - }, + }); + }); + }, - deployAWebsite: function deployAWebsite(req, res) { - res.render('resources/deploy-a-website', { - title: 'Deploy a Dynamic Website in 7 Minutes' - }); - }, + deployAWebsite: function deployAWebsite(req, res) { + res.render('resources/deploy-a-website', { + title: 'Deploy a Dynamic Website in 7 Minutes' + }); + }, - chat: function chat(req, res) { - res.render('resources/chat', { - title: "Enter Free Code Camp's Chat Rooms" - }); - }, + chat: function chat(req, res) { + res.render('resources/chat', { + title: "Enter Free Code Camp's Chat Rooms" + }); + }, - nonprofitProjectInstructions: function nonprofitProjectInstructions(req, res) { - res.render('resources/nonprofit-project-instructions', { - title: 'Nonprofit Project Instructions' - }); - }, + nonprofitProjectInstructions: function nonprofitProjectInstructions(req, res) { + res.render('resources/nonprofit-project-instructions', { + title: 'Nonprofit Project Instructions' + }); + }, - gmailShortcuts: function gmailShortcuts(req, res) { - res.render('resources/gmail-shortcuts', { - title: 'These Gmail Shortcuts will save you Hours' - }); - }, + gmailShortcuts: function gmailShortcuts(req, res) { + res.render('resources/gmail-shortcuts', { + title: 'These Gmail Shortcuts will save you Hours' + }); + }, - guideToOurNonprofitProjects: function guideToOurNonprofitProjects(req, res) { - res.render('resources/guide-to-our-nonprofit-projects', { - title: 'A guide to our Nonprofit Projects' - }); - }, + guideToOurNonprofitProjects: function guideToOurNonprofitProjects(req, res) { + res.render('resources/guide-to-our-nonprofit-projects', { + title: 'A guide to our Nonprofit Projects' + }); + }, - controlShortcuts: function controlShortcuts(req, res) { - res.render('resources/control-shortcuts', { - title: 'These Control Shortcuts will save you Hours' - }); - }, + controlShortcuts: function controlShortcuts(req, res) { + res.render('resources/control-shortcuts', { + title: 'These Control Shortcuts will save you Hours' + }); + }, - chromebook: function chromebook(req, res) { - res.render('resources/chromebook', { - title: 'Win a Chromebook' - }); - }, + chromebook: function chromebook(req, res) { + res.render('resources/chromebook', { + title: 'Win a Chromebook' + }); + }, - jqueryExercises: function jqueryExercises(req, res) { - res.render('resources/jquery-exercises', { - title: 'jQuery Exercises' - }); - }, + jqueryExercises: function jqueryExercises(req, res) { + res.render('resources/jquery-exercises', { + title: 'jQuery Exercises' + }); + }, - livePairProgramming: function(req, res) { - res.render('resources/live-pair-programming', { - title: 'Live Pair Programming' - }); - }, + livePairProgramming: function(req, res) { + res.render('resources/live-pair-programming', { + title: 'Live Pair Programming' + }); + }, - installScreenHero: function(req, res) { - res.render('resources/install-screenhero', { - title: 'Install ScreenHero' - }); - }, + installScreenHero: function(req, res) { + res.render('resources/install-screenhero', { + title: 'Install ScreenHero' + }); + }, - javaScriptInYourInbox: function(req, res) { - res.render('resources/javascript-in-your-inbox', { - title: 'JavaScript in your Inbox' - }); - }, + javaScriptInYourInbox: function(req, res) { + res.render('resources/javascript-in-your-inbox', { + title: 'JavaScript in your Inbox' + }); + }, - nodeSchoolChallenges: function(req, res) { - res.render('resources/nodeschool-challenges', { - title: 'NodeSchool Challenges' - }); - }, + nodeSchoolChallenges: function(req, res) { + res.render('resources/nodeschool-challenges', { + title: 'NodeSchool Challenges' + }); + }, - githubCalls: function(req, res) { - var githubHeaders = {headers: {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36'}, port:80 }; - request('https://api.github.com/repos/freecodecamp/freecodecamp/pulls?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function(err, status1, pulls) { - pulls = pulls ? Object.keys(JSON.parse(pulls)).length : "Can't connect to github"; - request('https://api.github.com/repos/freecodecamp/freecodecamp/issues?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function (err, status2, issues) { - issues = ((pulls === parseInt(pulls)) && issues) ? Object.keys(JSON.parse(issues)).length - pulls : "Can't connect to GitHub"; - res.send({"issues": issues, "pulls" : pulls}); - }); - }); - }, + githubCalls: function(req, res) { + var githubHeaders = {headers: {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36'}, port:80 }; + request('https://api.github.com/repos/freecodecamp/freecodecamp/pulls?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function(err, status1, pulls) { + pulls = pulls ? Object.keys(JSON.parse(pulls)).length : "Can't connect to github"; + request('https://api.github.com/repos/freecodecamp/freecodecamp/issues?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function (err, status2, issues) { + issues = ((pulls === parseInt(pulls)) && issues) ? Object.keys(JSON.parse(issues)).length - pulls : "Can't connect to GitHub"; + res.send({"issues": issues, "pulls" : pulls}); + }); + }); + }, - about: function(req, res, next) { - if (req.user) { - if (!req.user.profile.picture || req.user.profile.picture === "https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png") { - req.user.profile.picture = "https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png"; - req.user.save(); - } - } - var date1 = new Date('10/15/2014'); - var date2 = new Date(); - var progressTimestamps = req.user.progressTimestamps; - var now = Date.now() || 0; - - if (req.user.pointsNeedMigration) { - var challengesHash = req.user.challengesHash; - for (var key in challengesHash) { - if (challengesHash[key] > 0) { - req.user.progressTimestamps.push(challengesHash[key]); - } - } - - var timeStamps = []; - R.keys(req.user.challengesHash).forEach(function(key) { - "use strict"; - var timeStamp = parseInt(challengesHash[key], 10); - timeStamps.push({timeStamp: timeStamp.length !== 13 ? (+timeStamp) : (+timeStamp * 1000)}); - }); - - req.user.completedCoursewares = Array.zip(timeStamps, coursewares, - function(left, right) { - "use strict"; - return ({ - completedDate: left.timeStamp, - _id: right._id, - name: right.name - }); - }).filter(function(elem) { - "use strict"; - return elem.completedDate !== 0; - }); - req.user.pointsNeedMigration = false; - req.user.save(); - } - if (progressTimestamps[progressTimestamps.length - 1] <= (now - 43200)) { - req.user.progressTimestamps.push(now); - } - var timeDiff = Math.abs(date2.getTime() - date1.getTime()); - var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24)); - var announcements = resources.announcements; - function numberWithCommas(x) { - return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); - } - User.count({}, function (err, c3) { - if (err) { - debug('User err: ', err); - return next(err); - } - User.count({'points': {'$gt': 53}}, function (err, all) { - if (err) { - debug('User err: ', err); - return next(err); - } - - res.render('resources/learn-to-code', { - title: 'About Free Code Camp and Our Team of Volunteers', - daysRunning: daysRunning, - c3: numberWithCommas(c3), - all: all, - announcements: announcements - }); - }); - }); - }, - - randomPhrase: function() { - var phrases = resources.phrases; - return phrases[Math.floor(Math.random() * phrases.length)]; - }, - - randomVerb: function() { - var verbs = resources.verbs; - return verbs[Math.floor(Math.random() * verbs.length)]; - }, - - randomCompliment: function() { - var compliments = resources.compliments; - return compliments[Math.floor(Math.random() * compliments.length)]; - }, - - allBonfireIds: function() { - return bonfires.map(function(elem) { - return { - _id: elem._id, - difficulty: elem.difficulty - } - }) - .sort(function(a, b) { - return a.difficulty - b.difficulty; - }) - .map(function(elem) { - return elem._id; - }); - }, - allBonfireNames: function() { - return bonfires.map(function(elem) { - return { - name: elem.name, - difficulty: elem.difficulty, - _id: elem._id - } - }) - .sort(function(a, b) { - return a.difficulty - b.difficulty; - }) - .map (function(elem) { - return { - name : elem.name, - _id: elem._id - } - }); - }, - - getAllCourses: function() { - "use strict"; - return coursewares; - }, - - allCoursewareIds: function() { - return coursewares.map(function(elem) { - return { - _id: elem._id, - difficulty: elem.difficulty - }; - }) - .sort(function(a, b) { - return a.difficulty - b.difficulty; - }) - .map(function(elem) { - return elem._id; - }); - }, - allCoursewareNames: function() { - return coursewares.map(function(elem) { - return { - name: elem.name, - difficulty: elem.difficulty, - _id: elem._id - }; - }) - .sort(function(a, b) { - return a.difficulty - b.difficulty; - }) - .map (function(elem) { - return { - name: elem.name, - _id: elem._id - }; - }); - }, - whichEnvironment: function() { - return process.env.NODE_ENV; - }, - getURLTitle: function(url, callback) { - (function () { - var result = {title: '', image: '', url: '', description: ''}; - request(url, function (error, response, body) { - if (!error && response.statusCode === 200) { - var $ = cheerio.load(body); - var metaDescription = $("meta[name='description']"); - var metaImage = $("meta[property='og:image']"); - var urlImage = metaImage.attr('content') ? metaImage.attr('content') : ''; - var description = metaDescription.attr('content') ? metaDescription.attr('content') : ''; - result.title = $('title').text().length < 141 ? $('title').text() : $('title').text().slice(0, 137) + " ..."; - result.image = urlImage; - result.description = description; - callback(null, result); - } else { - callback('failed'); - } - }); - })(); - }, - updateUserStoryPictures: function(userId, picture, username, cb) { - - var counter = 0, - foundStories, - foundComments; - - Story.find({'author.userId': userId}, function(err, stories) { - if (err) { - return cb(err); - } - foundStories = stories; - counter++; - saveStoriesAndComments(); - }); - Comment.find({'author.userId': userId}, function(err, comments) { - if (err) { - return cb(err); - } - foundComments = comments; - counter++; - saveStoriesAndComments(); - }); - - function saveStoriesAndComments() { - if (counter !== 2) { - return; - } - var tasks = []; - R.forEach(function(comment) { - comment.author.picture = picture; - comment.author.username = username; - comment.markModified('author'); - tasks.push(function(cb) { - comment.save(cb); - }); - }, foundComments); - - R.forEach(function(story) { - story.author.picture = picture; - story.author.username = username; - story.markModified('author'); - tasks.push(function(cb) { - story.save(cb); - }); - }, foundStories); - async.parallel(tasks, function(err) { - if (err) { return cb(err); } - cb(); - }); - } + about: function(req, res, next) { + if (req.user) { + if (!req.user.profile.picture || req.user.profile.picture === "https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png") { + req.user.profile.picture = "https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png"; + req.user.save(); + } } + var date1 = new Date('10/15/2014'); + var date2 = new Date(); + var progressTimestamps = req.user.progressTimestamps; + var now = Date.now() || 0; + + if (req.user.pointsNeedMigration) { + var challengesHash = req.user.challengesHash; + for (var key in challengesHash) { + if (challengesHash[key] > 0) { + req.user.progressTimestamps.push(challengesHash[key]); + } + } + + var oldChallengeKeys = R.keys(req.user.challengesHash); + + var updatedTimesFromOldChallenges = oldChallengeKeys.map(function(timeStamp) { + if (timeStamp.toString().length !== 13) { + timeStamp *= 1000; + } + return timeStamp; + }); + + var newTimeStamps = R.map(function(timeStamp) { + if (timeStamp.toString().length !== 13) { + timeStamp *= 1000; + } + return timeStamp; + }, req.user.progressTimestamps); + + req.user.progressTimestamps = newTimeStamps; + + + req.user.completedCoursewares = Array.zip(updatedTimesFromOldChallenges, coursewares, + function(left, right) { + return ({ + completedDate: left.timeStamp, + _id: right._id, + name: right.name + }); + }).filter(function(elem) { + return elem.completedDate !== 0; + }); + req.user.pointsNeedMigration = false; + req.user.save(); + } + if (progressTimestamps[progressTimestamps.length - 1] <= (now - 43200)) { + req.user.progressTimestamps.push(now); + } + var timeDiff = Math.abs(date2.getTime() - date1.getTime()); + var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24)); + var announcements = resources.announcements; + function numberWithCommas(x) { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + } + User.count({}, function (err, c3) { + if (err) { + debug('User err: ', err); + return next(err); + } + User.count({'points': {'$gt': 53}}, function (err, all) { + if (err) { + debug('User err: ', err); + return next(err); + } + + res.render('resources/learn-to-code', { + title: 'About Free Code Camp and Our Team of Volunteers', + daysRunning: daysRunning, + c3: numberWithCommas(c3), + all: all, + announcements: announcements + }); + }); + }); + }, + + randomPhrase: function() { + var phrases = resources.phrases; + return phrases[Math.floor(Math.random() * phrases.length)]; + }, + + randomVerb: function() { + var verbs = resources.verbs; + return verbs[Math.floor(Math.random() * verbs.length)]; + }, + + randomCompliment: function() { + var compliments = resources.compliments; + return compliments[Math.floor(Math.random() * compliments.length)]; + }, + + allBonfireIds: function() { + return bonfires.map(function(elem) { + return { + _id: elem._id, + difficulty: elem.difficulty + } + }) + .sort(function(a, b) { + return a.difficulty - b.difficulty; + }) + .map(function(elem) { + return elem._id; + }); + }, + allBonfireNames: function() { + return bonfires.map(function(elem) { + return { + name: elem.name, + difficulty: elem.difficulty, + _id: elem._id + } + }) + .sort(function(a, b) { + return a.difficulty - b.difficulty; + }) + .map (function(elem) { + return { + name : elem.name, + _id: elem._id + } + }); + }, + + getAllCourses: function() { + "use strict"; + return coursewares; + }, + + allCoursewareIds: function() { + return coursewares.map(function(elem) { + return { + _id: elem._id, + difficulty: elem.difficulty + }; + }) + .sort(function(a, b) { + return a.difficulty - b.difficulty; + }) + .map(function(elem) { + return elem._id; + }); + }, + allCoursewareNames: function() { + return coursewares.map(function(elem) { + return { + name: elem.name, + difficulty: elem.difficulty, + _id: elem._id + }; + }) + .sort(function(a, b) { + return a.difficulty - b.difficulty; + }) + .map (function(elem) { + return { + name: elem.name, + _id: elem._id + }; + }); + }, + whichEnvironment: function() { + return process.env.NODE_ENV; + }, + getURLTitle: function(url, callback) { + (function () { + var result = {title: '', image: '', url: '', description: ''}; + request(url, function (error, response, body) { + if (!error && response.statusCode === 200) { + var $ = cheerio.load(body); + var metaDescription = $("meta[name='description']"); + var metaImage = $("meta[property='og:image']"); + var urlImage = metaImage.attr('content') ? metaImage.attr('content') : ''; + var description = metaDescription.attr('content') ? metaDescription.attr('content') : ''; + result.title = $('title').text().length < 141 ? $('title').text() : $('title').text().slice(0, 137) + " ..."; + result.image = urlImage; + result.description = description; + callback(null, result); + } else { + callback('failed'); + } + }); + })(); + }, + updateUserStoryPictures: function(userId, picture, username, cb) { + + var counter = 0, + foundStories, + foundComments; + + Story.find({'author.userId': userId}, function(err, stories) { + if (err) { + return cb(err); + } + foundStories = stories; + counter++; + saveStoriesAndComments(); + }); + Comment.find({'author.userId': userId}, function(err, comments) { + if (err) { + return cb(err); + } + foundComments = comments; + counter++; + saveStoriesAndComments(); + }); + + function saveStoriesAndComments() { + if (counter !== 2) { + return; + } + var tasks = []; + R.forEach(function(comment) { + comment.author.picture = picture; + comment.author.username = username; + comment.markModified('author'); + tasks.push(function(cb) { + comment.save(cb); + }); + }, foundComments); + + R.forEach(function(story) { + story.author.picture = picture; + story.author.username = username; + story.markModified('author'); + tasks.push(function(cb) { + story.save(cb); + }); + }, foundStories); + async.parallel(tasks, function(err) { + if (err) { return cb(err); } + cb(); + }); + } + } }; diff --git a/controllers/user.js b/controllers/user.js index 5e37f84c47..28d8d38a11 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -7,7 +7,8 @@ var _ = require('lodash'), secrets = require('../config/secrets'), moment = require('moment'), debug = require('debug')('freecc:cntr:challenges'), - resources = require('./resources'); + resources = require('./resources'), + R = require('ramda'); @@ -199,54 +200,44 @@ exports.postEmailSignup = function(req, res, next) { * For Calendar display */ -exports.getStreak = function(req, res) { +exports.getStreak = function(req, res, next) { - Array.prototype.timeReduce = function(combiner, initialValue) { - var counter, - accumulatedValue; + req.user.progressTimestamps = req.user.progressTimestamps.sort(function(a, b) { + return a - b; + }); - // If the array is empty, do nothing - if (this.length === 0) { - return this; - } else { - // If the user didn't pass an initial value, use the first item. - if (arguments.length === 1) { - counter = 1; - accumulatedValue = this[0]; - } - else if (arguments.length >= 2) { - counter = 0; - accumulatedValue = initialValue; - } - else { - throw "Invalid arguments."; - } + var timeObject = Object.create(null); + R.forEach(function(time) { + timeObject[moment(time).format('YYYY-MM-DD')] = time; + }, req.user.progressTimestamps); - // Loop through the array, feeding the current value and the result of - // the previous computation back into the combiner function until - // we've exhausted the entire array and are left with only one function. - while (counter < this.length) { - accumulatedValue = combiner(accumulatedValue, this[counter]); - counter++; + var tmpLongest = 1; + var timeKeys = R.keys(timeObject); + for (var i = 1; i <= timeKeys.length; i++) { + if (moment(timeKeys[i - 1]).add(1, 'd').toString() + === moment(timeKeys[i]).toString()) { + tmpLongest++; + if (tmpLongest > req.user.currentStreak) { + req.user.currentStreak = tmpLongest; + } + if ( req.user.currentStreak > req.user.longestStreak) { + req.user.longestStreak = req.user.currentStreak; } - - return [accumulatedValue]; } + } + + req.user.save(function(err) { + if (err) { + return next(err); + } + }); +s + var payload = { + longest: req.user.longestStreak, + timeObject: timeObject }; - var timeObject = req.user.progressTimestamps.timeReduce(function(accumulatedTime, timeStamp) { - - var copyOfAccumulatedTime = Object.create(accumulatedTime); - - copyOfAccumulatedTime[moment(timeStamp) - .format('MMMM Do YYYY')] = timeStamp; - - return copyOfAccumulatedTime; - }, - {}); - - debug('TimeObject is', timeObject); - return res.send(timeObject); + return res.send(payload); }; /** @@ -326,7 +317,7 @@ exports.returnUser = function(req, res, next) { var data = {}; var progressTimestamps = user.progressTimestamps; for (var i = 0; i < progressTimestamps.length; i++) { - data[progressTimestamps[i].toString()] = 1; + data[(progressTimestamps[i] / 1000).toString()] = 1; } res.render('account/show', { diff --git a/models/User.js b/models/User.js index 6019e7d83b..1e1d828853 100644 --- a/models/User.js +++ b/models/User.js @@ -1,7 +1,9 @@ var bcrypt = require('bcrypt-nodejs'); var crypto = require('crypto'); var mongoose = require('mongoose'); +require('mongoose-long')(mongoose); +var Long = mongoose.Types.Long; var userSchema = new mongoose.Schema({ email: { type: String, @@ -21,7 +23,7 @@ var userSchema = new mongoose.Schema({ type: Number, default: 0 }, - progressTimestamps: { type: Array, default: [Date] }, + progressTimestamps: [], challengesCompleted: { type: Array, default: [] }, pointsNeedMigration: { type: Boolean, default: true }, challengesHash: { @@ -332,9 +334,30 @@ var userSchema = new mongoose.Schema({ resetPasswordToken: String, resetPasswordExpires: Date, uncompletedBonfires: Array, - completedBonfires: Array, + completedBonfires: [ + { + _id: String, + completedWith: String, + completedDate: Long, + solution: String + } + ], uncompletedCoursewares: Array, - completedCoursewares: Array + completedCoursewares: [ + { + completedDate: Long, + _id: String, + name: String + } + ], + currentStreak: { + type: Number, + default: 0 + }, + longestStreak: { + type: Number, + default: 0 + } }); /**