Skip to content

Commit

Permalink
Add certification page
Browse files Browse the repository at this point in the history
  • Loading branch information
Berkeley Martinez authored and Berkeley Martinez committed Oct 5, 2015
1 parent d9332e7 commit 8c48626
Show file tree
Hide file tree
Showing 17 changed files with 415 additions and 82 deletions.
51 changes: 45 additions & 6 deletions client/commonFramework.js
Original file line number Diff line number Diff line change
Expand Up @@ -865,14 +865,53 @@ common.init.push((function() {
}
next();
});

}

function handleActionClick() {
$(this)
.parent()
.find('.disabled')
.removeClass('disabled');
function handleActionClick(e) {
var props = common.challengeSeed[0] ||
{ stepIndex: [] };

var $el = $(this);
var index = +$el.attr('id');
var propIndex = props.stepIndex.indexOf(index);

if (propIndex === -1) {
return $el
.parent()
.find('.disabled')
.removeClass('disabled');
}

// an API action
// prevent link from opening
e.preventDefault();
var prop = props.properties[propIndex];
var api = props.apis[propIndex];
if (common[prop]) {
return $el
.parent()
.find('.disabled')
.removeClass('disabled');
}
$
.post(api)
.done(function(data) {
// assume a boolean indicates passing
if (typeof data === 'boolean') {
return $el
.parent()
.find('.disabled')
.removeClass('disabled');
}
// assume api returns string when fails
$el
.parent()
.find('.disabled')
.replaceWith('<p>' + data + '</p>');
})
.fail(function() {
console.log('failed');
});
}

function handleFinishClick(e) {
Expand Down
21 changes: 19 additions & 2 deletions common/models/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,31 @@
},
"isLocked": {
"type": "boolean",
"default": false
"default": false,
"description": "Campers profile does not show challenges to the public"
},
"currentChallenge": {
"type": {}
},
"isUniqMigrated": {
"type": "boolean",
"default": false
"default": false,
"description": "Campers completedChallenges array is free of duplicates"
},
"isHonest": {
"type": "boolean",
"default": false,
"description": "Camper has signed academic honesty policy"
},
"isFrontEndCert": {
"type": "boolean",
"defaut": false,
"description": "Camper is front end certified"
},
"isFullStackCert": {
"type": "boolean",
"default": false,
"description": "Campers is full stack certified"
},
"completedChallenges": {
"type": [
Expand Down
Binary file added public/fonts/saxmono.ttf
Binary file not shown.
17 changes: 14 additions & 3 deletions seed/challenges/front-end-development-certificate.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@
{
"id": "561add10cb82ac38a17513be",
"title": "Claim Your Front End Development Certificate",
"difficulty": 0.00,
"challengeSeed": [],
"challengeSeed": [
{
"properties": ["isHonest", "isFrontEndCert"],
"apis": ["/certificate/honest", "/certificate/verify/front-end"],
"stepIndex": [0, 1]
}
],
"description": [
[
"http://i.imgur.com/RlEk2IF.jpg",
"a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits",
"Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.",
""
"#"
],
[
"http://i.imgur.com/RlEk2IF.jpg",
"a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits",
"Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.",
"#"
]
],
"type": "Waypoint",
Expand Down
16 changes: 14 additions & 2 deletions seed/challenges/full-stack-development-certificate.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,25 @@
"id": "660add10cb82ac38a17513be",
"title": "Claim Your Full Stack Development Certificate",
"difficulty": 0.00,
"challengeSeed": [],
"challengeSeed": [
{
"properties": ["isHonest", "isFullStackCert"],
"apis": ["/certificate/honest", "/certificate/verify/full-stack"],
"stepIndex": [0, 1]
}
],
"description": [
[
"http://i.imgur.com/RlEk2IF.jpg",
"a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits",
"Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.",
""
"#"
],
[
"http://i.imgur.com/RlEk2IF.jpg",
"a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits",
"Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.",
"#"
]
],
"type": "Waypoint",
Expand Down
131 changes: 131 additions & 0 deletions server/boot/certificate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import _ from 'lodash';
import dedent from 'dedent';
import { Observable } from 'rx';
import debugFactory from 'debug';

import {
ifNoUser401,
ifNoUserSend
} from '../utils/middleware';

import {
saveUser,
observeQuery
} from '../utils/rx';

const frontEndChallangeId = '561add10cb82ac38a17513be';
const fullStackChallangeId = '660add10cb82ac38a17513be';
const debug = debugFactory('freecc:certification');
const sendMessageToNonUser = ifNoUserSend(
'must be logged in to complete.'
);

function isCertified(frontEndIds, { completedChallenges, isFrontEndCert }) {
if (isFrontEndCert) {
return true;
}
return _.every(frontEndIds, ({ id }) => _.some(completedChallenges, { id }));
}

export default function certificate(app) {
const router = app.loopback.Router();
const { Challenge } = app.models;

const frontEndChallangeIds$ = observeQuery(
Challenge,
'findById',
frontEndChallangeId,
{
tests: true
}
)
.map(({ tests = [] }) => tests)
.shareReplay();

const fullStackChallangeIds$ = observeQuery(
Challenge,
'findById',
fullStackChallangeId,
{
tests: true
}
)
.map(({ tests = [] }) => tests)
.shareReplay();

router.post(
'/certificate/verify/front-end',
ifNoUser401,
verifyCert
);

router.post(
'/certificate/verify/full-stack',
ifNoUser401,
verifyCert
);

router.post(
'/certificate/honest',
sendMessageToNonUser,
postHonest
);

app.use(router);

function verifyCert(req, res, next) {
const isFront = req.path.split('/').pop() === 'front-end';
Observable.just({})
.flatMap(() => {
if (isFront) {
return frontEndChallangeIds$;
}
return fullStackChallangeIds$;
})
.flatMap((tests) => {
const { user } = req;
if (
isFront && !user.isFrontEndCert && isCertified(tests, user) ||
!isFront && !user.isFullStackCert && isCertified(tests, user)
) {
debug('certified');
if (isFront) {
user.isFrontEndCert = true;
} else {
user.isFullStackCert = true;
}
return saveUser(user);
}
return Observable.just(user);
})
.subscribe(
user => {
if (
isFront && user.isFrontEndCert ||
!isFront && user.isFullStackCert
) {
return res.status(200).send(true);
}
return res.status(200).send(
dedent`
Looks like you have not completed the neccessary steps,
Please return the map
`
);
},
next
);
}

function postHonest(req, res, next) {
const { user } = req;
user.isHonest = true;
saveUser(user)
.subscribe(
(user) => {
res.status(200).send(!!user.isHonest);
},
next
);
}
}
9 changes: 3 additions & 6 deletions server/boot/challenge.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ import utils from '../utils';
import {
saveUser,
observeMethod,
observableQueryFromModel
observeQuery
} from '../utils/rx';

import {
userMigration,
ifNoUserSend
} from '../utils/middleware';

Expand Down Expand Up @@ -147,8 +146,6 @@ module.exports = function(app) {
completedBonfire
);

// the follow routes are covered by userMigration
router.use(userMigration);
router.get('/map', challengeMap);
router.get(
'/challenges/next-challenge',
Expand Down Expand Up @@ -330,7 +327,7 @@ module.exports = function(app) {
challengeType: 5
};

observableQueryFromModel(
observeQuery(
User,
'findOne',
{ where: { username: ('' + completedWith).toLowerCase() } }
Expand Down Expand Up @@ -458,7 +455,7 @@ module.exports = function(app) {
verified: false
};

observableQueryFromModel(
observeQuery(
User,
'findOne',
{ where: { username: completedWith.toLowerCase() } }
Expand Down
Loading

0 comments on commit 8c48626

Please sign in to comment.