Merge branch 'master' into production

This commit is contained in:
phoenixeliot 2016-06-06 15:53:33 -07:00
commit 70c139a600
16 changed files with 79 additions and 50 deletions

View file

@ -71,6 +71,8 @@ module.exports = class TeacherClassView extends RootView
@singleStudentLevelProgressDotTemplate = require 'templates/teachers/hovers/progress-dot-single-student-level' @singleStudentLevelProgressDotTemplate = require 'templates/teachers/hovers/progress-dot-single-student-level'
@allStudentsLevelProgressDotTemplate = require 'templates/teachers/hovers/progress-dot-all-students-single-level' @allStudentsLevelProgressDotTemplate = require 'templates/teachers/hovers/progress-dot-all-students-single-level'
@debouncedRender = _.debounce @render
@state = new State(@getInitialState()) @state = new State(@getInitialState())
@updateHash @state.get('activeTab') # TODO: Don't push to URL history (maybe don't use url fragment for default tab) @updateHash @state.get('activeTab') # TODO: Don't push to URL history (maybe don't use url fragment for default tab)
@ -120,11 +122,6 @@ module.exports = class TeacherClassView extends RootView
@attachMediatorEvents() @attachMediatorEvents()
attachMediatorEvents: () -> attachMediatorEvents: () ->
@listenTo @state, 'sync change', ->
if _.isEmpty(_.omit(@state.changed, 'searchTerm'))
@renderSelectors('#enrollment-status-table')
else
@render()
# Model/Collection events # Model/Collection events
@listenTo @classroom, 'sync change update', -> @listenTo @classroom, 'sync change update', ->
classCode = @classroom.get('codeCamel') or @classroom.get('code') classCode = @classroom.get('codeCamel') or @classroom.get('code')
@ -137,7 +134,6 @@ module.exports = class TeacherClassView extends RootView
@state.set selectedCourse: @courses.first() unless @state.get('selectedCourse') @state.set selectedCourse: @courses.first() unless @state.get('selectedCourse')
@listenTo @courseInstances, 'sync change update', -> @listenTo @courseInstances, 'sync change update', ->
@setCourseMembers() @setCourseMembers()
@render() # TODO: use state
@listenTo @courseInstances, 'add-members', -> @listenTo @courseInstances, 'add-members', ->
noty text: $.i18n.t('teacher.assigned'), layout: 'center', type: 'information', killer: true, timeout: 5000 noty text: $.i18n.t('teacher.assigned'), layout: 'center', type: 'information', killer: true, timeout: 5000
@listenTo @students, 'sync change update add remove reset', -> @listenTo @students, 'sync change update add remove reset', ->
@ -149,7 +145,6 @@ module.exports = class TeacherClassView extends RootView
@state.set students: @students @state.set students: @students
@listenTo @students, 'sort', -> @listenTo @students, 'sort', ->
@state.set students: @students @state.set students: @students
@render()
@listenTo @, 'course-select:change', ({ selectedCourse }) -> @listenTo @, 'course-select:change', ({ selectedCourse }) ->
@state.set selectedCourse: selectedCourse @state.set selectedCourse: selectedCourse
@ -162,6 +157,15 @@ module.exports = class TeacherClassView extends RootView
onLoaded: -> onLoaded: ->
@removeDeletedStudents() # TODO: Move this to mediator listeners? For both classroom and students? @removeDeletedStudents() # TODO: Move this to mediator listeners? For both classroom and students?
@calculateProgressAndLevels() @calculateProgressAndLevels()
# render callback setup
@listenTo @courseInstances, 'sync change update', @debouncedRender
@listenTo @state, 'sync change', ->
if _.isEmpty(_.omit(@state.changed, 'searchTerm'))
@renderSelectors('#enrollment-status-table')
else
@debouncedRender()
@listenTo @students, 'sort', @debouncedRender
super() super()
afterRender: -> afterRender: ->

View file

@ -260,7 +260,10 @@ module.exports.ThangTypeNode = ThangTypeNode = class ThangTypeNode extends Treem
thangType?.name or '...' thangType?.name or '...'
getThangTypes: -> getThangTypes: ->
return if ThangTypeNode.thangTypesCollection if ThangTypeNode.thangTypesCollection
if not @constructor.thangTypes
@processThangTypes(ThangTypeNode.thangTypesCollection)
return
ThangTypeNode.thangTypesCollection = new CocoCollection([], { ThangTypeNode.thangTypesCollection = new CocoCollection([], {
url: '/db/thang.type' url: '/db/thang.type'
project:['name', 'components', 'original'] project:['name', 'components', 'original']

View file

@ -91,7 +91,8 @@ emailUserInitialRecruiting = (user, callback) ->
team = user.session.levelInfo.team team = user.session.levelInfo.team
team = team.substr(0, team.length - 1) team = team.substr(0, team.length - 1)
context = context =
email_id: sendwithus.templates.recruiting_email email_id: sendwithus.templates.recruiting_email.id
version_name: sendwithus.templates.recruiting_email.version
recipient: recipient:
address: if DEBUGGING then 'nick@codecombat.com' else user.email address: if DEBUGGING then 'nick@codecombat.com' else user.email
name: name name: name
@ -134,7 +135,8 @@ emailUserTournamentResults = (winner, callback) ->
name = winner.name name = winner.name
team = winner.team.substr(0, winner.team.length - 1) team = winner.team.substr(0, winner.team.length - 1)
context = context =
email_id: sendwithus.templates.greed_tournament_rank email_id: sendwithus.templates.greed_tournament_rank.id
version_name: sendwithus.templates.greed_tournament_rank.version
recipient: recipient:
address: if DEBUGGING then 'nick@codecombat.com' else winner.email address: if DEBUGGING then 'nick@codecombat.com' else winner.email
name: name name: name

View file

@ -107,6 +107,7 @@ function upsertLeads(done) {
function getCountryCode(country, emails) { function getCountryCode(country, emails) {
// console.log(`DEBUG: getCountryCode ${country} ${emails.length}`); // console.log(`DEBUG: getCountryCode ${country} ${emails.length}`);
if (country) { if (country) {
if (country.indexOf('Nederland') >= 0) return 'NL';
let countryCode = countryList.getCode(country); let countryCode = countryList.getCode(country);
if (countryCode) return countryCode; if (countryCode) return countryCode;
} }
@ -728,7 +729,7 @@ function createUpdateLeadFn(lead, existingLeads) {
if (data.total_results === 0) { if (data.total_results === 0) {
if (existingLeads[lead.name.toLowerCase()]) { if (existingLeads[lead.name.toLowerCase()]) {
if (existingLeads[lead.name.toLowerCase()].length === 1) { if (existingLeads[lead.name.toLowerCase()].length === 1) {
console.log(`DEBUG: Using lead from email lookup: ${lead.name}`); // console.log(`DEBUG: Using lead from email lookup: ${lead.name}`);
return updateExistingLead(lead, existingLeads[lead.name.toLowerCase()][0], done); return updateExistingLead(lead, existingLeads[lead.name.toLowerCase()][0], done);
} }
console.error(`ERROR: ${existingLeads[lead.name.toLowerCase()].length} email leads found for ${lead.name}`); console.error(`ERROR: ${existingLeads[lead.name.toLowerCase()].length} email leads found for ${lead.name}`);

View file

@ -468,7 +468,8 @@ module.exports = class Handler
notifyWatcherOfChange: (editor, watcher, changedDocument, editPath) -> notifyWatcherOfChange: (editor, watcher, changedDocument, editPath) ->
context = context =
email_id: sendwithus.templates.change_made_notify_watcher email_id: sendwithus.templates.change_made_notify_watcher.id
version_name: sendwithus.templates.change_made_notify_watcher.version
recipient: recipient:
address: watcher.get('email') address: watcher.get('email')
name: watcher.get('name') name: watcher.get('name')

View file

@ -187,7 +187,8 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
return @sendForbiddenError(res) unless prepaid.get('maxRedeemers') > prepaid.get('redeemers').length return @sendForbiddenError(res) unless prepaid.get('maxRedeemers') > prepaid.get('redeemers').length
for email in req.body.emails for email in req.body.emails
context = context =
email_id: sendwithus.templates.course_invite_email email_id: sendwithus.templates.course_invite_email.id
version: sendwithus.templates.course_invite_email.version
recipient: recipient:
address: email address: email
subject: course.get('name') subject: course.get('name')

View file

@ -100,7 +100,8 @@ PatchHandler = class PatchHandler extends Handler
sendPatchCreatedEmail: (patchCreator, watcher, patch, target, docLink) -> sendPatchCreatedEmail: (patchCreator, watcher, patch, target, docLink) ->
# return if watcher._id is patchCreator._id # return if watcher._id is patchCreator._id
context = context =
email_id: sendwithus.templates.patch_created email_id: sendwithus.templates.patch_created.id
version_name: sendwithus.templates.patch_created.version
recipient: recipient:
address: watcher.get('email') address: watcher.get('email')
name: watcher.get('name') name: watcher.get('name')

View file

@ -132,7 +132,8 @@ module.exports =
user.set('passwordReset', utils.getCodeCamel()) user.set('passwordReset', utils.getCodeCamel())
yield user.save() yield user.save()
context = context =
email_id: sendwithus.templates.password_reset email_id: sendwithus.templates.password_reset.id
version_name: sendwithus.templates.password_reset.version
recipient: recipient:
address: req.body.email address: req.body.email
email_data: email_data:

View file

@ -235,7 +235,8 @@ module.exports =
for email in req.body.emails for email in req.body.emails
joinCode = (classroom.get('codeCamel') or classroom.get('code')) joinCode = (classroom.get('codeCamel') or classroom.get('code'))
context = context =
email_id: sendwithus.templates.course_invite_email email_id: sendwithus.templates.course_invite_email.id
version_name: sendwithus.templates.course_invite_email.version
recipient: recipient:
address: email address: email
email_data: email_data:

View file

@ -85,7 +85,8 @@ module.exports =
if not user if not user
throw new errors.NotFound('User not found') throw new errors.NotFound('User not found')
context = context =
email_id: sendwithus.templates.verify_email email_id: sendwithus.templates.verify_email.id
version_name: sendwithus.templates.verify_email.version
recipient: recipient:
address: user.get('email') address: user.get('email')
name: user.broadName() name: user.broadName()

View file

@ -107,7 +107,8 @@ module.exports =
User.find({_id:{$in:watchers}}).select({email:1, name:1}).exec (err, watchers) -> User.find({_id:{$in:watchers}}).select({email:1, name:1}).exec (err, watchers) ->
for watcher in watchers for watcher in watchers
context = context =
email_id: sendwithus.templates.change_made_notify_watcher email_id: sendwithus.templates.change_made_notify_watcher.id
version_name: sendwithus.templates.change_made_notify_watcher.version
recipient: recipient:
address: watcher.get('email') address: watcher.get('email')
name: watcher.get('name') name: watcher.get('name')

View file

@ -61,7 +61,8 @@ createSendWithUsContext = (req, fromAddress, subject, content, done) ->
else config.mail.supportPrimary else config.mail.supportPrimary
context = context =
email_id: sendwithus.templates.plain_text_email email_id: sendwithus.templates.plain_text_email.id
version_name: sendwithus.templates.plain_text_email.version
recipient: recipient:
address: toAddress address: toAddress
sender: sender:

View file

@ -592,7 +592,8 @@ sendLadderUpdateEmail = (session, now, daysAgo) ->
sendEmail = (defeatContext, victoryContext, levelVersionsContext) -> sendEmail = (defeatContext, victoryContext, levelVersionsContext) ->
# TODO: do something with the preferredLanguage? # TODO: do something with the preferredLanguage?
context = context =
email_id: sendwithus.templates.ladder_update_email email_id: sendwithus.templates.ladder_update_email.id
version_name: sendwithus.templates.ladder_update_email.version
recipient: recipient:
address: if DEBUGGING then 'nick@codecombat.com' else user.get('email') address: if DEBUGGING then 'nick@codecombat.com' else user.get('email')
name: name name: name
@ -721,7 +722,8 @@ sendNextStepsEmail = (user, now, daysAgo) ->
# Used to use these categories to customize the email; not doing it right now. TODO: customize it again in Sendwithus. # Used to use these categories to customize the email; not doing it right now. TODO: customize it again in Sendwithus.
# TODO: do something with the preferredLanguage? # TODO: do something with the preferredLanguage?
context = context =
email_id: sendwithus.templates.next_steps_email email_id: sendwithus.templates.next_steps_email.id
version_name: sendwithus.templates.next_steps_email.version
recipient: recipient:
address: if DEBUGGING then 'nick@codecombat.com' else user.get('email') address: if DEBUGGING then 'nick@codecombat.com' else user.get('email')
name: name name: name

View file

@ -15,22 +15,26 @@ module.exports.api =
if swuAPIKey if swuAPIKey
module.exports.api = new sendwithusAPI swuAPIKey, debug module.exports.api = new sendwithusAPI swuAPIKey, debug
# Version name can be supplied to tie a specific version to a deploy.
# That is most useful for testing templates with new data fields on staging.
# If it doesn't need to be synchronized to a deploy, you can just "publish"
# the new template version on SendWithUs (and leave this version blank)
module.exports.templates = module.exports.templates =
parent_subscribe_email: 'tem_2APERafogvwKhmcnouigud' parent_subscribe_email: { id: 'tem_2APERafogvwKhmcnouigud' }
share_progress_email: 'tem_VHE3ihhGmVa3727qds9zY8' share_progress_email: { id: 'tem_VHE3ihhGmVa3727qds9zY8' }
welcome_email_user: 'tem_z7Xvj3mtWYk6ec6aW7RwFk' welcome_email_user: { id: 'tem_z7Xvj3mtWYk6ec6aW7RwFk' }
welcome_email_student: 'tem_4WYPZNLzs5wawMF9qUJXUH' welcome_email_student: { id: 'tem_4WYPZNLzs5wawMF9qUJXUH' }
verify_email: 'tem_zJee6uRsRmzqzktzneCkCn' verify_email: { id: 'tem_zJee6uRsRmzqzktzneCkCn' }
ladder_update_email: 'JzaZxf39A4cKMxpPZUfWy4' ladder_update_email: { id: 'JzaZxf39A4cKMxpPZUfWy4' }
patch_created: 'tem_xhxuNosLALsizTNojBjNcL' patch_created: { id: 'tem_xhxuNosLALsizTNojBjNcL' }
change_made_notify_watcher: 'tem_7KVkfmv9SZETb25dtHbUtG' change_made_notify_watcher: { id: 'tem_7KVkfmv9SZETb25dtHbUtG' }
recruiting_email: 'tem_mdFMgtcczHKYu94Jmq68j8' recruiting_email: { id: 'tem_mdFMgtcczHKYu94Jmq68j8' }
greed_tournament_rank: 'tem_c4KYnk2TriEkkZx5NqqGLG' greed_tournament_rank: { id: 'tem_c4KYnk2TriEkkZx5NqqGLG' }
generic_email: 'tem_JhRnQ4pvTS4KdQjYoZdbei' generic_email: { id: 'tem_JhRnQ4pvTS4KdQjYoZdbei' }
plain_text_email: 'tem_85UvKDCCNPXsFckERTig6Y' plain_text_email: { id: 'tem_85UvKDCCNPXsFckERTig6Y' }
next_steps_email: 'tem_RDHhTG5inXQi8pthyqWr5D' next_steps_email: { id: 'tem_RDHhTG5inXQi8pthyqWr5D' }
course_invite_email: 'tem_ic2ZhPkpj8GBADFuyAp4bj' course_invite_email: { id: 'tem_u6D2EFWYC5Ptk38bSykjsU', version: 'v3' }
teacher_free_trial: 'tem_R7d9Hpoba9SceQNiYSXBak' teacher_free_trial: { id: 'tem_R7d9Hpoba9SceQNiYSXBak' }
teacher_free_trial_hoc: 'tem_4ZSY9wsA9Qwn4wBFmZgPdc' teacher_free_trial_hoc: { id: 'tem_4ZSY9wsA9Qwn4wBFmZgPdc' }
teacher_request_demo: 'tem_cwG3HZjEyb6QE493hZuUra' teacher_request_demo: { id: 'tem_cwG3HZjEyb6QE493hZuUra' }
password_reset: 'tem_wbQUMRtLY9xhec8BSCykLA' password_reset: { id: 'tem_wbQUMRtLY9xhec8BSCykLA' }

View file

@ -75,24 +75,29 @@ describe 'TeacherClassView', ->
# it "shows the classroom's join code" # it "shows the classroom's join code"
describe 'the Students tab', -> describe 'the Students tab', ->
beforeEach -> beforeEach (done) ->
@view.state.set('activeTab', '#students-tab') @view.state.set('activeTab', '#students-tab')
_.defer(done)
# it 'shows all of the students' # it 'shows all of the students'
# it 'sorts correctly by Name' # it 'sorts correctly by Name'
# it 'sorts correctly by Progress' # it 'sorts correctly by Progress'
describe 'bulk-assign controls', -> describe 'bulk-assign controls', ->
it 'shows alert when assigning course 2 to unenrolled students', -> it 'shows alert when assigning course 2 to unenrolled students', (done) ->
expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(false) expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(false)
@view.$('.student-row .checkbox-flat').click() @view.$('.student-row .checkbox-flat').click()
@view.$('.assign-to-selected-students').click() @view.$('.assign-to-selected-students').click()
expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(true) _.defer =>
expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(true)
done()
it 'shows alert when assigning but no students are selected', -> it 'shows alert when assigning but no students are selected', (done) ->
expect(@view.$('.no-students-selected').hasClass('visible')).toBe(false) expect(@view.$('.no-students-selected').hasClass('visible')).toBe(false)
@view.$('.assign-to-selected-students').click() @view.$('.assign-to-selected-students').click()
expect(@view.$('.no-students-selected').hasClass('visible')).toBe(true) _.defer =>
expect(@view.$('.no-students-selected').hasClass('visible')).toBe(true)
done()
# describe 'the Course Progress tab', -> # describe 'the Course Progress tab', ->
# it 'shows the correct Course Overview progress' # it 'shows the correct Course Overview progress'