fix challenge naming scheme
This commit is contained in:
@ -25,6 +25,9 @@
|
||||
"dashedName": {
|
||||
"type": "string"
|
||||
},
|
||||
"block": {
|
||||
"type": "string"
|
||||
},
|
||||
"difficulty": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -39,6 +39,8 @@ Challenge.destroyAll(function(err, info) {
|
||||
challenges.forEach(function(file) {
|
||||
var challengeSpec = require('./challenges/' + file);
|
||||
var order = challengeSpec.order;
|
||||
var block = challengeSpec.name;
|
||||
|
||||
var challenges = challengeSpec.challenges
|
||||
.map(function(challenge, index) {
|
||||
// NOTE(berks): add title for displaying in views
|
||||
@ -46,11 +48,14 @@ Challenge.destroyAll(function(err, info) {
|
||||
_.capitalize(challenge.type) +
|
||||
': ' +
|
||||
challenge.title.replace(/[^a-zA-Z0-9\s]/g, '');
|
||||
|
||||
challenge.dashedName = challenge.name
|
||||
.toLowerCase()
|
||||
.replace(/\:/g, '')
|
||||
.replace(/\s/g, '-');
|
||||
challenge.order = +('' + order + (index + 1));
|
||||
challenge.block = block;
|
||||
|
||||
return challenge;
|
||||
});
|
||||
|
||||
|
@ -1,17 +1,24 @@
|
||||
var R = require('ramda'),
|
||||
Rx = require('rx'),
|
||||
assign = require('object.assign'),
|
||||
debug = require('debug')('freecc:challenges'),
|
||||
utils = require('../utils'),
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import R from 'ramda';
|
||||
import { Observable } from 'rx';
|
||||
import assign from 'object.assign';
|
||||
import debugFactory from 'debug';
|
||||
import utils from '../utils';
|
||||
|
||||
// this would be so much cleaner with destructering...
|
||||
saveUser = require('../utils/rx').saveUser,
|
||||
observableQueryFromModel = require('../utils/rx').observableQueryFromModel,
|
||||
import {
|
||||
saveUser,
|
||||
observeMethod,
|
||||
observableQueryFromModel
|
||||
} from '../utils/rx';
|
||||
|
||||
userMigration = require('../utils/middleware').userMigration,
|
||||
ifNoUserRedirectTo = require('../utils/middleware').ifNoUserRedirectTo,
|
||||
ifNoUserSend = require('../utils/middleware').ifNoUserSend;
|
||||
import {
|
||||
userMigration,
|
||||
ifNoUserRedirectTo,
|
||||
ifNoUserSend
|
||||
} from '../utils/middleware';
|
||||
|
||||
const debug = debugFactory('freecc:challenges');
|
||||
var challengeMapWithNames = utils.getChallengeMapWithNames();
|
||||
var challengeMapWithIds = utils.getChallengeMapWithIds();
|
||||
var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames();
|
||||
@ -22,6 +29,10 @@ var unDasherize = utils.unDasherize;
|
||||
|
||||
var getMDNLinks = utils.getMDNLinks;
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
}
|
||||
|
||||
function updateUserProgress(user, challengeId, completedChallenge) {
|
||||
var alreadyCompleted = user.completedChallenges.some(({ id }) => {
|
||||
return id === challengeId;
|
||||
@ -37,12 +48,18 @@ function updateUserProgress(user, challengeId, completedChallenge) {
|
||||
}
|
||||
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
var Challenge = app.models.Challenge;
|
||||
var User = app.models.User;
|
||||
var redirectNonUser =
|
||||
ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works');
|
||||
var send200toNonUser = ifNoUserSend(true);
|
||||
const router = app.loopback.Router();
|
||||
|
||||
const Challenge = app.models.Challenge;
|
||||
const findChallenge$ = observeMethod(Challenge, 'find');
|
||||
|
||||
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(
|
||||
'/completed-challenge/',
|
||||
@ -182,7 +199,7 @@ module.exports = function(app) {
|
||||
.map(function(key) {
|
||||
return challengeMapWithIds[key]
|
||||
.filter(function(elem) {
|
||||
return elem === ('' + challenge.id);
|
||||
return elem === '' + challenge.id;
|
||||
})
|
||||
.map(function() {
|
||||
return key;
|
||||
@ -266,7 +283,7 @@ module.exports = function(app) {
|
||||
}
|
||||
})
|
||||
.withLatestFrom(
|
||||
Rx.Observable.just(req.user),
|
||||
Observable.just(req.user),
|
||||
function(pairedWith, user) {
|
||||
return {
|
||||
user: user,
|
||||
@ -289,7 +306,7 @@ module.exports = function(app) {
|
||||
// not iterate users
|
||||
.flatMap(function(dats) {
|
||||
debug('flatmap');
|
||||
return Rx.Observable.from([dats.user, dats.pairedWith]);
|
||||
return Observable.from([dats.user, dats.pairedWith]);
|
||||
})
|
||||
// save 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 {
|
||||
user: user,
|
||||
pairedWith: pairedWith
|
||||
@ -408,7 +425,7 @@ module.exports = function(app) {
|
||||
);
|
||||
})
|
||||
.flatMap(function({ user, pairedWith }) {
|
||||
return Rx.Observable.from([user, pairedWith]);
|
||||
return Observable.from([user, pairedWith]);
|
||||
})
|
||||
// save users
|
||||
.flatMap(function(user) {
|
||||
@ -428,50 +445,73 @@ module.exports = function(app) {
|
||||
);
|
||||
}
|
||||
|
||||
function challengeMap(req, res, next) {
|
||||
var completedList = [];
|
||||
function challengeMap({ user = {} }, res, next) {
|
||||
const daysRunning = moment().diff(new Date('10/15/2014'), 'days');
|
||||
|
||||
if (req.user) {
|
||||
completedList = req.user.completedChallenges;
|
||||
}
|
||||
// if user
|
||||
// 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
|
||||
.map(function(challenge) {
|
||||
// backwards compatibility
|
||||
return (challenge.id || challenge._id);
|
||||
});
|
||||
var challengeList = utils.
|
||||
getChallengeMapForDisplay(completedChallengeList);
|
||||
const query = {
|
||||
order: 'order ASC'
|
||||
};
|
||||
|
||||
Object.keys(challengeList).forEach(function(key) {
|
||||
challengeList[key].completed = challengeList[key]
|
||||
.challenges.filter(function(elem) {
|
||||
// backwards compatibility hack
|
||||
return completedChallengeList.indexOf(elem.id || elem._id) > -1;
|
||||
});
|
||||
});
|
||||
// create a stream of all the challenges
|
||||
const challenge$ = findChallenge$(query)
|
||||
.flatMap(challenges => Observable.from(challenges))
|
||||
.shareReplay();
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
}
|
||||
// create a stream of an array of all the challenge blocks
|
||||
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');
|
||||
var date2 = new Date();
|
||||
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
|
||||
var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
|
||||
return {
|
||||
name: blockArray[0].block,
|
||||
dashedName: dasherize(blockArray[0].block),
|
||||
challenges: blockArray,
|
||||
completed: completedCount / blockArray.length * 100
|
||||
};
|
||||
})
|
||||
// turn stream of blocks into a stream of an array
|
||||
.toArray();
|
||||
|
||||
User.count(function(err, camperCount) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
res.render('challengeMap/show', {
|
||||
daysRunning: daysRunning,
|
||||
camperCount: numberWithCommas(camperCount),
|
||||
title: "A map of all Free Code Camp's Challenges",
|
||||
challengeList: challengeList,
|
||||
completedChallengeList: completedChallengeList
|
||||
});
|
||||
});
|
||||
Observable.combineLatest(
|
||||
camperCount$,
|
||||
blocks$,
|
||||
(camperCount, blocks) => ({ camperCount, blocks })
|
||||
)
|
||||
.subscribe(
|
||||
({ camperCount, blocks }) => {
|
||||
res.render('challengeMap/show', {
|
||||
blocks,
|
||||
daysRunning,
|
||||
camperCount,
|
||||
title: "A map of all Free Code Camp's Challenges"
|
||||
});
|
||||
},
|
||||
next
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -19,17 +19,6 @@ var allNonprofitNames,
|
||||
challengeMapWithNames, allChallengeIds,
|
||||
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() {
|
||||
if (!challengeMap) {
|
||||
var localChallengeMap = {};
|
||||
|
@ -1,8 +1,7 @@
|
||||
extends ../layout
|
||||
block content
|
||||
script.
|
||||
var completedChallenges = !{JSON.stringify(completedChallengeList)};
|
||||
var challengeList = !{JSON.stringify(challengeList)};
|
||||
var challenges = !{JSON.stringify(challenges)};
|
||||
.bg-danger.default-border-radius
|
||||
p
|
||||
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
|
||||
h3 800 Hours of Practice:
|
||||
ol
|
||||
for challengeBlock in challengeList
|
||||
for challengeBlock in blocks
|
||||
.row
|
||||
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
|
||||
.col-xs-12.col-sm-9.col-md-10
|
||||
li.large-p.faded.negative-10
|
||||
@ -39,7 +38,7 @@ block content
|
||||
else
|
||||
.hidden-xs.col-sm-3.col-md-2
|
||||
.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
|
||||
li.large-p.negative-10
|
||||
a(href='#' + challengeBlock.dashedName)= challengeBlock.name
|
||||
@ -74,7 +73,7 @@ block content
|
||||
li.large-p.negative-10 300-hour Nonprofit Project
|
||||
hr
|
||||
|
||||
for challengeBlock in challengeList
|
||||
for challengeBlock in blocks
|
||||
.row
|
||||
a(href='#' name=challengeBlock.dashedName)
|
||||
.spacer.negative-55
|
||||
@ -86,12 +85,12 @@ block content
|
||||
.col-xs-12
|
||||
ol
|
||||
for challenge in challengeBlock.challenges
|
||||
if completedChallengeList.indexOf(challenge.id || challenge._id) > -1
|
||||
if challenge.completed
|
||||
.row
|
||||
.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
|
||||
li.faded.large-p.negative-10
|
||||
a(href="/challenges/#{challenge.dashedName}")= challenge.name
|
||||
a(href="/challenges/#{challenge.dashedName}")= challenge.title
|
||||
|
||||
else
|
||||
.row
|
||||
@ -99,7 +98,7 @@ block content
|
||||
span.negative-10
|
||||
.col-xs-12.col-sm-9.col-md-10
|
||||
li.large-p.negative-10
|
||||
a(href="/challenges/#{challenge.dashedName}")= challenge.name
|
||||
a(href="/challenges/#{challenge.dashedName}")= challenge.title
|
||||
|
||||
//#announcementModal.modal(tabindex='-1')
|
||||
// .modal-dialog.animated.fadeInUp.fast-animation
|
||||
|
Reference in New Issue
Block a user