Refactor all server-side fetching calls, refactor solutionProblemView, minor changes to rest

This commit is contained in:
Josh Callebaut 2016-05-13 15:32:59 -07:00
parent 19e1c808d8
commit 38e97ab404
11 changed files with 216 additions and 234 deletions

View file

@ -6,10 +6,13 @@ module.exports = class LevelCollection extends CocoCollection
model: Level model: Level
fetchForClassroom: (classroomID, options={}) -> fetchForClassroom: (classroomID, options={}) ->
options.url = "/db/classroom/#{classroomID}/levels" options.url = '/db/classroom/#{classroomID}/levels'
@fetch(options) @fetch(options)
fetchForClassroomAndCourse: (classroomID, courseID, options={}) -> fetchForClassroomAndCourse: (classroomID, courseID, options={}) ->
options.url = "/db/classroom/#{classroomID}/courses/#{courseID}/levels" options.url = '/db/classroom/#{classroomID}/courses/#{courseID}/levels'
@fetch(options) @fetch(options)
fetchForCampaign: (campaignSlug, options={}) ->
options.url = '/db/campaign/' + campaignSlug + '/levels'
@fetch(options)

View file

@ -1,5 +1,9 @@
#solution-problems-view #solution-problems-view
.problemType .problemType
width: 50% width: 87.5%
.problemValue .problemValue
width: 50% width: 12.5%
.problemsTable
width: 100%

View file

@ -3,14 +3,14 @@ extends /templates/base
block content block content
img(src='/images/pages/user/artisan.png') img(src='/images/pages/user/artisan.png')
div div
a(href='artisans/thang-tasks') a(href='/artisans/thang-tasks')
|Thang Tasks |Thang Tasks
div div
a(href="artisans/level-tasks") a(href="/artisans/level-tasks")
|Level Tasks |Level Tasks
div div
a(href="artisans/solution-problems") a(href="/artisans/solution-problems")
|Solution Problems |Solution Problems
div div
a(href="artisans/course-gear") a(href="/artisans/course-gear")
|Course Gear Progression |Course Gear Progression

View file

@ -3,8 +3,9 @@ extends /templates/base
block content block content
#level-tasks-view #level-tasks-view
div div
a(href='./') a(href='/artisans')
| < Artisans Home | < Artisans Home
br
input#nameSearch(placeholder='Filter: Level Name') input#nameSearch(placeholder='Filter: Level Name')
br br
input#descSearch(placeholder='Filter: Task Description') input#descSearch(placeholder='Filter: Task Description')
@ -20,15 +21,13 @@ block content
else else
div ole? div ole?
mixin levelRow(level) mixin levelRow(level)
tr tr
td.taskOwner td.taskOwner
a(href= 'level/' + level.slug)= level.name a(href= 'level/' + level.slug)= level.name
td.tasks td.tasks
table.table-condensed.table-striped.table-hover.tasksTable table.table-striped.table-hover.tasksTable
for task in (level.tasks2 || []) for task in (level.tasks || [])
if !task.complete if !task.complete
tr tr
td= task.name td= task.name

View file

@ -1,15 +1,24 @@
extends /templates/base extends /templates/base
block content block content
div Total number of problems: #{view.problemCount} #solution-problems-view
table.table.table-striped#levelTable div
for level in (view.parsedLevels || []) a(href='/artisans')
if (level.problems || []).length != 0 | < Artisans Home
tr br
td(style="width:10%")= level.level.get('slug') div Total number of problems: #{view.problemCount}
td hr
table.table-condensed.table-striped.table-hover table.table.table-striped#levelTable
for problem in (level.problems || []) tr
tr(style="width:100%") th Level Name
td.problemType= problem.type th Solution Problems
td.problemValue= problem.value for level in (view.parsedLevels || [])
if (level.problems || []).length != 0
tr
td(style="width:10%")= level.level.get('name')
td
table.table-striped.table-hover.problemsTable
for problem in (level.problems || [])
tr(style="width:100%")
td.problemValue= problem.value
td.problemType= problem.type

View file

@ -3,8 +3,9 @@ extends /templates/base
block content block content
#thang-tasks-view #thang-tasks-view
div div
a(href='./') a(href='/artisans')
| < Artisans Home | < Artisans Home
br
input#nameSearch(placeholder='Filter: Thang Name') input#nameSearch(placeholder='Filter: Thang Name')
br br
input#descSearch(placeholder='Filter: Task Description') input#descSearch(placeholder='Filter: Task Description')

View file

@ -1,57 +1,67 @@
RootView = require 'views/core/RootView' RootView = require 'views/core/RootView'
template = require 'templates/artisans/level-tasks-view' template = require 'templates/artisans/level-tasks-view'
#ThangType = require 'models/ThangType'
Level = require 'models/Level' Campaigns = require 'collections/Campaigns'
Campaign = require 'models/Campaign' Campaign = require 'models/Campaign'
CocoCollection = require 'collections/CocoCollection'
module.exports = class LevelTasksView extends RootView module.exports = class LevelTasksView extends RootView
template: template template: template
id: 'level-tasks-view' id: 'level-tasks-view'
events: events:
'input input': 'searchUpdate' 'input .searchInput': 'searchUpdate'
'change input': 'searchUpdate' 'change .searchInput': 'searchUpdate'
excludedCampaigns = [ excludedCampaigns = [
"picoctf" 'picoctf', 'auditions'
"auditions"
] ]
constructor: (options) -> levels: {}
super options initialize: () ->
@campaigns = new CocoCollection([], @searchUpdate = _.debounce(@searchUpdate, 250)
url: '/db/campaign?project=name,slug,tasks'
model: Campaign
)
@campaigns.fetch()
@listenTo(@campaigns, 'sync', @onCampaignsLoaded)
@supermodel.loadCollection(@campaigns, 'campaigns')
onCampaignsLoaded: -> @campaigns = new Campaigns()
@listenTo(@campaigns, 'sync', @onCampaignsLoaded)
@supermodel.trackRequest(@campaigns.fetch(
data:
project: 'name,slug,levels,tasks'
))
onCampaignsLoaded: (campCollection) ->
@levels = {} @levels = {}
sum = 0 for campaign in campCollection.models
for campaign in @campaigns.models campaignSlug = campaign.get 'slug'
continue unless excludedCampaigns.indexOf(campaign.get 'slug') is -1 continue if campaignSlug in excludedCampaigns
levels = campaign.get('levels') levels = campaign.get 'levels'
sum += Object.keys(levels).length
for key, level of levels for key, level of levels
continue unless ///#{$('#nameSearch')[0].value}///i.test level.name continue unless ///#{$('#nameSearch')[0].value}///i.test level.name
levelSlug = level.slug levelSlug = level.slug
@levels[levelSlug] = level @levels[levelSlug] = level
@processedLevels = @levels @processedLevels = {}
for key, level of @processedLevels for key, level of @levels
level.tasks2 = _.filter level.tasks, (_elem) -> filteredTasks = level.tasks.filter (elem) ->
return ///#{$('#descSearch')[0].value}///i.test _elem.name return ///#{$('#descSearch')[0].value}///i.test elem.name
@processedLevels[key] = {
tasks: filteredTasks
name: level.name
}
@renderSelectors '#levelTable' @renderSelectors '#levelTable'
searchUpdate: -> searchUpdate: ->
@onCampaignsLoaded(@campaigns)
###
if not @lastLoad? or (new Date()).getTime() - @lastLoad > 60 * 1000 * 1 # Update only after a minute from last update. if not @lastLoad? or (new Date()).getTime() - @lastLoad > 60 * 1000 * 1 # Update only after a minute from last update.
@campaigns.fetch() #@campaigns.fetch()
@listenTo(@campaigns, 'sync', @onCampaignsLoaded) @listenTo(@campaigns, 'sync', @onCampaignsLoaded)
@supermodel.loadCollection(@campaigns, 'campaigns') @superModel.trackRequest()
#@supermodel.loadCollection(@campaigns, 'campaigns')
@lastLoad = (new Date()).getTime() @lastLoad = (new Date()).getTime()
else else
@onCampaignsLoaded() @onCampaignsLoaded()
###
destroy: ->
@searchUpdate.cancel()
super()
# Jade helper # Jade helper
hasIncompleteTasks: (level) -> hasIncompleteTasks: (level) ->
return level.tasks2 and level.tasks2.filter((_elem) -> return not _elem.complete).length > 0 return level.tasks and level.tasks.filter((_elem) -> return not _elem.complete).length > 0

View file

@ -1,206 +1,165 @@
RootView = require 'views/core/RootView' RootView = require 'views/core/RootView'
template = require 'templates/artisans/solution-problems-view' template = require 'templates/artisans/solution-problems-view'
Level = require 'models/Level' Level = require 'models/Level'
Campaign = require 'models/Campaign' Campaign = require 'models/Campaign'
Level = require 'models/Level'
CocoCollection = require 'collections/CocoCollection' CocoCollection = require 'collections/CocoCollection'
Campaigns = require 'collections/Campaigns'
Levels = require 'collections/Levels'
module.exports = class SolutionProblemsView extends RootView module.exports = class SolutionProblemsView extends RootView
template: template template: template
id: 'solution-problems-view' id: 'solution-problems-view'
excludedCampaigns = [ excludedCampaigns = [
"picoctf" # Misc. campaigns
"auditions" 'picoctf', 'auditions'
"dungeon" # Campaign-version campaigns
"forest" #'dungeon', 'forest', 'desert', 'mountain', 'glacier'
"desert"
#"mountain"
"glacier"
"dungeon-branching-test" # Test campaigns
"forest-branching-test" 'dungeon-branching-test', 'forest-branching-test', 'desert-branching-test'
"desert-branching-test"
"intro" # Course-version campaigns
"course-2" #'intro', 'course-2', 'course-3', 'course-4', 'course-5', 'course-6'
"course-3"
"course-4"
"course-5"
"course-6"
] ]
excludedSimulationLevels = [ excludedSimulationLevels = [
# Course Arenas # Course Arenas
"wakka-maul" 'wakka-maul', 'cross-bones'
"cross-bones"
] ]
excludedSolutionLevels = [ excludedSolutionLevels = [
# Multiplayer Levels # Multiplayer Levels
"cavern-survival" 'cavern-survival'
'dueling-grounds', 'multiplayer-treasure-grove'
"dueling-grounds" 'harrowland'
"multiplayer-treasure-grove" 'zero-sum'
'ace-of-coders', 'capture-their-flag'
"harrowland"
"zero-sum"
"ace-of-coders"
"capture-their-flag"
] ]
levelOffset: 0 simulationRequirements = [
isReady: 0 'seed'
requiresSubs: 0 'succeeds'
'heroConfig'
'frameCount'
'goals'
]
includedLanguages = [
'python', 'javascript', 'java', 'lua', 'coffeescript'
]
# TODO: Phase the following out:
excludedLanguages = [
#'java', 'lua', 'coffeescript'
]
rob: [] rob: []
test2: [] test2: []
constructor: (options) -> unloadedCampaigns: 0
super options campaignLevels: {}
@campaigns = new CocoCollection([], loadedLevels: {}
url: '/db/campaign?project=slug' parsedLevels: []
model: Campaign problemCount: 0
)
@campaigns.fetch() initialize: ->
@campaigns = new Campaigns([])
@listenTo(@campaigns, 'sync', @onCampaignsLoaded) @listenTo(@campaigns, 'sync', @onCampaignsLoaded)
@supermodel.loadCollection(@campaigns, 'campaigns') @supermodel.trackRequest(@campaigns.fetch(
### data:
@levels = new CocoCollection([], project:'slug'
url: '/db/level?project=slug,thangs&limit=100&skip=' + @levelOffset ))
model: Level
)
@levels.fetch()
@listenTo(@levels, 'sync', @onLevelsLoaded)
@supermodel.loadCollection(@levels, 'levels')
###
onCampaignsLoaded: -> onCampaignsLoaded: (campCollection) ->
@levelSlugs = [] for campaign in campCollection.models
@test = {}
@loadedLevels = {}
count = 0
for campaign in @campaigns.models
campaignSlug = campaign.get('slug') campaignSlug = campaign.get('slug')
continue unless excludedCampaigns.indexOf(campaignSlug) is -1 continue if campaignSlug in excludedCampaigns
count++ @unloadedCampaigns++
@test[campaignSlug] = new CocoCollection([],
url: '/db/campaign/' + campaignSlug + '/levels?project=thangs,slug,requiresSubscription,campaign'
model: Level
)
@test[campaignSlug].fetch()
@listenTo(@test[campaignSlug], 'sync', (e) ->
e.models.reverse()
for level in e.models
if not @loadedLevels[level.get('slug')]? and level.get('requiresSubscription')
@requiresSubs++
@loadedLevels[level.get('slug')] = level
count--
if count is 0
@readyUp()
)
@supermodel.loadCollection(@test[campaignSlug], 'levels')
readyUp: -> @campaignLevels[campaignSlug] = new Levels()
console.log("Count of levels: " + _.size(@loadedLevels)) @listenTo(@campaignLevels[campaignSlug], 'sync', @onLevelsLoaded)
console.log("Count requires sub: " + @requiresSubs) @supermodel.trackRequest(@campaignLevels[campaignSlug].fetchForCampaign(campaignSlug,
@parsedLevels = [] data:
project: 'thangs,name,slug,campaign'
))
@problemCount = 0 onLevelsLoaded: (lvlCollection) ->
for level in lvlCollection.models
@loadedLevels[level.get('slug')] = level
if --@unloadedCampaigns is 0
@onAllLevelsLoaded()
onAllLevelsLoaded: ->
for levelSlug, level of @loadedLevels for levelSlug, level of @loadedLevels
unless level? unless level?
console.error 'Level Slug doesn\'t have associated Level', levelSlug
continue continue
thangs = level.get('thangs') continue if levelSlug in excludedSolutionLevels
thangs = level.get 'thangs'
component = null component = null
thang = _.findWhere(thangs, (elem) -> thangs = _.filter(thangs, (elem) ->
return elem.id is "Hero Placeholder" and _.findWhere(elem.components, (elem2) -> return _.findWhere(elem.components, (elem2) ->
if elem2.config?.programmableMethods?.plan? if elem2.config?.programmableMethods?
component = elem2 component = elem2
return true return true
) )
) )
thangs = _.filter(thangs, (elem) ->
return _.findWhere(elem.components, (elem2) ->
if elem2.config?.programmableMethods?
return true
)
)
if thangs.length > 1 if thangs.length > 1
console.log levelSlug + ":" + thangs.length + " " + thangs.map((elem) -> return elem.id) unless levelSlug in excludedSimulationLevels
unless thang? and component console.warn 'Level has more than 1 programmableMethod Thangs', levelSlug
console.log("Cannot find programmableMethods component in: " + levelSlug)
continue continue
unless component?.config?.programmableMethods?.plan? unless component?
console.log("Cannot find plannable method inside component: " + levelSlug) console.error 'Level doesn\'t have programmableMethod Thang', levelSlug
continue continue
plan = component.config.programmableMethods.plan plan = component.config.programmableMethods.plan
solutions = plan.solutions solutions = plan.solutions or []
problems = [] problems = []
if excludedSolutionLevels.indexOf(levelSlug) is -1 problems = problems.concat(@findMissingSolutions solutions)
for lang in ["python", "javascript", "lua", "java", "coffeescript"] unless levelSlug in excludedSimulationLevels
if _.findWhere(solutions, (elem) -> return elem.language is lang) for solution in solutions
#@rob.push language: lang, level: levelSlug problems = problems.concat(@findSimulationProblems solution)
problems = problems.concat(@findPass solution)
else if lang not in ["lua", "java", "coffeescript"] problems = problems.concat(@findIdenticalToSource solution, plan)
problems.push { @problemCount += problems.length
"type":"Missing Solution Language", @parsedLevels.push
"value":lang
}
@test2.push(levelSlug)
#break
@problemCount++
else
# monitor lua/java when we care about it here
for solutionIndex of solutions
solution = solutions[solutionIndex]
if excludedSimulationLevels.indexOf(levelSlug) is -1
isSimul = true
for req in ["seed", "succeeds", "heroConfig", 'frameCount', 'goals'] # Implement a fix for lastHash
unless solution[req]?
console.log levelSlug, req
problems.push {
"type":"Solution is not simulatable",
"value":solution.language
}
@problemCount++
isSimul = false
break
if isSimul
console.log level.get('campaign')
if @rob.indexOf(levelSlug) is -1
@rob.push(levelSlug)
if solution.source.search(/pass\n/i) isnt -1
problems.push {
"type":"Solution contains pass",
"value":solution.language
}
@problemCount++
if solution.source.indexOf('<%=') is -1
problems.push {
"type":"Solution is not i18n'd",
"value":solution.language
}
@problemCount++
if solution.language is 'javascript'
if solution.source is plan.source
problems.push {
"type":"Solution is identical to source",
"value":solution.language
}
@problemCount++
else
#console.log solution.source
#console.log plan.languages[solution.language]
if solution.source is plan.languages[solution.language]
problems.push {
"type":"Solution is identical to source",
"value":solution.language
}
@problemCount++
@parsedLevels.push {
level: level level: level
problems: problems problems: problems
}
@renderSelectors '#levelTable' @renderSelectors '#levelTable'
findMissingSolutions: (solutions) ->
problems = []
for lang in includedLanguages
if _.findWhere(solutions, (elem) -> return elem.language is lang)
# TODO: Phase the following out:
else if lang not in excludedLanguages
problems.push
type: 'Missing solution language'
value: lang
problems
findSimulationProblems: (solution) ->
problems = []
for req in simulationRequirements
unless solution[req]?
problems.push
type: 'Solution is not simulatable'
value: solution.language
break
problems
findPass: (solution) ->
problems = []
if solution.source.search(/pass\n/) isnt -1
problems.push
type: 'Solution contains pass'
value: solution.language
problems
findIdenticalToSource: (solution, plan) ->
problems = []
source = if solution.lang is 'javascript' then plan.source else plan.languages[solution.language]
if solution.source is source
problems.push
type: 'Solution matches sample code'
value: solution.language
problems

View file

@ -1,7 +1,9 @@
RootView = require 'views/core/RootView' RootView = require 'views/core/RootView'
template = require 'templates/artisans/thang-tasks-view' template = require 'templates/artisans/thang-tasks-view'
ThangType = require 'models/ThangType' ThangType = require 'models/ThangType'
CocoCollection = require 'collections/CocoCollection'
ThangTypes = require 'collections/ThangTypes'
module.exports = class ThangTasksView extends RootView module.exports = class ThangTasksView extends RootView
template: template template: template
@ -9,18 +11,13 @@ module.exports = class ThangTasksView extends RootView
events: events:
'input input': 'searchUpdate' 'input input': 'searchUpdate'
'change input': 'searchUpdate' 'change input': 'searchUpdate'
initialize: () ->
constructor: (options) -> @thangs = new ThangTypes()
super options
@thangs = new CocoCollection([],
url: '/db/thang.type?project=name,tasks,slug'
model: ThangType
comparator: @sortThangs
)
@lastLoad = (new Date()).getTime()
@listenTo(@thangs, 'sync', @onThangsLoaded) @listenTo(@thangs, 'sync', @onThangsLoaded)
@supermodel.loadCollection(@thangs, 'thangs') @supermodel.trackRequest(@thangs.fetch(
data:
project: 'name,tasks,slug'
))
searchUpdate: -> searchUpdate: ->
if not @lastLoad? or (new Date()).getTime() - @lastLoad > 60 * 1000 * 1 # Update only after a minute from last update. if not @lastLoad? or (new Date()).getTime() - @lastLoad > 60 * 1000 * 1 # Update only after a minute from last update.
@thangs.fetch() @thangs.fetch()