bind\bind_static 'updater', require 'facades.updater'
bind\bind_static 'crypto', require 'services.crypto'
bind\bind_static 'uuidv4', require 'services.uuid'
-bind\bind_static 'queries', require 'services.queries'
bind\bind_static 'scoring', require 'services.scoring'
bind\bind_static 'time', require 'utils.time'
make_controller
middleware: { 'logged_in', 'competition_started' }
+ scripts: { 'leaderboard_update' }
+ raw_scripts: { '/socket.io/socket.io.js' }
get: =>
@navbar.selected = 0
import Problems from require 'models'
make_controller
- inject:
- queries: 'queries'
-
middleware: { 'logged_in', 'competition_started' }
scripts: { "pie_chart" }
raw_scripts: {
make_controller
inject:
- queries: 'queries'
executer: 'executer'
middleware: { 'logged_in', 'during_competition' }
@problem = Problems\find short_name: @params.problem_name
render: 'problem.submit'
-
+
post: capture_errors_json =>
assert_valid @params, {
{ "problem_name", exists: true }
problem = Problems\find short_name: @params.problem_name
unless problem
return json: { status: 'problem not found' }
-
+
test_cases = problem\get_test_cases!
id = @executer\request @params.lang, @params.code, @user.id, problem.id, @competition.id, test_cases, problem.time_limit
import Competitions, Problems from require 'models'
make_controller
- inject:
- queries: 'queries'
-
middleware: { 'logged_in' }
scripts: { 'pie_chart' }
@problem_ids = @competition\get_problem_ids!
@problems = [Problems\find id for id in *@problem_ids]
- render: 'submission.list'
\ No newline at end of file
+ render: 'submission.list'
import Competitions, Jobs from require 'models'
make_controller
- inject:
- queries: 'queries'
-
middleware: { 'logged_in' }
scripts: { 'vendor/ace/ace', 'submission_reloader' }
raw_scripts: { '/socket.io/socket.io.js' }
class UpdaterFacade
push_submission_update: (job_id) =>
http.simple "#{config.updater_addr}/submission_update?submission_ida=#{job_id}"
+
+ push_leaderboard_update: =>
+ http.simple "#{config.updater_addr}/leaderboard_update"
(time.time_to_number @['end']) + @time_offset * 60
}
}
+
+ @delete_leaderboard: (competition_id) =>
+ db.query "delete from leaderboard_problems
+ where leaderboard_placement_id in
+ (select id from leaderboard_placements where competition_id=?)", competition_id
+
+ db.delete "leaderboard_placements", competition_id: competition_id
where competitions.active=TRUE and problem_id=?", @id)[1].count > 0
}
}
+
+ @get_codegolf_leaders: (problem_id, competition_id) =>
+ db.select "user_id, problem_id, competition_id, status, time_initiated, char_length(code) as bytes
+ from jobs
+ where problem_id=? and competition_id=?
+ order by status asc, bytes asc, time_initiated asc", problem_id, competition_id
+
+ @clear_codegolf_scores: (problem_id, competition_id) =>
+ db.query "update leaderboard_problems
+ set points=0, status=1
+ from leaderboard_placements
+ where problem_id=? and leaderboard_placements.competition_id=?", problem_id, competition_id
import Model from require "lapis.db.model"
db = require 'lapis.db'
+import Jobs from require 'models'
class Users extends Model
@relations: {
where competitions.active=? and jobs.user_id=?", db.TRUE, @id
}
}
+
+ @has_correct_submission: (user_id, problem_name) =>
+ count = db.select "count(jobs.id) from jobs
+ inner join problems on problems.id = jobs.problem_id
+ inner join competitions on jobs.competition_id=competitions.id
+ where competitions.active=TRUE and jobs.status=? and jobs.user_id=? and problems.short_name=?
+ ", (Jobs.statuses\for_db 'correct'), user_id, problem_name
+
+ return count[1].count > 0
+
+ @count_incorrect_submission: (user_id, problem_name) =>
+ count = db.select "count(jobs.id) from jobs
+ inner join problems on problems.id = jobs.problem_id
+ inner join competitions on jobs.competition_id=competitions.id
+ where competitions.active=TRUE and jobs.status in ? and jobs.user_id=? and problems.short_name=?
+ ",
+ (db.list {
+ Jobs.statuses\for_db 'wrong_answer'
+ Jobs.statuses\for_db 'timed_out'
+ Jobs.statuses\for_db 'error'
+ Jobs.statuses\for_db 'compile_err'
+ }), user_id, problem_name
+
+ return count[1].count
+
+ @has_incorrect_submission: (user_id, problem_name) =>
+ (@@count_incorrect_submission user_id, problem_name) > 0
+
+ @get_score: (user_id, competition_id) =>
+ res = db.select "sum(points)
+ from leaderboard_problems
+ inner join leaderboard_placements on leaderboard_placements.id=leaderboard_problems.leaderboard_placement_id
+ where leaderboard_placements.user_id=? and competition_id=?", user_id, competition_id
+
+ return 0 if #res == 0
+ res[1].sum
+
+ @get_first_correct_submission: (user_id, problem_id, competition_id) =>
+ jobs = db.select "* from jobs where user_id=? and problem_id=? and competition_id=? order by time_initiated asc limit 1", user_id, problem_id, competition_id
+ jobs[1]
+
+ @get_jobs_by_problem: (user_id, problem_id, competition_id) =>
+ db.select "* from jobs where user_id=? and problem_id=? and competition_id=? order by time_initiated desc", user_id, problem_id, competition_id
+++ /dev/null
-db = require 'lapis.db'
-import Jobs from require 'models'
-
-has_correct_submission = (user_id, problem_name) ->
- count = db.select "count(jobs.id) from jobs
- inner join problems on problems.id = jobs.problem_id
- inner join competitions on jobs.competition_id=competitions.id
- where competitions.active=TRUE and jobs.status=? and jobs.user_id=? and problems.short_name=?
- ", (Jobs.statuses\for_db 'correct'), user_id, problem_name
-
- return count[1].count > 0
-
-count_incorrect_submission = (user_id, problem_name) ->
- count = db.select "count(jobs.id) from jobs
- inner join problems on problems.id = jobs.problem_id
- inner join competitions on jobs.competition_id=competitions.id
- where competitions.active=TRUE and jobs.status in ? and jobs.user_id=? and problems.short_name=?
- ",
- (db.list {
- Jobs.statuses\for_db 'wrong_answer'
- Jobs.statuses\for_db 'timed_out'
- Jobs.statuses\for_db 'error'
- Jobs.statuses\for_db 'compile_err'
- }), user_id, problem_name
-
- return count[1].count
-
-has_incorrect_submission = (user_id, problem_name) ->
- count_incorrect_submission(user_id, problem_name) > 0
-
-get_jobs_by_user_and_problem_and_competition = (user_id, problem_id, competition_id) ->
- db.select "* from jobs where user_id=? and problem_id=? and competition_id=? order by time_initiated desc", user_id, problem_id, competition_id
-
-get_first_correct_submission = (user_id, problem_id, competition_id) ->
- jobs = db.select "* from jobs where user_id=? and problem_id=? and competition_id=? order by time_initiated asc limit 1", user_id, problem_id, competition_id
- jobs[1]
-
-delete_leaderboard_for_competition = (competition_id) ->
- db.query "delete from leaderboard_problems
- where leaderboard_placement_id in
- (select id from leaderboard_placements where competition_id=?)", competition_id
-
- db.delete "leaderboard_placements", competition_id: competition_id
-
-get_user_score = (user_id, competition_id) ->
- res = db.select "sum(points)
- from leaderboard_problems
- inner join leaderboard_placements on leaderboard_placements.id=leaderboard_problems.leaderboard_placement_id
- where leaderboard_placements.user_id=? and competition_id=?", user_id, competition_id
-
- return 0 if #res == 0
- res[1].sum
-
-get_codegolf_leaders = (problem_id, competition_id) ->
- db.select "user_id, problem_id, competition_id, status, time_initiated, char_length(code) as bytes
- from jobs
- where problem_id=? and competition_id=?
- order by status asc, bytes asc, time_initiated asc", problem_id, competition_id
-
-clear_codegolf_scores = (problem_id, competition_id) ->
- db.query "update leaderboard_problems
- set points=0, status=1
- from leaderboard_placements
- where problem_id=? and leaderboard_placements.competition_id=?", problem_id, competition_id
-
--> {
- :has_correct_submission
- :count_incorrect_submission
- :has_incorrect_submission
- :get_jobs_by_user_and_problem_and_competition
- :get_first_correct_submission
- :delete_leaderboard_for_competition
- :get_user_score
- :get_codegolf_leaders
- :clear_codegolf_scores
-}
class Scoring extends Injectable
new: =>
- @queries = @make 'queries'
@time = @make 'time'
+ @updater = @make 'updater'
-- Get the currently active competition
@competition = Competitions\find active: true
setup_scoring_tables: =>
-- Delete all old leaderboard entries for this competition
- @queries.delete_leaderboard_for_competition @competition.id
+ Competitions\delete_leaderboard @competition.id
-- Refetch the problems and users in case they have changed
@comp_problems = @competition\get_competition_problems!
-- Count the incorrect submissions, and if there are
-- any, set the status of the problem to be wrong
- attempts += @queries.count_incorrect_submission user_id, problem.short_name
+ attempts += Users\count_incorrect_submission user_id, problem.short_name
if attempts > 0
status = LeaderboardProblems.statuses.wrong
-- (best for problem worth) and compute the points
-- for this problem:
-- points = programming_points * worth - 50 * wrong_attempts
- if @queries.has_correct_submission user_id, problem.short_name
- job = @queries.get_first_correct_submission user_id, problem.id, @competition.id
+ if Users\has_correct_submission user_id, problem.short_name
+ job = Users\get_first_correct_submission user_id, problem.id, @competition.id
points += math.ceil (@competition.programming_points * @get_problem_worth job.time_initiated)
points -= 50 * attempts
score_codegolf: (problem) =>
-- Clear the codegolf scores for this problem in this competition
- @queries.clear_codegolf_scores problem.id, @competition.id
+ Problems\clear_codegolf_scores problem.id, @competition.id
-- Get the best code golf submisssions
- leaders = @queries.get_codegolf_leaders problem.id, @competition.id
+ leaders = Problems\get_codegolf_leaders problem.id, @competition.id
points = @competition.codegolf_points
third_points = points / 3
place: =>
for u in *@users
- u.score = @queries.get_user_score u.id, @competition.id
+ u.score = Users\get_score u.id, @competition.id
table.sort @users, (a, b) ->
a.score > b.score
place: num
score: u.score
+ @updater\push_leaderboard_update!
+
rescore_everything: =>
-- Completely resets everything if a problem is
-- added or removed or if a user registers
+updateLeaderboard = ->
+ $.get '/leaderboard/update', {}, (html, _, data) ->
+ $('#leaderboard-container').html html
+
$(document).ready ->
- console.log "Hello!"
+ socket = io()
+ socket.emit "request-leaderboard-updates"
+
+ socket.on 'update', ->
+ updateLeaderboard()
// Generated by CoffeeScript 2.4.1
(function() {
+ var updateLeaderboard;
+
+ updateLeaderboard = function() {
+ return $.get('/leaderboard/update', {}, function(html, _, data) {
+ return $('#leaderboard-container').html(html);
+ });
+ };
+
$(document).ready(function() {
- return console.log("Hello!");
+ var socket;
+ socket = io();
+ socket.emit("request-leaderboard-updates");
+ return socket.on('update', function() {
+ return updateLeaderboard();
+ });
});
}).call(this);
"coffee/leaderboard_update.coffee"
],
"names": [],
- "mappings": ";AAAA;EAAA,CAAA,CAAE,QAAF,CAAW,CAAC,KAAZ,CAAkB,QAAA,CAAA,CAAA;WACd,OAAO,CAAC,GAAR,CAAY,QAAZ;EADc,CAAlB;AAAA",
+ "mappings": ";AAAA;AAAA,MAAA;;EAAA,iBAAA,GAAoB,QAAA,CAAA,CAAA;WACnB,CAAC,CAAC,GAAF,CAAM,qBAAN,EAA6B,CAAA,CAA7B,EAAiC,QAAA,CAAC,IAAD,EAAO,CAAP,EAAU,IAAV,CAAA;aAChC,CAAA,CAAE,wBAAF,CAA2B,CAAC,IAA5B,CAAiC,IAAjC;IADgC,CAAjC;EADmB;;EAIpB,CAAA,CAAE,QAAF,CAAW,CAAC,KAAZ,CAAkB,QAAA,CAAA,CAAA;AACjB,QAAA;IAAA,MAAA,GAAS,EAAA,CAAA;IACT,MAAM,CAAC,IAAP,CAAY,6BAAZ;WAEA,MAAM,CAAC,EAAP,CAAU,QAAV,EAAoB,QAAA,CAAA,CAAA;aACnB,iBAAA,CAAA;IADmB,CAApB;EAJiB,CAAlB;AAJA",
"sourcesContent": [
- "$(document).ready ->\n console.log \"Hello!\"\n"
+ "updateLeaderboard = ->\n\t$.get '/leaderboard/update', {}, (html, _, data) ->\n\t\t$('#leaderboard-container').html html\n\n$(document).ready ->\n\tsocket = io()\n\tsocket.emit \"request-leaderboard-updates\"\n\n\tsocket.on 'update', ->\n\t\tupdateLeaderboard()\n"
]
}
\ No newline at end of file
content: =>
h1 "#{@competition.name} - Leaderboard"
- div class: 'content', ->
+ div id: 'leaderboard-container', class: 'content', ->
widget (Leaderboard @placements)
html = require 'lapis.html'
-import Competitions from require 'models'
+import Competitions, Users from require 'models'
class ProblemSidebar extends html.Widget
load_problems: =>
@problems = @current_comp\get_problems!
for prob in *@problems
- if @queries.has_correct_submission @user.id, prob.short_name
+ if Users\has_correct_submission @user.id, prob.short_name
prob.tag = "correct"
- elseif @queries.has_incorrect_submission @user.id, prob.short_name
- prob.tab = "incorrect"
+ elseif Users\has_incorrect_submission @user.id, prob.short_name
+ prob.tag = "incorrect"
content: =>
@load_problems!
div {
selected: prob.short_name == @params.problem_name
correct: prob.tag == "correct"
- wrong: prob.tab == "incorrect"
+ wrong: prob.tag == "incorrect"
class: 'sidebar-problem'
}, ->
div class: 'sidebar-problem-letter', -> text prob.letter
- div class: 'sidebar-problem-name', -> text prob.name
\ No newline at end of file
+ div class: 'sidebar-problem-name', -> text prob.name
html = require 'lapis.html'
-import CompetitionProblems, LeaderboardProblems from require 'models'
+import CompetitionProblems, LeaderboardProblems, Problems from require 'models'
class Leaderboard extends html.Widget
new: (@placements) =>
flip: true
local_key: 'problem_id'
where: { competition_id: @competition.id }
+ Problems\include_in @problems, 'id',
+ as: 'p'
+ flip: true
+ local_key: 'problem_id'
+ fields: 'id, kind'
-- Sort the problems by letter
prob.lnum = (prob.cp.letter\byte 1) for prob in *@problems
div "Name"
div class: 'problem', style: "grid-template-columns: repeat(#{#@problems}, 1fr)", ->
for prob in *@problems
- div "#{prob.cp.letter}"
+ div style: 'position: relative', ->
+ if prob.p.kind == Problems.kinds.golf
+ span style: "position: absolute; left: 0; top 0; font-size:.8rem", 'Golf'
+ div "#{prob.cp.letter}"
div "Score"
drawn_labels = true
html = require 'lapis.html'
-import Jobs from require 'models'
+import Jobs, Users from require 'models'
class SubmissionList extends html.Widget
content: =>
for prob in *@problems
div class: 'header-line', -> div prob.name
div class: 'box', ->
- jobs = @queries.get_jobs_by_user_and_problem_and_competition @user.id, prob.id, @competition.id
+ jobs = Users\get_jobs_by_problem @user.id, prob.id, @competition.id
if #jobs == 0
div class: 'pad-12', "No submissions to this problem."
return
update_forwarder = new UpdateForwarder()
update_forwarder.add_channel "submission-updates"
+update_forwarder.add_channel "leaderboard-updates"
io.on 'connection', (socket) ->
# data is the submission id
socket.param = data
update_forwarder.join_channel "submission-updates", socket
+ socket.on 'request-leaderboard-updates', (data) ->
+ update_forwarder.join_channel "leaderboard-updates", socket
+
socket.once 'disconnect', ->
update_forwarder.leave socket
res.status 200
res.end()
+app.get '/leaderboard_update', (req, res) ->
+ update_forwarder.push_update "leaderboard-updates"
+
+ res.status 200
+ res.end()
+
main = ->
port = 5000
console.log "Socket IO server running on port #{port}"