<commit_before>var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt');
var SALT_WORK_FACTOR = 10;
var _ = require('lodash');
var moment = require('moment');
var hljs = require('./highlight.js');
var cheerio = require('cheerio');

var enet = require('./eventnet');

var UserSchema = new Schema({
    username: { type: String, required: true, index: { unique: true } },
    password: { type: String, required: true },
    isAdmin: { type: Boolean, default: false },
    bestTime: { type: Number },
    bestSpeed: { type: Number },
    gamesWon: { type: Number },
    totalGames: { type: Number },
    currentGame: { type: Schema.ObjectId, ref: 'GameSchema' }
});

// TODO: Look into using async library to avoid the spaghetti nesting
UserSchema.pre('save', function(next) {
    var user = this;

    if (!user.isModified('password')) {
        return next();
    }
    bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
        if (err) {
            console.log(err);
            return next(err);
        }
        bcrypt.hash(user.password, salt, function(err, hash) {
            if (err) {
                console.log(err);
                return next(err);
            }
            user.password = hash;
            next();
        });
    });
});

UserSchema.methods.comparePassword = function(candidate, callback) {
    bcrypt.compare(candidate, this.password, function(err, isMatch) {
        if (err) {
            console.log(err);
            return callback(err);
        }
        callback(null, isMatch);
    });
};

UserSchema.methods.joinGame = function(game, callback) {
    var user = this;

    if (!game.isJoinable) {
        return callback('game is not joinable', false);
    }

    // A player cannot player against himself
    if (!game.isNew && _.contains(game.players, user._id)) {
        return callback('cannot play against self', false);
    }

    var wasNew = game.isNew;
    user.currentGame = game._id;
    game.players.push(user._id);
    game.numPlayers += 1;

    game.save(function(err) {
        if (err) {
            console.log(err);
            return callback('error saving game', false);
        }
        if (wasNew) {
            enet.emit('games:new', game);
        } else {
            enet.emit('games:update', game);
        }
        user.save(function(err) {
            if (err) {
                console.log(err);
                return callback('error saving user', false);
            }
            enet.emit('users:update', user);
            callback(null, true, game);
        });
    });
};

UserSchema.methods.createGame = function(opts, callback) {
    var user = this;
    var game = new Game();
    _.forOwn(opts, function(v, k) {
        if (k in game) {
            game[k] = v;
        }
    });

    game.setStatus('waiting');
    game.isJoinable = true;
    game.isComplete = false;
    game.creator = user._id;

    user.joinGame(game, callback);
};

UserSchema.methods.quitCurrentGame = function(callback) {
    var user = this;
    if (user.currentGame) {
        Game.findById(user.currentGame, function(err, game) {
            if (err) {
                console.log(err);
                return callback('error retrieving game');
            }
            if (game) {
                game.players.remove(user._id);
                game.numPlayers = game.numPlayers <= 0 ? 0 : game.numPlayers - 1;
                if (game.numPlayers === 0) {
                    game.isComplete = true;
                }
                game.save(function(err) {
                    if (err) {
                        console.log(err);
                        return callback('error saving game');
                    }
                    if (game.isComplete) {
                        enet.emit('games:remove', game);
                    } else {
                        enet.emit('games:update', game);
                    }

                    user.currentGame = undefined;
                    user.save(function(err) {
                        if (err) {
                            console.log(err);
                            return callback('error saving user');
                        }
                        callback(null, game);
                    });
                });
            }
        });
    } else {
        callback();
    }
};

var LangSchema = new Schema({
    key: { type: String },
    name: { type: String },
    projectName: { type: String },
    order: { type: Number },
    exercises: [Schema.ObjectId]
});

LangSchema.methods.randomExercise = function() {
    return this.exercises[Math.floor(Math.random() * this.exercises.length)];
};<commit_msg>Disallow multiple game creation by a single user<commit_after>var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt');
var SALT_WORK_FACTOR = 10;
var _ = require('lodash');
var moment = require('moment');
var hljs = require('./highlight.js');
var cheerio = require('cheerio');

var enet = require('./eventnet');

var UserSchema = new Schema({
    username: { type: String, required: true, index: { unique: true } },
    password: { type: String, required: true },
    isAdmin: { type: Boolean, default: false },
    bestTime: { type: Number },
    bestSpeed: { type: Number },
    gamesWon: { type: Number },
    totalGames: { type: Number },
    currentGame: { type: Schema.ObjectId, ref: 'GameSchema' }
});

// TODO: Look into using async library to avoid the spaghetti nesting
UserSchema.pre('save', function(next) {
    var user = this;

    if (!user.isModified('password')) {
        return next();
    }
    bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
        if (err) {
            console.log(err);
            return next(err);
        }
        bcrypt.hash(user.password, salt, function(err, hash) {
            if (err) {
                console.log(err);
                return next(err);
            }
            user.password = hash;
            next();
        });
    });
});

UserSchema.methods.comparePassword = function(candidate, callback) {
    bcrypt.compare(candidate, this.password, function(err, isMatch) {
        if (err) {
            console.log(err);
            return callback(err);
        }
        callback(null, isMatch);
    });
};

UserSchema.methods.joinGame = function(game, callback) {
    var user = this;

    if (!game.isJoinable) {
        return callback('game is not joinable', false);
    }

    // A player cannot play against himself
    if (!game.isNew && _.contains(game.players, user._id)) {
        return callback('cannot play against self', false);
    }

    var wasNew = game.isNew;
    user.currentGame = game._id;
    game.players.push(user._id);
    game.numPlayers += 1;

    game.save(function(err) {
        if (err) {
            console.log(err);
            return callback('error saving game', false);
        }
        if (wasNew) {
            enet.emit('games:new', game);
        } else {
            enet.emit('games:update', game);
        }
        user.save(function(err) {
            if (err) {
                console.log(err);
                return callback('error saving user', false);
            }
            enet.emit('users:update', user);
            return callback(null, true, game);
        });
    });
};

UserSchema.methods.createGame = function(opts, callback) {
    var user = this;

    // User cannot create a game when he is already in one
    if (user.currentGame) {
        return callback('already in a game', false);
    }

    var game = new Game();
    _.forOwn(opts, function(v, k) {
        if (k in game) {
            game[k] = v;
        }
    });

    game.setStatus('waiting');
    game.isJoinable = true;
    game.isComplete = false;
    game.creator = user._id;

    return user.joinGame(game, callback);
};

UserSchema.methods.quitCurrentGame = function(callback) {
    var user = this;
    if (user.currentGame) {
        Game.findById(user.currentGame, function(err, game) {
            if (err) {
                console.log(err);
                return callback('error retrieving game');
            }
            if (game) {
                game.players.remove(user._id);
                game.numPlayers = game.numPlayers <= 0 ? 0 : game.numPlayers - 1;
                if (game.numPlayers === 0) {
                    game.isComplete = true;
                }
                game.save(function(err) {
                    if (err) {
                        console.log(err);
                        return callback('error saving game');
                    }
                    if (game.isComplete) {
                        enet.emit('games:remove', game);
                    } else {
                        enet.emit('games:update', game);
                    }

                    user.currentGame = undefined;
                    user.save(function(err) {
                        if (err) {
                            console.log(err);
                            return callback('error saving user');
                        }
                        return callback(null, game);
                    });
                });
            }
        });
    } else {
        return callback();
    }
};

var LangSchema = new Schema({
    key: { type: String },
    name: { type: String },
    projectName: { type: String },
    order: { type: Number },
    exercises: [Schema.ObjectId]
});

LangSchema.methods.randomExercise = function() {
    return this.exercises[Math.floor(Math.random() * this.exercises.length)];
};
{'fraction_non_alphanumeric': 0.13147742675424878, 'fraction_numerical': 0.0014597018037743718, 'mean_word_length': 1.6996904024767803, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5843453', 'n_tokens_mistral': 2777, 'n_tokens_neox': 2657, 'n_words': 629}