fix challenge naming scheme
This commit is contained in:
@ -25,6 +25,9 @@
|
|||||||
"dashedName": {
|
"dashedName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"block": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"difficulty": {
|
"difficulty": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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 = {};
|
||||||
|
@ -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
|
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.
|
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
|
||||||
|
Reference in New Issue
Block a user