fix challenge naming scheme

This commit is contained in:
Berkeley Martinez
2015-08-09 22:14:31 -07:00
parent 2476f35309
commit 967f92e6f4
5 changed files with 116 additions and 80 deletions

View File

@ -25,6 +25,9 @@
"dashedName": { "dashedName": {
"type": "string" "type": "string"
}, },
"block": {
"type": "string"
},
"difficulty": { "difficulty": {
"type": "string" "type": "string"
}, },

View File

@ -39,6 +39,8 @@ Challenge.destroyAll(function(err, info) {
challenges.forEach(function(file) { challenges.forEach(function(file) {
var challengeSpec = require('./challenges/' + file); var challengeSpec = require('./challenges/' + file);
var order = challengeSpec.order; var order = challengeSpec.order;
var block = challengeSpec.name;
var challenges = challengeSpec.challenges var challenges = challengeSpec.challenges
.map(function(challenge, index) { .map(function(challenge, index) {
// NOTE(berks): add title for displaying in views // NOTE(berks): add title for displaying in views
@ -46,11 +48,14 @@ Challenge.destroyAll(function(err, info) {
_.capitalize(challenge.type) + _.capitalize(challenge.type) +
': ' + ': ' +
challenge.title.replace(/[^a-zA-Z0-9\s]/g, ''); challenge.title.replace(/[^a-zA-Z0-9\s]/g, '');
challenge.dashedName = challenge.name challenge.dashedName = challenge.name
.toLowerCase() .toLowerCase()
.replace(/\:/g, '') .replace(/\:/g, '')
.replace(/\s/g, '-'); .replace(/\s/g, '-');
challenge.order = +('' + order + (index + 1)); challenge.order = +('' + order + (index + 1));
challenge.block = block;
return challenge; return challenge;
}); });

View File

@ -1,17 +1,24 @@
var R = require('ramda'), import _ from 'lodash';
Rx = require('rx'), import moment from 'moment';
assign = require('object.assign'), import R from 'ramda';
debug = require('debug')('freecc:challenges'), import { Observable } from 'rx';
utils = require('../utils'), import assign from 'object.assign';
import debugFactory from 'debug';
import utils from '../utils';
// this would be so much cleaner with destructering... import {
saveUser = require('../utils/rx').saveUser, saveUser,
observableQueryFromModel = require('../utils/rx').observableQueryFromModel, observeMethod,
observableQueryFromModel
} from '../utils/rx';
userMigration = require('../utils/middleware').userMigration, import {
ifNoUserRedirectTo = require('../utils/middleware').ifNoUserRedirectTo, userMigration,
ifNoUserSend = require('../utils/middleware').ifNoUserSend; ifNoUserRedirectTo,
ifNoUserSend
} from '../utils/middleware';
const debug = debugFactory('freecc:challenges');
var challengeMapWithNames = utils.getChallengeMapWithNames(); var challengeMapWithNames = utils.getChallengeMapWithNames();
var challengeMapWithIds = utils.getChallengeMapWithIds(); var challengeMapWithIds = utils.getChallengeMapWithIds();
var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames(); var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames();
@ -22,6 +29,10 @@ var unDasherize = utils.unDasherize;
var getMDNLinks = utils.getMDNLinks; var getMDNLinks = utils.getMDNLinks;
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
function updateUserProgress(user, challengeId, completedChallenge) { function updateUserProgress(user, challengeId, completedChallenge) {
var alreadyCompleted = user.completedChallenges.some(({ id }) => { var alreadyCompleted = user.completedChallenges.some(({ id }) => {
return id === challengeId; return id === challengeId;
@ -37,12 +48,18 @@ function updateUserProgress(user, challengeId, completedChallenge) {
} }
module.exports = function(app) { module.exports = function(app) {
var router = app.loopback.Router(); const router = app.loopback.Router();
var Challenge = app.models.Challenge;
var User = app.models.User; const Challenge = app.models.Challenge;
var redirectNonUser = const findChallenge$ = observeMethod(Challenge, 'find');
ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works');
var send200toNonUser = ifNoUserSend(true); const User = app.models.User;
const userCount$ = observeMethod(User, 'count');
const send200toNonUser = ifNoUserSend(true);
const redirectNonUser = ifNoUserRedirectTo(
'/challenges/learn-how-free-code-camp-works'
);
router.post( router.post(
'/completed-challenge/', '/completed-challenge/',
@ -182,7 +199,7 @@ module.exports = function(app) {
.map(function(key) { .map(function(key) {
return challengeMapWithIds[key] return challengeMapWithIds[key]
.filter(function(elem) { .filter(function(elem) {
return elem === ('' + challenge.id); return elem === '' + challenge.id;
}) })
.map(function() { .map(function() {
return key; return key;
@ -266,7 +283,7 @@ module.exports = function(app) {
} }
}) })
.withLatestFrom( .withLatestFrom(
Rx.Observable.just(req.user), Observable.just(req.user),
function(pairedWith, user) { function(pairedWith, user) {
return { return {
user: user, user: user,
@ -289,7 +306,7 @@ module.exports = function(app) {
// not iterate users // not iterate users
.flatMap(function(dats) { .flatMap(function(dats) {
debug('flatmap'); debug('flatmap');
return Rx.Observable.from([dats.user, dats.pairedWith]); return Observable.from([dats.user, dats.pairedWith]);
}) })
// save user // save user
.flatMap(function(user) { .flatMap(function(user) {
@ -392,7 +409,7 @@ module.exports = function(app) {
); );
} }
}) })
.withLatestFrom(Rx.Observable.just(req.user), function(pairedWith, user) { .withLatestFrom(Observable.just(req.user), function(pairedWith, user) {
return { return {
user: user, user: user,
pairedWith: pairedWith pairedWith: pairedWith
@ -408,7 +425,7 @@ module.exports = function(app) {
); );
}) })
.flatMap(function({ user, pairedWith }) { .flatMap(function({ user, pairedWith }) {
return Rx.Observable.from([user, pairedWith]); return Observable.from([user, pairedWith]);
}) })
// save users // save users
.flatMap(function(user) { .flatMap(function(user) {
@ -428,50 +445,73 @@ module.exports = function(app) {
); );
} }
function challengeMap(req, res, next) { function challengeMap({ user = {} }, res, next) {
var completedList = []; const daysRunning = moment().diff(new Date('10/15/2014'), 'days');
if (req.user) { // if user
completedList = req.user.completedChallenges; // get the id's of all the users completed challenges
} const completedChallenges = !user.completedChallenges ?
[] :
_.uniq(user.completedChallenges).map(({ id }) => id);
var noDuplicatedChallenges = R.uniq(completedList); const camperCount$ = userCount$()
.map(camperCount => numberWithCommas(camperCount));
var completedChallengeList = noDuplicatedChallenges const query = {
.map(function(challenge) { order: 'order ASC'
// backwards compatibility };
return (challenge.id || challenge._id);
});
var challengeList = utils.
getChallengeMapForDisplay(completedChallengeList);
Object.keys(challengeList).forEach(function(key) { // create a stream of all the challenges
challengeList[key].completed = challengeList[key] const challenge$ = findChallenge$(query)
.challenges.filter(function(elem) { .flatMap(challenges => Observable.from(challenges))
// backwards compatibility hack .shareReplay();
return completedChallengeList.indexOf(elem.id || elem._id) > -1;
});
});
function numberWithCommas(x) { // create a stream of an array of all the challenge blocks
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); const blocks$ = challenge$
} // mark challenge completed
.map(challenge => {
if (completedChallenges.indexOf(challenge.id) !== -1) {
challenge.completed = true;
}
return challenge;
})
// group challenges by block | returns a stream of observables
.groupBy(challenge => challenge.block)
// turn block group stream into an array
.flatMap(block$ => block$.toArray())
.map(blockArray => {
const completedCount = blockArray.reduce((sum, { completed }) => {
if (completed) {
return sum + 1;
}
return sum;
});
var date1 = new Date('10/15/2014'); return {
var date2 = new Date(); name: blockArray[0].block,
var timeDiff = Math.abs(date2.getTime() - date1.getTime()); dashedName: dasherize(blockArray[0].block),
var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24)); challenges: blockArray,
completed: completedCount / blockArray.length * 100
};
})
// turn stream of blocks into a stream of an array
.toArray();
User.count(function(err, camperCount) { Observable.combineLatest(
if (err) { return next(err); } camperCount$,
blocks$,
res.render('challengeMap/show', { (camperCount, blocks) => ({ camperCount, blocks })
daysRunning: daysRunning, )
camperCount: numberWithCommas(camperCount), .subscribe(
title: "A map of all Free Code Camp's Challenges", ({ camperCount, blocks }) => {
challengeList: challengeList, res.render('challengeMap/show', {
completedChallengeList: completedChallengeList blocks,
}); daysRunning,
}); camperCount,
title: "A map of all Free Code Camp's Challenges"
});
},
next
);
} }
}; };

View File

@ -19,17 +19,6 @@ var allNonprofitNames,
challengeMapWithNames, allChallengeIds, challengeMapWithNames, allChallengeIds,
challengeMapWithDashedNames; challengeMapWithDashedNames;
Array.zip = function(left, right, combinerFunction) {
var counter,
results = [];
for (counter = 0; counter < Math.min(left.length, right.length); counter++) {
results.push(combinerFunction(left[counter], right[counter]));
}
return results;
};
(function() { (function() {
if (!challengeMap) { if (!challengeMap) {
var localChallengeMap = {}; var localChallengeMap = {};

View File

@ -1,8 +1,7 @@
extends ../layout extends ../layout
block content block content
script. script.
var completedChallenges = !{JSON.stringify(completedChallengeList)}; var challenges = !{JSON.stringify(challenges)};
var challengeList = !{JSON.stringify(challengeList)};
.bg-danger.default-border-radius .bg-danger.default-border-radius
p &nbsp;&nbsp;&nbsp;&nbsp; p &nbsp;&nbsp;&nbsp;&nbsp;
a(href='https://github.com/FreeCodeCamp/freecodecamp/wiki/beta' target='_blank') You're using our experimental beta site. None of your progress here will be saved. Please click this to learn more. a(href='https://github.com/FreeCodeCamp/freecodecamp/wiki/beta' target='_blank') You're using our experimental beta site. None of your progress here will be saved. Please click this to learn more.
@ -28,10 +27,10 @@ block content
.col-xs-12.col-sm-8.col-sm-offset-2 .col-xs-12.col-sm-8.col-sm-offset-2
h3 800 Hours of Practice: h3 800 Hours of Practice:
ol ol
for challengeBlock in challengeList for challengeBlock in blocks
.row .row
if (user) if (user)
if (challengeBlock.completed.length === challengeBlock.challenges.length) if (challengeBlock.completed === 100)
.hidden-xs.col-sm-3.col-md-2.text-primary.ion-checkmark-circled.padded-ionic-icon.text-center.large-p.negative-10 .hidden-xs.col-sm-3.col-md-2.text-primary.ion-checkmark-circled.padded-ionic-icon.text-center.large-p.negative-10
.col-xs-12.col-sm-9.col-md-10 .col-xs-12.col-sm-9.col-md-10
li.large-p.faded.negative-10 li.large-p.faded.negative-10
@ -39,7 +38,7 @@ block content
else else
.hidden-xs.col-sm-3.col-md-2 .hidden-xs.col-sm-3.col-md-2
.progress.progress-bar-padding.text-center.thin-progress-bar .progress.progress-bar-padding.text-center.thin-progress-bar
.progress-bar(role='progressbar', aria-valuenow=((challengeBlock.completed.length / challengeBlock.challenges.length) * 100), aria-valuemin='0', aria-valuemax='100', style='width: ' + ((challengeBlock.completed.length / challengeBlock.challenges.length) * 100) + '%;') .progress-bar(role='progressbar', aria-valuenow=(challengeBlock.completed), aria-valuemin='0', aria-valuemax='100', style='width: ' + challengeBlock.completed + '%;')
.col-xs-12.col-sm-9.col-md-10 .col-xs-12.col-sm-9.col-md-10
li.large-p.negative-10 li.large-p.negative-10
a(href='#' + challengeBlock.dashedName)= challengeBlock.name a(href='#' + challengeBlock.dashedName)= challengeBlock.name
@ -74,7 +73,7 @@ block content
li.large-p.negative-10 300-hour Nonprofit Project li.large-p.negative-10 300-hour Nonprofit Project
hr hr
for challengeBlock in challengeList for challengeBlock in blocks
.row .row
a(href='#' name=challengeBlock.dashedName) a(href='#' name=challengeBlock.dashedName)
.spacer.negative-55 .spacer.negative-55
@ -86,12 +85,12 @@ block content
.col-xs-12 .col-xs-12
ol ol
for challenge in challengeBlock.challenges for challenge in challengeBlock.challenges
if completedChallengeList.indexOf(challenge.id || challenge._id) > -1 if challenge.completed
.row .row
.hidden-xs.col-sm-3.col-md-2.text-primary.ion-checkmark-circled.padded-ionic-icon.text-center.large-p.negative-10 .hidden-xs.col-sm-3.col-md-2.text-primary.ion-checkmark-circled.padded-ionic-icon.text-center.large-p.negative-10
.col-xs-12.col-sm-9.col-md-10 .col-xs-12.col-sm-9.col-md-10
li.faded.large-p.negative-10 li.faded.large-p.negative-10
a(href="/challenges/#{challenge.dashedName}")= challenge.name a(href="/challenges/#{challenge.dashedName}")= challenge.title
else else
.row .row
@ -99,7 +98,7 @@ block content
span.negative-10 span.negative-10
.col-xs-12.col-sm-9.col-md-10 .col-xs-12.col-sm-9.col-md-10
li.large-p.negative-10 li.large-p.negative-10
a(href="/challenges/#{challenge.dashedName}")= challenge.name a(href="/challenges/#{challenge.dashedName}")= challenge.title
//#announcementModal.modal(tabindex='-1') //#announcementModal.modal(tabindex='-1')
// .modal-dialog.animated.fadeInUp.fast-animation // .modal-dialog.animated.fadeInUp.fast-animation