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": {
"type": "string"
},
"block": {
"type": "string"
},
"difficulty": {
"type": "string"
},

View File

@ -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;
});

View File

@ -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
);
}
};

View File

@ -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 = {};

View File

@ -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 &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.
@ -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