challenges: sync score to challenge, lotsa bug fixes

This commit is contained in:
Tyler Renelle
2013-10-27 15:27:01 -07:00
parent e52d0a156a
commit a078889d58
7 changed files with 45 additions and 83 deletions

View File

@@ -119,50 +119,6 @@ habitrpg.controller("ChallengesCtrl", ['$scope', '$rootScope', 'User', 'Challeng
// chart = new google.visualization.LineChart $(".challenge-#{chal.id}-member-#{member.id}-history-#{task.id}")[0] // chart = new google.visualization.LineChart $(".challenge-#{chal.id}-member-#{member.id}-history-#{task.id}")[0]
// chart.draw(data, options) // chart.draw(data, options)
/**
* Sync user to challenge (when they score, add to statistics)
*/
// TODO this needs to be moved to the server. Either:
// 1. Calculate on load (simplest, but bad performance)
// 2. Updated from user score API
// app.model.on("change", "_page.user.priv.tasks.*.value", function(id, value, previous, passed) {
// /* Sync to challenge, but do it later*/
//
// var _this = this;
// return async.nextTick(function() {
// var chal, chalTask, chalUser, ctx, cu, model, pub, task, tobj;
// model = app.model;
// ctx = {
// model: model
// };
// task = model.at("_page.user.priv.tasks." + id);
// tobj = task.get();
// pub = model.get("_page.user.pub");
// if (((chalTask = helpers.taskInChallenge.call(ctx, tobj)) != null) && chalTask.get()) {
// chalTask.increment("value", value - previous);
// chal = model.at("groups." + tobj.group.id + ".challenges." + tobj.challenge);
// chalUser = function() {
// return helpers.indexedAt.call(ctx, chal.path(), 'members', {
// id: pub.id
// });
// };
// cu = chalUser();
// if (!(cu != null ? cu.get() : void 0)) {
// chal.push("members", {
// id: pub.id,
// name: model.get(pub.profile.name)
// });
// cu = model.at(chalUser());
// } else {
// cu.set('name', pub.profile.name);
// }
// return cu.set("" + tobj.type + "s." + tobj.id, {
// value: tobj.value,
// history: tobj.history
// });
// }
// });
// });
/* /*
-------------------------- --------------------------
@@ -170,15 +126,6 @@ habitrpg.controller("ChallengesCtrl", ['$scope', '$rootScope', 'User', 'Challeng
-------------------------- --------------------------
*/ */
$scope.unlink = function(task, keep) {
// TODO move this to userServices, turn userSerivces.user into ng-resource
$http.post(API_URL + '/api/v1/user/task/' + task.id + '/unlink', {keep:keep})
.success(function(){
debugger
User.log({});
});
};
$scope.unsubscribe = function(keep) { $scope.unsubscribe = function(keep) {
if (keep == 'cancel') { if (keep == 'cancel') {
$scope.selectedChal = undefined; $scope.selectedChal = undefined;

View File

@@ -1,7 +1,7 @@
"use strict"; "use strict";
habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User', 'Algos', 'Helpers', 'Notification', habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User', 'Algos', 'Helpers', 'Notification', '$http', 'API_URL',
function($scope, $rootScope, $location, User, Algos, Helpers, Notification) { function($scope, $rootScope, $location, User, Algos, Helpers, Notification, $http, API_URL) {
$scope.score = function(task, direction) { $scope.score = function(task, direction) {
if (task.type === "reward" && User.user.stats.gp < task.value){ if (task.type === "reward" && User.user.stats.gp < task.value){
return Notification.text('Not enough GP.'); return Notification.text('Not enough GP.');
@@ -94,6 +94,14 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User', '
$scope.editing = false; $scope.editing = false;
}; };
$scope.unlink = function(task, keep) {
// TODO move this to userServices, turn userSerivces.user into ng-resource
$http.post(API_URL + '/api/v1/user/task/' + task.id + '/unlink?keep=' + keep)
.success(function(){
User.log({});
});
};
/* /*
------------------------ ------------------------
Items Items

View File

@@ -139,7 +139,7 @@ var syncChalToUser = function(chal, user) {
_.each(chal.tasks, function(task){ _.each(chal.tasks, function(task){
var type = task.type; var type = task.type;
_.defaults(task, {tags: tags, challenge:{}}); _.defaults(task, {tags: tags, challenge:{}});
_.defaults(task.challenge, {id:chal._id, broken:false}); _.defaults(task.challenge, {id:chal._id});
if (user.tasks[task.id]) { if (user.tasks[task.id]) {
_.merge(user.tasks[task.id], keepAttrs(task)); _.merge(user.tasks[task.id], keepAttrs(task));
} else { } else {
@@ -213,8 +213,6 @@ api.leave = function(req, res){
Challenge.findByIdAndUpdate(cid, {$pull:{members:user._id}}, cb); Challenge.findByIdAndUpdate(cid, {$pull:{members:user._id}}, cb);
}, },
function(chal, cb){ function(chal, cb){
// Remove challenge from user
//User.findByIdAndUpdate(user._id, {$pull:{challenges:cid}}, cb);
var i = user.challenges.indexOf(cid) var i = user.challenges.indexOf(cid)
if (~i) user.challenges.splice(i,1); if (~i) user.challenges.splice(i,1);
unlink(user, chal._id, keep) unlink(user, chal._id, keep)
@@ -229,7 +227,11 @@ api.leave = function(req, res){
}); });
} }
api.unlink = function(req, res) { api.unlink = function(req, res, next) {
// they're scoring the task - commented out, we probably don't need it due to route ordering in api.js
//var urlParts = req.originalUrl.split('/');
//if (_.contains(['up','down'], urlParts[urlParts.length -1])) return next();
var user = res.locals.user; var user = res.locals.user;
var tid = req.params.id; var tid = req.params.id;
var cid = user.tasks[tid].challenge.id; var cid = user.tasks[tid].challenge.id;

View File

@@ -1,8 +1,5 @@
/* @see ./routes.coffee for routing*/ /* @see ./routes.coffee for routing*/
// fixme remove this junk, was coffeescript compiled (probably for IE8 compat)
var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
var url = require('url'); var url = require('url');
var ipn = require('paypal-ipn'); var ipn = require('paypal-ipn');
var _ = require('lodash'); var _ = require('lodash');
@@ -16,6 +13,7 @@ var check = validator.check;
var sanitize = validator.sanitize; var sanitize = validator.sanitize;
var User = require('./../models/user').model; var User = require('./../models/user').model;
var Group = require('./../models/group').model; var Group = require('./../models/group').model;
var Challenge = require('./../models/challenge').model;
var api = module.exports; var api = module.exports;
// FIXME put this in a proper location // FIXME put this in a proper location
@@ -82,6 +80,16 @@ function addTask(user, task) {
--------------- ---------------
*/ */
var syncScoreToChallenge = function(task, delta){
if (!task.challenge || !task.challenge.id) return;
Challenge.findById(task.challenge.id, function(err, chal){
if (err) throw err;
var t = chal.tasks[task.id]
t.value += delta;
t.history.push({value: t.value, date: +new Date});
chal.save();
});
}
/** /**
This is called form deprecated.coffee's score function, and the req.headers are setup properly to handle the login This is called form deprecated.coffee's score function, and the req.headers are setup properly to handle the login
@@ -96,6 +104,7 @@ api.scoreTask = function(req, res, next) {
// Send error responses for improper API call // Send error responses for improper API call
if (!id) return res.json(500, {err: ':id required'}); if (!id) return res.json(500, {err: ':id required'});
if (direction !== 'up' && direction !== 'down') { if (direction !== 'up' && direction !== 'down') {
if (direction == 'unlink') return next();
return res.json(500, {err: ":direction must be 'up' or 'down'"}); return res.json(500, {err: ":direction must be 'up' or 'down'"});
} }
// If exists already, score it // If exists already, score it
@@ -124,13 +133,16 @@ api.scoreTask = function(req, res, next) {
} }
task = user.tasks[id]; task = user.tasks[id];
var delta = algos.score(user, task, direction); var delta = algos.score(user, task, direction);
//user.markModified('flags'); //user.markModified('flags');
user.save(function(err, saved) { user.save(function(err, saved) {
if (err) return res.json(500, {err: err}); if (err) return res.json(500, {err: err});
res.json(200, _.extend({ res.json(200, _.extend({
delta: delta delta: delta
}, saved.toJSON().stats)); }, saved.toJSON().stats));
}); });
// if it's a challenge task, sync the score
syncScoreToChallenge(task, delta);
}; };
/** /**

View File

@@ -28,8 +28,8 @@ var TaskSchema = new Schema({
streak: {type: Number, 'default': 0}, streak: {type: Number, 'default': 0},
challenge: { challenge: {
id: {type: 'String', ref:'Challenge'}, id: {type: 'String', ref:'Challenge'},
broken: {type: Boolean, 'default': false} broken: String // CHALLENGE_DELETED, TASK_DELETED, UNSUBSCRIBED, etc
// group: {type: 'Strign', redf: 'Group'} // if we restore this, rename `id` above to `challenge` // group: {type: 'Strign', ref: 'Group'} // if we restore this, rename `id` above to `challenge`
} }
}); });

View File

@@ -2,22 +2,15 @@
.span2.well .span2.well
h4 Filters h4 Filters
ul ul
li(ng-repeat='group in groups')
input(type='checkbox', ng-model='search.group')
| {{group.name}}
li li
input(type='checkbox', checked='checked') input(type='checkbox', ng-model='search.members')
.label.label-warning todo | Subscribed (TODO)
| Party
li li
input(type='checkbox', checked='checked') input(type='checkbox', ng-model='search.members')
.label.label-warning todo | Available (TODO)
| (list groups)
li
input(type='checkbox', checked='checked')
.label.label-warning todo
| Subscribed
li
input(type='checkbox', checked='checked')
.label.label-warning todo
| Available
.span10 .span10
// Creation form // Creation form
a.btn.btn-success(ng-click='create()') Create Challenge a.btn.btn-success(ng-click='create()') Create Challenge
@@ -33,7 +26,7 @@
habitrpg-tasks(main=false, obj='newChallenge') habitrpg-tasks(main=false, obj='newChallenge')
// Challenges list // Challenges list
.accordion-group(ng-repeat='challenge in challenges', ng-init='locked=true') .accordion-group(ng-repeat='challenge in challenges | filter:search', ng-init='locked=true')
.accordion-heading .accordion-heading
ul.pull-right.challenge-accordion-header-specs ul.pull-right.challenge-accordion-header-specs
li {{challenge.members.length}} Subscribers li {{challenge.members.length}} Subscribers

View File

@@ -64,15 +64,15 @@ li(ng-repeat='task in list.tasks', class='task {{taskClasses(task, user.filters,
div(ng-if='task.challenge.broken=="TASK_DELETED"') div(ng-if='task.challenge.broken=="TASK_DELETED"')
p Broken Challenge Link: this task was part of a challenge, but has been removed from it. What would you like to do? p Broken Challenge Link: this task was part of a challenge, but has been removed from it. What would you like to do?
p p
a(ng-click="unlink(task, 'keep')") Keep It a(ng-click='unlink(task, "keep")') Keep It
| &nbsp;|&nbsp; | &nbsp;|&nbsp;
a(ng-click="remove(list, $index)") Remove It a(ng-click="remove(list, $index)") Remove It
div(ng-if='task.challenge.broken=="CHALLENGE_DELETED"') div(ng-if='task.challenge.broken=="CHALLENGE_DELETED"')
p Broken Challenge Link: this task was part of a challenge, but the challenge (or group) has been deleted. What to do with the orphan tasks? p Broken Challenge Link: this task was part of a challenge, but the challenge (or group) has been deleted. What to do with the orphan tasks?
p p
a(ng-click="unlink(task 'keep-all')") Keep Them a(ng-click='unlink(task, "keep-all")') Keep Them
| &nbsp;|&nbsp; | &nbsp;|&nbsp;
a(ng-click="unlink(task, 'remove-all')") Remove Them a(ng-click='unlink(task, "remove-all")') Remove Them
//-div(ng-if='task.challenge.broken=="UNSUBSCRIBED"') //-div(ng-if='task.challenge.broken=="UNSUBSCRIBED"')
p Broken Challenge Link: this task was part of a challenge, but you have unsubscribed from the challenge. What to do with the orphan tasks? p Broken Challenge Link: this task was part of a challenge, but you have unsubscribed from the challenge. What to do with the orphan tasks?
p p