diff --git a/common/models/user.json b/common/models/user.json index 9a8a90450c..e39ce0e8b1 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -123,6 +123,11 @@ "defaut": false, "description": "Camper is front end certified" }, + "isDataVisCert": { + "type": "boolean", + "defaut": false, + "description": "Camper is data visualization certified" + }, "isBackEndCert": { "type": "boolean", "default": false, diff --git a/server/boot/certificate.js b/server/boot/certificate.js index 9180af7e6d..0c3289bd43 100644 --- a/server/boot/certificate.js +++ b/server/boot/certificate.js @@ -15,6 +15,7 @@ import { import { frontEndChallengeId, + dataVisChallengeId, backEndChallengeId } from '../utils/constantStrings.json'; @@ -22,6 +23,8 @@ import { completeCommitment$ } from '../utils/commit'; +import certTypes from '../utils/certTypes.json'; + const debug = debugFactory('freecc:certification'); const sendMessageToNonUser = ifNoUserSend( 'must be logged in to complete.' @@ -35,46 +38,47 @@ function isCertified(ids, { completedChallenges }) { }); } +function getIdsForCert$(id, Challenge) { + return observeQuery( + Challenge, + 'findById', + id, + { + id: true, + tests: true, + name: true, + challengeType: true + } + ) + .shareReplay(); +} + export default function certificate(app) { const router = app.loopback.Router(); const { Challenge } = app.models; - const frontEndChallengeIds$ = observeQuery( - Challenge, - 'findById', - frontEndChallengeId, - { - id: true, - tests: true, - name: true, - challengeType: true - } - ) - .shareReplay(); - - const backEndChallengeIds$ = observeQuery( - Challenge, - 'findById', - backEndChallengeId, - { - id: true, - tests: true, - name: true, - challengeType: true - } - ) - .shareReplay(); + const certTypeIds = { + [certTypes.frontEnd]: getIdsForCert$(frontEndChallengeId, Challenge), + [certTypes.backEnd]: getIdsForCert$(dataVisChallengeId, Challenge), + [certTypes.dataVis]: getIdsForCert$(backEndChallengeId, Challenge) + }; router.post( '/certificate/verify/front-end', ifNoUser401, - verifyCert + verifyCert.bind(null, certTypes.frontEnd) ); router.post( '/certificate/verify/back-end', ifNoUser401, - verifyCert + verifyCert.bind(null, certTypes.backEnd) + ); + + router.post( + '/certificate/verify/data-visualization', + ifNoUser401, + verifyCert.bind(null, certTypes.dataVis) ); router.post( @@ -85,14 +89,10 @@ export default function certificate(app) { app.use(router); - function verifyCert(req, res, next) { - const isFront = req.path.split('/').pop() === 'front-end'; + function verifyCert(certType, req, res, next) { Observable.just({}) .flatMap(() => { - if (isFront) { - return frontEndChallengeIds$; - } - return backEndChallengeIds$; + return certTypeIds[certType]; }) .flatMap(challenge => { const { user } = req; @@ -103,29 +103,17 @@ export default function certificate(app) { challengeType } = challenge; if ( - - isFront && - !user.isFrontEndCert && - isCertified(tests, user) || - - !isFront && - !user.isBackEndCert && + !user[certType] && isCertified(tests, user) - ) { - debug('certified'); - if (isFront) { - user.isFrontEndCert = true; - } else { - user.isBackEndCert = true; - } - + user[certType] = true; user.completedChallenges.push({ id, name, completedDate: new Date(), challengeType }); + return saveUser(user) // If user has commited to nonprofit, // this will complete his pledge @@ -146,8 +134,7 @@ export default function certificate(app) { .subscribe( user => { if ( - isFront && user.isFrontEndCert || - !isFront && user.isBackEndCert + user[certType] ) { return res.status(200).send(true); } diff --git a/server/boot/user.js b/server/boot/user.js index 7c6a859c6f..8244fdb695 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -6,14 +6,37 @@ import debugFactory from 'debug'; import { frontEndChallengeId, + dataVisChallengeId, backEndChallengeId } from '../utils/constantStrings.json'; + +import certTypes from '../utils/certTypes.json'; + import { ifNoUser401, ifNoUserRedirectTo } from '../utils/middleware'; import { observeQuery } from '../utils/rx'; import { calcCurrentStreak, calcLongestStreak } from '../utils/user-stats'; const debug = debugFactory('freecc:boot:user'); const sendNonUserToMap = ifNoUserRedirectTo('/map'); +const certIds = { + [certTypes.frontEnd]: frontEndChallengeId, + [certTypes.dataVis]: dataVisChallengeId, + [certTypes.backEnd]: backEndChallengeId +}; + +const certViews = { + [certTypes.frontEnd]: 'certificate/front-end.jade', + [certTypes.dataVis]: 'certificate/data-vis.jade', + [certTypes.backEnd]: 'certificate/back-end.jade', + [certTypes.fullStack]: 'certificate/full-stack.jade' +}; + +const certText = { + [certTypes.fronEnd]: 'Front End certified', + [certTypes.dataVis]: 'Data Vis Certified', + [certTypes.backEnd]: 'Back End Certified', + [certTypes.fullStack]: 'Full Stack Certified' +}; function replaceScriptTags(value) { return value @@ -80,7 +103,17 @@ module.exports = function(app) { // Ensure these are the last routes! router.get( '/:username/front-end-certification', - showCert + showCert.bind(null, certTypes.frontEnd) + ); + + router.get( + '/:username/data-visualization-certification', + showCert.bind(null, certTypes.dataVis) + ); + + router.get( + '/:username/back-end-certification', + showCert.bind(null, certTypes.backEnd) ); router.get( @@ -88,11 +121,6 @@ module.exports = function(app) { (req, res) => res.redirect(req.url.replace('full-stack', 'back-end')) ); - router.get( - '/:username/back-end-certification', - showCert - ); - router.get('/:username', returnUser); app.use(router); @@ -207,6 +235,7 @@ module.exports = function(app) { pledge: profileUser.pledge, isFrontEndCert: profileUser.isFrontEndCert, + isDataVisCert: profileUser.isDataVisCert, isBackEndCert: profileUser.isBackEndCert, isFullStackCert: profileUser.isFullStackCert, isHonest: profileUser.isHonest, @@ -237,11 +266,9 @@ module.exports = function(app) { ); } - function showCert(req, res, next) { + function showCert(certType, req, res, next) { const username = req.params.username.toLowerCase(); const { user } = req; - const whichCert = req.path.split('/').pop(); - const showFront = whichCert === 'front-end-certification'; Observable.just(user) .flatMap(user => { if (user && user.username === username) { @@ -250,8 +277,9 @@ module.exports = function(app) { return findUserByUsername$(username, { isGithubCool: true, isFrontEndCert: true, - isFullStackCert: true, + isDataVisCert: true, isBackEndCert: true, + isFullStackCert: true, isHonest: true, completedChallenges: true, username: true, @@ -294,33 +322,29 @@ module.exports = function(app) { return res.redirect('back'); } - if ( - showFront && user.isFrontEndCert || - !showFront && user.isBackEndCert - ) { + if (user[certType]) { + + // find challenge in user profile + // if not found supply empty object + // if found grab date + // if no date use todays date var { completedDate = new Date() } = - _.find(user.completedChallenges, { - id: showFront ? - frontEndChallengeId : - backEndChallengeId - }) || {}; + _.find( + user.completedChallenges, + { id: certIds[certType] } + ) || {}; return res.render( - showFront ? - 'certificate/front-end.jade' : - 'certificate/back-end.jade', + certViews[certType], { username: user.username, - date: moment(new Date(completedDate)) - .format('MMMM, Do YYYY'), + date: moment(new Date(completedDate)).format('MMMM, Do YYYY'), name: user.name } ); } req.flash('errors', { - msg: showFront ? - `Looks like user ${username} is not Front End certified` : - `Looks like user ${username} is not Back End certified` + msg: `Looks like user ${username} is not ${certText[certType]}` }); res.redirect('back'); }, diff --git a/server/utils/certTypes.json b/server/utils/certTypes.json new file mode 100644 index 0000000000..6dcd7bdcc7 --- /dev/null +++ b/server/utils/certTypes.json @@ -0,0 +1,6 @@ +{ + "frontEnd": "isFrontEndCert", + "backEnd": "isBackEndCert", + "dataVis": "isDataVisCert", + "fullStack": "isFullStackCert" +} diff --git a/server/utils/commit.js b/server/utils/commit.js index 6eb48d139b..02ce7ab5dc 100644 --- a/server/utils/commit.js +++ b/server/utils/commit.js @@ -8,7 +8,13 @@ const debug = debugFactory('freecc:utils/commit'); export { commitGoals }; export function completeCommitment$(user) { - const { isFrontEndCert, isFullStackCert } = user; + const { + isFrontEndCert, + isDataVisCert, + isBackEndCert, + isFullStackCert + } = user; + return Observable.fromNodeCallback(user.pledge, user)() .flatMap(pledge => { if (!pledge) { @@ -18,8 +24,10 @@ export function completeCommitment$(user) { const { goal } = pledge; if ( - isFrontEndCert && goal === commitGoals.frontEndCert || - isFullStackCert && goal === commitGoals.fullStackCert + (isFrontEndCert && goal === commitGoals.frontEndCert) || + (isDataVisCert && goal === commitGoals.dataVisCert) || + (isBackEndCert && goal === commitGoals.backEndCert) || + (isFullStackCert && goal === commitGoals.fullStackCert) ) { debug('marking goal complete'); pledge.isCompleted = true; diff --git a/server/utils/constantStrings.json b/server/utils/constantStrings.json index 74d21b642d..eba50d1f67 100644 --- a/server/utils/constantStrings.json +++ b/server/utils/constantStrings.json @@ -1,5 +1,6 @@ { "gitHubUserAgent": "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", "frontEndChallengeId": "561add10cb82ac38a17513be", + "dataVisChallengeId": "561add10cb82ac38a17513b3", "backEndChallengeId": "660add10cb82ac38a17513be" } diff --git a/server/views/account/show.jade b/server/views/account/show.jade index 6446b4847f..545d2341e4 100644 --- a/server/views/account/show.jade +++ b/server/views/account/show.jade @@ -67,6 +67,9 @@ block content .spacer if isFrontEndCert a.btn.btn-primary(href='/' + username + '/front-end-certification') View My Front End Development Certification + if isDataVisCert + .button-spacer + a.btn.btn-success(href='/' + username + '/data-visualization-certification') View My Data Visualization Certification if isBackEndCert .button-spacer a.btn.btn-success(href='/' + username + '/back-end-certification') View My Back End Development Certification diff --git a/server/views/certificate/data-vis.jade b/server/views/certificate/data-vis.jade new file mode 100644 index 0000000000..ba90adcf2a --- /dev/null +++ b/server/views/certificate/data-vis.jade @@ -0,0 +1,6 @@ +include font +#name.cert-name= name +img#cert.img-abs(src='http://i.imgur.com/l7tIptn.jpg') +.cert-date= date +.cert-link verify this certification at: http://freecodecamp.com/#{username}/data-vis-certification +include script