Merge branch 'master' into production

This commit is contained in:
phoenixeliot 2016-04-15 15:40:20 -07:00
commit f36aab2f91
15 changed files with 120 additions and 81 deletions

View file

@ -15,6 +15,9 @@ module.exports = class CocoCollection extends Backbone.Collection
@once 'sync', =>
@loaded = true
model.loaded = true for model in @models
if window.application?.testing
@fakeRequests = []
@on 'request', -> @fakeRequests.push jasmine.Ajax.requests.mostRecent()
getURL: ->
return if _.isString @url then @url else @url()

View file

@ -90,7 +90,7 @@ module.exports = class God extends CocoClass
return if hadPreloader
@angelsShare.workQueue = []
@angelsShare.workQueue.push
work =
userCodeMap: userCodeMap
level: @level
levelSessionIDs: @levelSessionIDs
@ -103,8 +103,10 @@ module.exports = class God extends CocoClass
preload: preload
synchronous: not Worker? # Profiling world simulation is easier on main thread, or we are IE9.
realTime: realTime
@angelsShare.workQueue.push work
angel.workIfIdle() for angel in @angelsShare.angels
work
getUserCodeMap: (spells) ->
userCodeMap = {}
for spellKey, spell of spells

View file

@ -377,7 +377,7 @@ module.exports = class LevelLoader extends CocoClass
resource.markLoaded() if resource.spriteSheetKeys.length is 0
denormalizeSession: ->
return if @headless or @sessionDenormalized or @spectateMode or @sessionless or me.isTeacher()
return if @headless or @sessionDenormalized or @spectateMode or @sessionless or me.isSessionless()
# This is a way (the way?) PUT /db/level.sessions/undefined was happening
# See commit c242317d9
return if not @session.id

View file

@ -446,9 +446,9 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
tip_nerds: "Nerdi milujú skákanie na stoličku a zo stoličky. Strácajú pritom kontrolu. - John Green"
tip_self_taught: "90% toho, čo potrebujem, som sa naučil sám. A je to normálne! - Hank Green"
tip_luna_lovegood: "Don't worry, you're just as sane as I am. - Luna Lovegood"
# tip_good_idea: "The best way to have a good idea is to have a lot of ideas. - Linus Pauling"
# tip_programming_not_about_computers: "Computer Science is no more about computers than astronomy is about telescopes. - Edsger Dijkstra"
# tip_mulan: "Believe you can, then you will. - Mulan"
tip_good_idea: "Najlepším spôsobom ako mať dobrý nápad, je mať veľa dobrých nápadov. - Linus Pauling"
tip_programming_not_about_computers: "Veda o počítačoch je o počítačoch v tej miere ako je astronómia o teleskopoch. - Edsger Dijkstra"
tip_mulan: "Ver, že môžeš a potom budeš aj chcieť. - Mulan"
game_menu:
inventory_tab: "Inventár"
@ -513,49 +513,49 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
feature4: "<strong>{{gems}} bonusových diamantov</strong> každý mesiac !"
feature5: "Video tutoriály"
feature6: "Prémiová emailová podpora"
# feature7: "Private <strong>Clans</strong>"
# feature8: "<strong>No ads!</strong>"
feature7: "Súkromné<strong>klany</strong>"
feature8: "<strong>Žiadne reklamy!</strong>"
free: "Zdarma"
month: "mesiac"
# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above."
must_be_logged: "Najskôr sa musíš prihlásiť. Vytvor si účet alebo sa prihlás."
subscribe_title: "Predplatné"
unsubscribe: "Zrušiť predplatné"
confirm_unsubscribe: "Potvrdiť zrušenie predplatného"
never_mind: "Nevadí, stále ťa máme radi"
thank_you_months_prefix: "Ďakujeme za tvoju podporu v posledných"
thank_you_months_suffix: "mesiacoch."
# thank_you: "Thank you for supporting CodeCombat."
# sorry_to_see_you_go: "Sorry to see you go! Please let us know what we could have done better."
# unsubscribe_feedback_placeholder: "O, what have we done?"
# parent_button: "Ask your parent"
# parent_email_description: "We'll email them so they can buy you a CodeCombat subscription."
# parent_email_input_invalid: "Email address invalid."
# parent_email_input_label: "Parent email address"
# parent_email_input_placeholder: "Enter parent email"
# parent_email_send: "Send Email"
# parent_email_sent: "Email sent!"
# parent_email_title: "What's your parent's email?"
# parents: "For Parents"
# parents_title: "Dear Parent: Your child is learning to code. Will you help them continue?"
# parents_blurb1: "Your child has played __nLevels__ levels and learned programming basics. Help cultivate their interest and buy them a subscription so they can keep playing."
# parents_blurb1a: "Computer programming is an essential skill that your child will undoubtedly use as an adult. By 2020, basic software skills will be needed by 77% of jobs, and software engineers are in high demand across the world. Did you know that Computer Science is the highest-paid university degree?"
# parents_blurb2: "For ${{price}} USD/mo, your child will get new challenges every week and personal email support from professional programmers."
# parents_blurb3: "No Risk: 100% money back guarantee, easy 1-click unsubscribe."
# payment_methods: "Payment Methods"
# payment_methods_title: "Accepted Payment Methods"
# payment_methods_blurb1: "We currently accept credit cards and Alipay. You can also PayPal {{three_month_price}} USD to nick@codecombat.com with your account email in the memo to purchase three months' subscription and gems, or ${{year_price}} for a year."
# payment_methods_blurb2: "If you require an alternate form of payment, please contact"
# sale_button: "Sale!"
# sale_button_title: "Save $21 when you purchase a 1 year subscription"
# stripe_description: "Monthly Subscription"
# stripe_description_year_sale: "1 Year Subscription (${{discount}} discount)"
# subscription_required_to_play: "You'll need a subscription to play this level."
# unlock_help_videos: "Subscribe to unlock all video tutorials."
# personal_sub: "Personal Subscription" # Accounts Subscription View below
# loading_info: "Loading subscription information..."
# managed_by: "Managed by"
# will_be_cancelled: "Will be cancelled on"
# currently_free: "You currently have a free subscription"
thank_you: "Ďakujeme za podporu CodeCombatu."
sorry_to_see_you_go: "Je nám ľúto, že odchádzaš. Čo sme mali urobiť lepšie?"
unsubscribe_feedback_placeholder: "Ó, čo sme ti urobili?"
parent_button: "Spýtaj sa rodičov"
parent_email_description: "Pošleme im email, aby ti mohli predplatiť CodeCombat."
parent_email_input_invalid: "Neplatný email."
parent_email_input_label: "Email rodiča"
parent_email_input_placeholder: "Zadaj email rodiča"
parent_email_send: "Pošli email"
parent_email_sent: "Email odoslaný!"
parent_email_title: "Aký je email jedného z tvojích rodičov?"
parents: "Pre rodičov"
parents_title: "Drahý rodič: Vaše dieťa sa učí programovať. Chcete, aby v tom pokračovalo?"
parents_blurb1: "Vaše dieťa už prešlo __nLevels__ úrovňami a naučilo sa základy programovania. Pomôžte mu v rozvíjaní jeho záujmov a zaplaťte mu predplatné."
parents_blurb1a: "Programovanie je základná zručnosť, ktorú Vaše dieťa určite využije v dospelosti. V roku 2020 budú základné softvérové zručnosti potrebné v 77% povolaní. Po programátoroch je veľký dopyt.Je to tiež najlepšie platené miesto pre ľudí s vysokoškolským vzdelaním."
parents_blurb2: "Za ${{price}} USD/mesiac získa Vaše dieťa nové výzvy každý mesiac a osobnú podporu cez email od profesionálnych programátorov."
parents_blurb3: "Žiadne riziko: 100% garancia vrátenia peňazí,ľahké odhlásenie predplatného."
payment_methods: "Metódy platby"
payment_methods_title: "Akceptované metódy platby"
payment_methods_blurb1: "V súčasmosti akceptujeme kreditné karty a Alipay. Môžete tiež použiť PayPal a poslať {{three_month_price}} USD na email nick@codecombat.com. Uveďte v poznámke ku platbe registračný email a predplaťťe si 3 mesiace alebo za cenu ${{year_price}} si zakúpte ročné predplatné."
payment_methods_blurb2: "Ak požadujete iný spôsob platby, spojte sa s nami"
sale_button: "Kúp!"
sale_button_title: "Objednaj si ročné predplatné a ušetri 21$"
stripe_description: "Mesačné predplatné"
stripe_description_year_sale: "Ročné predplatné (zľava ${{discount}})"
subscription_required_to_play: "Potrebuješ predplatné, ak chceš hrať túto úroveň."
unlock_help_videos: "Predplať si Codecombat a získaj prístup ku videonávodom."
personal_sub: "Predplatné" # Accounts Subscription View below
loading_info: "Nahrávam informácie o predplatnom..."
managed_by: "Riadené"
will_be_cancelled: "Končí"
currently_free: "Nemáš platené predplatné"
# currently_free_until: "You currently have a subscription until"
# was_free_until: "You had a free subscription until"
# managed_subs: "Managed Subscriptions"

View file

@ -21,6 +21,9 @@ class CocoModel extends Backbone.Model
@on 'add', @onLoaded, @
@saveBackup = _.debounce(@saveBackup, 500)
@usesVersions = @schema()?.properties?.version?
if window.application?.testing
@fakeRequests = []
@on 'request', -> @fakeRequests.push jasmine.Ajax.requests.mostRecent()
created: -> new Date(parseInt(@id.substring(0, 8), 16) * 1000)

View file

@ -4,6 +4,7 @@ CocoModel = require './CocoModel'
util = require 'core/utils'
ThangType = require './ThangType'
Level = require './Level'
utils = require 'core/utils'
module.exports = class User extends CocoModel
@className: 'User'
@ -64,6 +65,11 @@ module.exports = class User extends CocoModel
isTeacher: ->
return @get('role') in ['teacher', 'technology coordinator', 'advisor', 'principal', 'superintendent', 'parent']
isSessionless: ->
# TODO: Fix old users who got mis-tagged as teachers
# TODO: Should this just be isTeacher, eventually?
me.isTeacher() and utils.getQueryVariable('course', false)
setRole: (role, force=false) ->
return if me.isAdmin()

View file

@ -2,24 +2,42 @@ extends /templates/base-flat
block content
.container
each test in view.tests
each test, id in view.tests
if test.level
h2= test.level.get('name')
small= ' in ' + test.language + ''
div.well(style='width: 300px; float: right')
if test.goals
each v,k in test.goals || []
case v.status
when 'success': div(style='color: green') ✓ #{k}
when 'incomplete': div(style='color: orange') ✘ #{k}
when 'failure': div(style='color: red') ✘ #{k}
default: div(style='color: blue') #{k}
else
h3 Running....
if test.solution
pre(style='margin-right: 350px') #{test.solution.source}
if !test.goals
h2(style='color: orange')= test.level.get('name')
small= ' in ' + test.language + ''
else if test.isSucessful()
h2(style='color: green')= test.level.get('name')
small= ' in ' + test.language + ''
else
h4 Solution not found...
h2(style='color: red')= test.level.get('name')
small= ' in ' + test.language + ''
div.row(class=(test.isSucessful() && id > 1 ? 'collapse' : 'collapse in'))
div.col-xs-8
if test.solution
pre #{test.solution.source}
else
h4 Solution not found...
div.col-xs-4.well
if test.goals
if test.frames == test.solution.frameCount
div(style='color: green') ✓ Frames: #{test.frames}
else
div(style='color: red') ✘ Frames: #{test.frames} vs #{test.solution.frameCount}
each v,k in test.goals || []
if !test.solution.goals
div(style='color: orange') ? #{k} (#{v.status})
else if v.status == test.solution.goals[k]
div(style='color: green') ✓ #{k} (#{v.status})
else
div(style='color: red') ✘ #{k} (#{v.status} vs #{test.solution.goals[k]})
else
h3 Running....
else
h1 Loading Level...

View file

@ -7,7 +7,7 @@
.levels-link-area
a.levels-link(href=homeLink || "/")
.glyphicon.glyphicon-play
span(data-i18n=me.isTeacher() ? "nav.courses" : (ladderGame ? "general.ladder" : "nav.play")).home-text Levels
span(data-i18n=me.isSessionless() ? "nav.courses" : (ladderGame ? "general.ladder" : "nav.play")).home-text Levels
if isMultiplayerLevel && !observing
.multiplayer-area-container

View file

@ -13,7 +13,7 @@
h3.text-uppercase(data-i18n='play_level.completed_level')
h2.text-uppercase.text-center= i18n(view.level.attributes, 'name')
.well.well-sm.well-parchment
if me.isTeacher()
if me.isSessionless()
h3.course-title
span.text-uppercase.spr(data-i18n='play_level.course')
span.text-uppercase.text-center= i18n(view.course.attributes, 'name')

View file

@ -78,12 +78,21 @@ module.exports = class VerifierTest extends CocoClass
@updateCallback? state: 'running'
processSingleGameResults: (e) ->
console.log(e)
@goals = e.goalStates
@frames = e.totalFrames
@lastFrameHash = e.lastFrameHash
@state = 'complete'
@updateCallback? state: @state
isSucessful: () ->
return false unless @frames == @solution.frameCount
if @goals and @solution.goals
for k of @goals
continue if not @solution.goals[k]
return false if @solution.goals[k] != @goals[k].status
return true
fail: (e) ->
@error = 'Failed due to infinate loop.'
@state = 'error'

View file

@ -75,7 +75,7 @@ module.exports = class ControlBarView extends CocoView
c.spectateGame = @spectateGame
c.observing = @observing
@homeViewArgs = [{supermodel: if @hasReceivedMemoryWarning then null else @supermodel}]
if me.isTeacher()
if me.isSessionless()
@homeLink = "/teachers/courses"
@homeViewClass = "views/courses/TeacherCoursesView"
else if @level.get('type', true) in ['ladder', 'ladder-tutorial', 'hero-ladder', 'course-ladder']

View file

@ -137,7 +137,7 @@ module.exports = class PlayLevelView extends RootView
@loadStartTime = new Date()
@god = new God()
levelLoaderOptions = supermodel: @supermodel, levelID: @levelID, sessionID: @sessionID, opponentSessionID: @opponentSessionID, team: @getQueryVariable('team'), observing: @observing, courseID: @courseID
if me.isTeacher()
if me.isSessionless()
levelLoaderOptions.fakeSessionConfig = {}
@levelLoader = new LevelLoader levelLoaderOptions
@listenToOnce @levelLoader, 'world-necessities-loaded', @onWorldNecessitiesLoaded
@ -552,7 +552,7 @@ module.exports = class PlayLevelView extends RootView
@endHighlight()
options = {level: @level, supermodel: @supermodel, session: @session, hasReceivedMemoryWarning: @hasReceivedMemoryWarning, courseID: @courseID, courseInstanceID: @courseInstanceID, world: @world}
ModalClass = if @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course', 'course-ladder'] then HeroVictoryModal else VictoryModal
ModalClass = CourseVictoryModal if @isCourseMode() or me.isTeacher()
ModalClass = CourseVictoryModal if @isCourseMode() or me.isSessionless()
ModalClass = PicoCTFVictoryModal if window.serverConfig.picoCTF
victoryModal = new ModalClass(options)
@openModalView(victoryModal)

View file

@ -11,6 +11,7 @@ EarnedAchievement = require 'models/EarnedAchievement'
LocalMongo = require 'lib/LocalMongo'
ProgressView = require './ProgressView'
NewItemView = require './NewItemView'
utils = require 'core/utils'
module.exports = class CourseVictoryModal extends ModalView
id: 'course-victory-modal'
@ -100,7 +101,7 @@ module.exports = class CourseVictoryModal extends ModalView
triggeredBy: @session.id
achievement: achievement.id
})
if me.isTeacher()
if me.isSessionless()
@newEarnedAchievements.push ea
else
ea.save()
@ -114,7 +115,7 @@ module.exports = class CourseVictoryModal extends ModalView
@supermodel.loadModel(me, {cache: false})
@newEarnedAchievementsResource.markLoaded()
unless me.isTeacher()
unless me.isSessionless()
# have to use a something resource because addModelResource doesn't handle models being upserted/fetched via POST like we're doing here
@newEarnedAchievementsResource = @supermodel.addSomethingResource('earned achievements') if @newEarnedAchievements.length
@ -163,14 +164,14 @@ module.exports = class CourseVictoryModal extends ModalView
@showView(@views[index+1])
onNextLevel: ->
if me.isTeacher()
link = "/play/level/#{@nextLevel.get('slug')}?course=#{@courseID}&codeLanguage=#{me.get('aceConfig').language}"
if me.isSessionless()
link = "/play/level/#{@nextLevel.get('slug')}?course=#{@courseID}&codeLanguage=#{utils.getQueryVariable('codeLanguage', 'python')}"
else
link = "/play/level/#{@nextLevel.get('slug')}?course=#{@courseID}&course-instance=#{@courseInstanceID}"
application.router.navigate(link, {trigger: true})
onDone: ->
if me.isTeacher()
if me.isSessionless()
link = "/teachers/courses"
else
link = "/courses/#{@courseID}/#{@courseInstanceID}"

View file

@ -63,8 +63,7 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
describe 'when the user already has a TrialRequest and is a teacher', ->
beforeEach (done) ->
spyOn(me, 'isTeacher').and.returnValue(true)
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
_.last(view.trialRequests.fakeRequests).respondWith({
status: 200
responseText: JSON.stringify([{
_id: '1'
@ -86,7 +85,7 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
describe 'when the user has role "student"', ->
beforeEach ->
me.set('role', 'student')
jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, responseText: JSON.stringify('[]') })
_.last(view.trialRequests.fakeRequests).respondWith({ status: 200, responseText: JSON.stringify('[]') })
view.render()
it 'shows a warning that they will convert to a teacher account', ->
@ -104,18 +103,16 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
form.submit()
it 'requires confirmation', ->
request = jasmine.Ajax.requests.mostRecent()
expect(request.url).not.toBe('/db/trial.request')
expect(request.method).not.toBe('POST')
expect(view.trialRequest.fakeRequests.length).toBe(0)
confirmModal = view.openModalView.calls.argsFor(0)[0]
confirmModal.trigger 'confirm'
request = jasmine.Ajax.requests.mostRecent()
request = _.last(view.trialRequest.fakeRequests)
expect(request.url).toBe('/db/trial.request')
expect(request.method).toBe('POST')
describe '"Log out" link', ->
beforeEach ->
jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, responseText: JSON.stringify('[]') })
_.last(view.trialRequests.fakeRequests).respondWith({ status: 200, responseText: JSON.stringify('[]') })
it 'logs out the user and redirects them to /teachers/signup', ->
spyOn(me, 'logout')
@ -124,13 +121,13 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
describe 'submitting the form', ->
beforeEach ->
jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, responseText: JSON.stringify('[]') })
_.last(view.trialRequests.fakeRequests).respondWith({ status: 200, responseText: JSON.stringify('[]') })
form = view.$('form')
forms.objectToForm(form, successForm, {overwriteExisting: true})
form.submit()
it 'creates a new TrialRequest with the information', ->
request = _.last(jasmine.Ajax.requests.filter((r) -> _.string.startsWith(r.url, '/db/trial.request')))
request = _.last(view.trialRequest.fakeRequests)
expect(request).toBeTruthy()
expect(request.method).toBe('POST')
attrs = JSON.parse(request.params)
@ -139,7 +136,7 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
expect(attrs.properties?.email).toBe('some@email.com')
it 'redirects to /teachers/classes', ->
request = jasmine.Ajax.requests.mostRecent()
request = _.last(view.trialRequest.fakeRequests)
request.respondWith({
status: 201
responseText: JSON.stringify(_.extend({_id:'fraghlarghl'}, JSON.parse(request.params)))
@ -149,7 +146,7 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
expect(args[0]).toBe('/teachers/classes')
it 'sets a teacher role', ->
request = jasmine.Ajax.requests.mostRecent()
request = _.last(view.trialRequest.fakeRequests)
request.respondWith({
status: 201
responseText: JSON.stringify(_.extend({_id:'fraghlarghl'}, JSON.parse(request.params)))

View file

@ -1,7 +1,7 @@
useEsper = false
useEsper = true
bowerComponentsPath = './bower_components/'
headlessClientPath = './headless_client/'
require 'aether'
# SETTINGS
options =
workerCode: require headlessClientPath + 'worker_world'