Description:
grades contest submissions
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r385:865ba2aed5c0 - - 3 files changed: 73 inserted, 25 deleted

@@ -0,0 +1,47
1 + #!/usr/bin/env ruby
2 +
3 + APP_PATH = File.expand_path('../../config/application', __FILE__)
4 + require File.expand_path('../../config/boot', __FILE__)
5 + require APP_PATH
6 +
7 + # set Rails.env here if desired
8 + Rails.application.require_environment!
9 +
10 + def main
11 + if ARGV.length != 1
12 + puts "Usage: contest_grade_prob.rb [problem_name]"
13 + exit(0)
14 + end
15 +
16 + problem_name = ARGV[0]
17 + problem = Problem.where(:name => problem_name).first
18 + if !problem
19 + puts "Problem not found"
20 + exit(0)
21 + end
22 +
23 + problem.full_score = 100
24 + problem.save
25 +
26 + test_pair = TestPair.get_for(problem, true)
27 +
28 + User.all.each do |u|
29 + puts "#{u.login}:"
30 + submissions = Submission.find_all_by_user_problem(u.id, problem.id)
31 + submissions.each do |sub|
32 + result = test_pair.grade(sub.output)
33 + result2 = test_pair.grade(sub.source)
34 + if result2[:score] > result[:score]
35 + result = result2
36 + puts "Use source field (#{sub.id})"
37 + end
38 +
39 + full_score = result[:full_score]
40 + sub.points = result[:score]*100 / full_score
41 + sub.grader_comment = result[:msg]
42 + sub.save
43 + end
44 + end
45 + end
46 +
47 + main
@@ -97,420 +97,396
97 97
98 98 if GraderConfiguration.time_limit_mode? and user.contest_finished?
99 99 @submission.errors.add(:base,"The contest is over.")
100 100 prepare_list_information
101 101 render :action => 'list' and return
102 102 end
103 103
104 104 if @submission.valid?
105 105 if @submission.save == false
106 106 flash[:notice] = 'Error saving your submission'
107 107 elsif Task.create(:submission_id => @submission.id,
108 108 :status => Task::STATUS_INQUEUE) == false
109 109 flash[:notice] = 'Error adding your submission to task queue'
110 110 else
111 111 flash[:notice] = 'จัดเก็บคำตอบและโปรแกรมที่คุณส่งเรียบร้อย'
112 112 end
113 113 else
114 114 prepare_list_information
115 115 render :action => 'list' and return
116 116 end
117 117
118 118 if @current_problem
119 119 session[:current_problem_id] = @current_problem.id
120 120 end
121 121 redirect_to :action => 'list'
122 122 end
123 123
124 124 def source
125 125 submission = Submission.find(params[:id])
126 126 if ((submission.user_id == session[:user_id]) and
127 127 (submission.problem != nil) and
128 128 (submission.problem.available))
129 129 send_data(submission.source,
130 130 {:filename => submission.download_filename,
131 131 :type => 'text/plain'})
132 132 else
133 133 flash[:notice] = 'Error viewing source'
134 134 redirect_to :action => 'list'
135 135 end
136 136 end
137 137
138 138 def compiler_msg
139 139 @submission = Submission.find(params[:id])
140 140 if @submission.user_id == session[:user_id]
141 141 render :action => 'compiler_msg', :layout => 'empty'
142 142 else
143 143 flash[:notice] = 'Error viewing source'
144 144 redirect_to :action => 'list'
145 145 end
146 146 end
147 147
148 148 def submission
149 149 @user = User.find(session[:user_id])
150 150 @problems = @user.available_problems
151 151 if params[:id]==nil
152 152 @problem = nil
153 153 @submissions = nil
154 154 else
155 155 @problem = Problem.find_by_name(params[:id])
156 156 if not @problem.available
157 157 redirect_to :action => 'list'
158 158 flash[:notice] = 'Error: submissions for that problem are not viewable.'
159 159 return
160 160 end
161 161 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
162 162 end
163 163 end
164 164
165 165 def result
166 166 if !GraderConfiguration.show_grading_result
167 167 redirect_to :action => 'list' and return
168 168 end
169 169 @user = User.find(session[:user_id])
170 170 @submission = Submission.find(params[:id])
171 171 if @submission.user!=@user
172 172 flash[:notice] = 'You are not allowed to view result of other users.'
173 173 redirect_to :action => 'list' and return
174 174 end
175 175 prepare_grading_result(@submission)
176 176 end
177 177
178 178 def load_output
179 179 if !GraderConfiguration.show_grading_result or params[:num]==nil
180 180 redirect_to :action => 'list' and return
181 181 end
182 182 @user = User.find(session[:user_id])
183 183 @submission = Submission.find(params[:id])
184 184 if @submission.user!=@user
185 185 flash[:notice] = 'You are not allowed to view result of other users.'
186 186 redirect_to :action => 'list' and return
187 187 end
188 188 case_num = params[:num].to_i
189 189 out_filename = output_filename(@user.login,
190 190 @submission.problem.name,
191 191 @submission.id,
192 192 case_num)
193 193 if !FileTest.exists?(out_filename)
194 194 flash[:notice] = 'Output not found.'
195 195 redirect_to :action => 'list' and return
196 196 end
197 197
198 198 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
199 199 response.headers['Content-Type'] = "application/force-download"
200 200 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
201 201 response.headers["X-Sendfile"] = out_filename
202 202 response.headers['Content-length'] = File.size(out_filename)
203 203 render :nothing => true
204 204 else
205 205 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
206 206 end
207 207 end
208 208
209 209 def error
210 210 @user = User.find(session[:user_id])
211 211 end
212 212
213 213 # announcement refreshing and hiding methods
214 214
215 215 def announcements
216 216 if params.has_key? 'recent'
217 217 prepare_announcements(params[:recent])
218 218 else
219 219 prepare_announcements
220 220 end
221 221 render(:partial => 'announcement',
222 222 :collection => @announcements,
223 223 :locals => {:announcement_effect => true})
224 224 end
225 225
226 226 def confirm_contest_start
227 227 user = User.find(session[:user_id])
228 228 if request.method == 'POST'
229 229 user.update_start_time
230 230 redirect_to :action => 'list'
231 231 else
232 232 @contests = user.contests
233 233 @user = user
234 234 end
235 235 end
236 236
237 237 # thailandoi contests
238 238
239 239 def verifying_testcase
240 240 problem = Problem.find(params[:id])
241 241 if !problem.available
242 242 flash[:notice] = 'Error: problem is not available'
243 243 redirect_to :action => 'list'
244 244 else
245 245 test_pair = TestPair.get_for(problem, false)
246 246 send_data(test_pair.input,
247 247 {:filename => problem.name + '-verifying-input.txt',
248 248 :type => 'text/plain'})
249 249 end
250 250 end
251 251
252 252 def testcase
253 253 problem = Problem.find(params[:id])
254 254 if !problem.available
255 255 flash[:notice] = 'Error: problem is not available'
256 256 redirect_to :action => 'list'
257 257 else
258 258 test_pair = TestPair.get_for(problem, true)
259 259
260 260 user = User.find(session[:user_id])
261 261 assignment = user.get_test_pair_assignment_for(problem)
262 262
263 263 if !assignment
264 264 assignment = TestPairAssignment.create_for(user, problem, test_pair)
265 265 assignment.save
266 266 end
267 267
268 268 send_data(test_pair.input,
269 269 {:filename => problem.name + '-input.txt',
270 270 :type => 'text/plain'})
271 271 end
272 272 end
273 273
274 274 def verifying_submit
275 275 user = User.find(session[:user_id])
276 276 problem_id = params[:id]
277 277 problem = Problem.find(problem_id)
278 278
279 279 if !problem or !problem.available
280 280 flash[:notice] = 'Error: problem is not available'
281 281 redirect_to :action => 'list' and return
282 282 end
283 283
284 284 @current_problem = problem
285 285 test_pair = TestPair.get_for(problem, false)
286 286 if (params['output_file']) and (params['output_file']!='')
287 287 output = params['output_file'].read
288 288
289 - @grading_result = grade(output, test_pair.solution)
289 + @grading_result = test_pair.grade(output)
290 290 prepare_list_information
291 291 render :action => 'list' and return
292 292 else
293 293 flash[:notice] = 'Error: output file errors'
294 294 prepare_list_information
295 295 render :action => 'list' and return
296 296 end
297 297 end
298 298
299 299 protected
300 300
301 - def grade(output, solution)
302 - out_items = output.split("\n")
303 - sol_items = solution.split("\n")
304 - res = ''
305 - f = 0
306 - s = 0
307 - sol_items.length.times do |i|
308 - f += 1
309 - si = sol_items[i].chomp
310 - if out_items[i]
311 - oi = out_items[i].chomp
312 - else
313 - oi = ''
314 - end
315 - if oi == si
316 - res = res + 'P'
317 - s += 1
318 - else
319 - res = res + '-'
320 - end
321 - end
322 - return { :score => s, :full_score => f, :msg => res }
323 - end
324 -
325 301 def prepare_announcements(recent=nil)
326 302 if GraderConfiguration.show_tasks_to?(@user)
327 303 @announcements = Announcement.find_published(true)
328 304 else
329 305 @announcements = Announcement.find_published
330 306 end
331 307 if recent!=nil
332 308 recent_id = recent.to_i
333 309 @announcements = @announcements.find_all { |a| a.id > recent_id }
334 310 end
335 311 end
336 312
337 313 def prepare_timeout_information(problems)
338 314 @submission_timeouts = {}
339 315 problems.each do |problem|
340 316 assignment = @user.get_test_pair_assignment_for(problem)
341 317 if assignment == nil
342 318 timeout = nil
343 319 else
344 320 if (assignment.expired?) or (assignment.submitted)
345 321 timeout = 0
346 322 else
347 323 timeout = assignment.created_at + TEST_ASSIGNMENT_EXPIRATION_DURATION - Time.new.gmtime
348 324 end
349 325 end
350 326 @submission_timeouts[problem.id] = timeout
351 327 end
352 328 end
353 329
354 330 def prepare_list_information
355 331 @user = User.find(session[:user_id])
356 332 if not GraderConfiguration.multicontests?
357 333 @problems = @user.available_problems
358 334 else
359 335 @contest_problems = @user.available_problems_group_by_contests
360 336 @problems = @user.available_problems
361 337 end
362 338 @prob_submissions = {}
363 339 @problems.each do |p|
364 340 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
365 341 if sub!=nil
366 342 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
367 343 else
368 344 @prob_submissions[p.id] = { :count => 0, :submission => nil }
369 345 end
370 346 end
371 347 prepare_announcements
372 348 prepare_timeout_information(@problems)
373 349 end
374 350
375 351 def check_viewability
376 352 @user = User.find(session[:user_id])
377 353 if (!GraderConfiguration.show_tasks_to?(@user)) and
378 354 ((action_name=='submission') or (action_name=='submit'))
379 355 redirect_to :action => 'list' and return
380 356 end
381 357 end
382 358
383 359 def prepare_grading_result(submission)
384 360 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
385 361 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
386 362 else
387 363 # guess task info from problem.full_score
388 364 cases = submission.problem.full_score / 10
389 365 grading_info = {
390 366 'testruns' => cases,
391 367 'testcases' => cases
392 368 }
393 369 end
394 370 @test_runs = []
395 371 if grading_info['testruns'].is_a? Integer
396 372 trun_count = grading_info['testruns']
397 373 trun_count.times do |i|
398 374 @test_runs << [ read_grading_result(@user.login,
399 375 submission.problem.name,
400 376 submission.id,
401 377 i+1) ]
402 378 end
403 379 else
404 380 grading_info['testruns'].keys.sort.each do |num|
405 381 run = []
406 382 testrun = grading_info['testruns'][num]
407 383 testrun.each do |c|
408 384 run << read_grading_result(@user.login,
409 385 submission.problem.name,
410 386 submission.id,
411 387 c)
412 388 end
413 389 @test_runs << run
414 390 end
415 391 end
416 392 end
417 393
418 394 def grading_result_dir(user_name, problem_name, submission_id, case_num)
419 395 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
420 396 end
421 397
422 398 def output_filename(user_name, problem_name, submission_id, case_num)
423 399 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
424 400 return "#{dir}/output.txt"
425 401 end
426 402
427 403 def read_grading_result(user_name, problem_name, submission_id, case_num)
428 404 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
429 405 result_file_name = "#{dir}/result"
430 406 if !FileTest.exists?(result_file_name)
431 407 return {:num => case_num, :msg => 'program did not run'}
432 408 else
433 409 results = File.open(result_file_name).readlines
434 410 run_stat = extract_running_stat(results)
435 411 output_filename = "#{dir}/output.txt"
436 412 if FileTest.exists?(output_filename)
437 413 output_file = true
438 414 output_size = File.size(output_filename)
439 415 else
440 416 output_file = false
441 417 output_size = 0
442 418 end
443 419
444 420 return {
445 421 :num => case_num,
446 422 :msg => results[0],
447 423 :run_stat => run_stat,
448 424 :output => output_file,
449 425 :output_size => output_size
450 426 }
451 427 end
452 428 end
453 429
454 430 # copied from grader/script/lib/test_request_helper.rb
455 431 def extract_running_stat(results)
456 432 running_stat_line = results[-1]
457 433
458 434 # extract exit status line
459 435 run_stat = ""
460 436 if !(/[Cc]orrect/.match(results[0]))
461 437 run_stat = results[0].chomp
462 438 else
463 439 run_stat = 'Program exited normally'
464 440 end
465 441
466 442 logger.info "Stat line: #{running_stat_line}"
467 443
468 444 # extract running time
469 445 if res = /r(.*)u(.*)s/.match(running_stat_line)
470 446 seconds = (res[1].to_f + res[2].to_f)
471 447 time_stat = "Time used: #{seconds} sec."
472 448 else
473 449 seconds = nil
474 450 time_stat = "Time used: n/a sec."
475 451 end
476 452
477 453 # extract memory usage
478 454 if res = /s(.*)m/.match(running_stat_line)
479 455 memory_used = res[1].to_i
480 456 else
481 457 memory_used = -1
482 458 end
483 459
484 460 return {
485 461 :msg => "#{run_stat}\n#{time_stat}",
486 462 :running_time => seconds,
487 463 :exit_status => run_stat,
488 464 :memory_usage => memory_used
489 465 }
490 466 end
491 467
492 468 def confirm_and_update_start_time
493 469 user = User.find(session[:user_id])
494 470 if (GraderConfiguration.indv_contest_mode? and
495 471 GraderConfiguration['contest.confirm_indv_contest_start'] and
496 472 !user.contest_started?)
497 473 redirect_to :action => 'confirm_contest_start' and return
498 474 end
499 475 if not GraderConfiguration.analysis_mode?
500 476 user.update_start_time
501 477 end
502 478 end
503 479
504 480 def reject_announcement_refresh_when_logged_out
505 481 if not session[:user_id]
506 482 render :text => 'Access forbidden', :status => 403
507 483 end
508 484
509 485 if GraderConfiguration.multicontests?
510 486 user = User.find(session[:user_id])
511 487 if user.contest_stat.forced_logout
512 488 render :text => 'Access forbidden', :status => 403
513 489 end
514 490 end
515 491 end
516 492
@@ -1,8 +1,33
1 1 class TestPair < ActiveRecord::Base
2 2 belongs_to :problem
3 3
4 4 def self.get_for(problem, is_private)
5 5 return TestPair.where(:problem_id => problem.id,
6 6 :is_private => is_private).first
7 7 end
8 +
9 + def grade(output)
10 + out_items = output.split("\n")
11 + sol_items = solution.split("\n")
12 + res = ''
13 + f = 0
14 + s = 0
15 + sol_items.length.times do |i|
16 + f += 1
17 + si = sol_items[i].chomp
18 + if out_items[i]
19 + oi = out_items[i].chomp
20 + else
21 + oi = ''
22 + end
23 + if oi == si
24 + res = res + 'P'
25 + s += 1
26 + else
27 + res = res + '-'
28 + end
29 + end
30 + return { :score => s, :full_score => f, :msg => res }
31 + end
32 +
8 33 end
You need to be logged in to leave comments. Login now