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

r176:1bdb8b71a462 - - 1 file changed: 1 inserted, 1 deleted

@@ -48,424 +48,424
48 using: (1) grader
48 using: (1) grader
49 (2) grader environment [mode] [options]
49 (2) grader environment [mode] [options]
50 (3) grader stop [all|pids-list]
50 (3) grader stop [all|pids-list]
51 (4) grader --help
51 (4) grader --help
52 (1) call grader with environment = 'exam', mode = 'queue'
52 (1) call grader with environment = 'exam', mode = 'queue'
53 (2) possible modes are: 'queue', 'test_request', 'prob', 'sub', 'contest', and 'autonew'
53 (2) possible modes are: 'queue', 'test_request', 'prob', 'sub', 'contest', and 'autonew'
54 queue: repeatedly check the task queue and grade any available tasks
54 queue: repeatedly check the task queue and grade any available tasks
55
55
56 prob: re-grade every user latest submission of the specific problem.
56 prob: re-grade every user latest submission of the specific problem.
57 the problem name must be specified by the next argument.
57 the problem name must be specified by the next argument.
58
58
59 additional options:
59 additional options:
60 --all-sub re-grade every submissions instead of just the latest submission of each user.
60 --all-sub re-grade every submissions instead of just the latest submission of each user.
61
61
62 sub: re-grader the specified submission.
62 sub: re-grader the specified submission.
63 The submission ID to be re-graded must be specified by the next argument.
63 The submission ID to be re-graded must be specified by the next argument.
64
64
65 options:
65 options:
66 --err-log log error to a file in the log dir
66 --err-log log error to a file in the log dir
67
67
68 (3) create stop-file to stop running grader in queue mode
68 (3) create stop-file to stop running grader in queue mode
69 (4) You are here.
69 (4) You are here.
70 USAGE
70 USAGE
71 end
71 end
72
72
73 def process_options_and_stop_file
73 def process_options_and_stop_file
74 # The list of options are:
74 # The list of options are:
75 # - stop [all|process ids]
75 # - stop [all|process ids]
76 # -
76 # -
77
77
78 # Process 'help' option
78 # Process 'help' option
79 if (ARGV.length==1) and (/help/.match(ARGV[0]))
79 if (ARGV.length==1) and (/help/.match(ARGV[0]))
80 display_manual
80 display_manual
81 exit(0)
81 exit(0)
82 end
82 end
83
83
84 # Process 'stop' option.
84 # Process 'stop' option.
85 if (ARGV.length >= 1) and (ARGV[0]=='stop')
85 if (ARGV.length >= 1) and (ARGV[0]=='stop')
86 if ARGV.length==1
86 if ARGV.length==1
87 puts "you should specify pid-list or 'all'"
87 puts "you should specify pid-list or 'all'"
88 display_manual
88 display_manual
89 elsif (ARGV.length==2) and (ARGV[1]=='all')
89 elsif (ARGV.length==2) and (ARGV[1]=='all')
90 stop_grader(:all)
90 stop_grader(:all)
91 puts "A global stop file ('stop.all') created."
91 puts "A global stop file ('stop.all') created."
92 puts "You should remove it manually later."
92 puts "You should remove it manually later."
93 else
93 else
94 (1..ARGV.length-1).each do |i|
94 (1..ARGV.length-1).each do |i|
95 stop_grader(ARGV[i])
95 stop_grader(ARGV[i])
96 end
96 end
97 puts "stop file(s) created"
97 puts "stop file(s) created"
98 end
98 end
99 exit(0)
99 exit(0)
100 end
100 end
101
101
102 # Check stop file.
102 # Check stop file.
103 if check_stopfile
103 if check_stopfile
104 puts "Stop file exists. Terminated."
104 puts "Stop file exists. Terminated."
105 clear_stopfile
105 clear_stopfile
106 exit(0)
106 exit(0)
107 end
107 end
108
108
109 #default options
109 #default options
110 options = {
110 options = {
111 :mode => 'queue',
111 :mode => 'queue',
112 :environment => 'exam',
112 :environment => 'exam',
113 :dry_run => false,
113 :dry_run => false,
114 }
114 }
115
115
116 # Process mode and environment option
116 # Process mode and environment option
117 if ARGV.length >= 1
117 if ARGV.length >= 1
118 options[:environment] = ARGV.shift
118 options[:environment] = ARGV.shift
119 if ARGV.length >=1
119 if ARGV.length >=1
120 options[:mode] = ARGV.shift
120 options[:mode] = ARGV.shift
121 end
121 end
122 else
122 else
123 puts 'no argument specified, using default mode and environment.'
123 puts 'no argument specified, using default mode and environment.'
124 end
124 end
125
125
126 options[:dry_run] = (ARGV.delete('--dry') != nil)
126 options[:dry_run] = (ARGV.delete('--dry') != nil)
127 if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode])
127 if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode])
128 puts "Dry run currently works only for 'prob' or 'contest' modes."
128 puts "Dry run currently works only for 'prob' or 'contest' modes."
129 exit(0)
129 exit(0)
130 end
130 end
131
131
132 options[:report] = (ARGV.delete('--report') != nil)
132 options[:report] = (ARGV.delete('--report') != nil)
133 if options[:report] and (not ['prob','contest','autonew'].include? options[:mode])
133 if options[:report] and (not ['prob','contest','autonew'].include? options[:mode])
134 puts "Report currently works only for 'prob' or 'contest' modes."
134 puts "Report currently works only for 'prob' or 'contest' modes."
135 exit(0)
135 exit(0)
136 end
136 end
137
137
138 options[:all_sub] = (ARGV.delete('--all-sub') != nil)
138 options[:all_sub] = (ARGV.delete('--all-sub') != nil)
139
139
140 options[:err_log] = (ARGV.delete('--err-log') != nil)
140 options[:err_log] = (ARGV.delete('--err-log') != nil)
141
141
142 return options
142 return options
143 end
143 end
144
144
145 class ResultCollector
145 class ResultCollector
146 def initialize
146 def initialize
147 @results = {}
147 @results = {}
148 @problems = {}
148 @problems = {}
149 @users = {}
149 @users = {}
150 end
150 end
151
151
152 def after_save_hook(submission, grading_result)
152 def after_save_hook(submission, grading_result)
153 end
153 end
154
154
155 def save(submission, grading_result)
155 def save(submission, grading_result)
156 user = submission.user
156 user = submission.user
157 problem = submission.problem
157 problem = submission.problem
158 if not @problems.has_key? problem.id
158 if not @problems.has_key? problem.id
159 @problems[problem.id] = problem
159 @problems[problem.id] = problem
160 end
160 end
161 if not @users.has_key? user.id
161 if not @users.has_key? user.id
162 @users[user.id] = user
162 @users[user.id] = user
163 end
163 end
164 @results[[user.id, problem.id]] = grading_result
164 @results[[user.id, problem.id]] = grading_result
165
165
166 after_save_hook(submission, grading_result)
166 after_save_hook(submission, grading_result)
167 end
167 end
168
168
169 def print_report_by_user
169 def print_report_by_user
170 puts "---------------------"
170 puts "---------------------"
171 puts " REPORT"
171 puts " REPORT"
172 puts "---------------------"
172 puts "---------------------"
173
173
174 print "login,email"
174 print "login,email"
175 @problems.each_value do |problem|
175 @problems.each_value do |problem|
176 print ",#{problem.name}"
176 print ",#{problem.name}"
177 end
177 end
178 print "\n"
178 print "\n"
179
179
180 @users.each_value do |user|
180 @users.each_value do |user|
181 print "#{user.login},#{user.email}"
181 print "#{user.login},#{user.email}"
182 @problems.each_value do |problem|
182 @problems.each_value do |problem|
183 if @results.has_key? [user.id, problem.id]
183 if @results.has_key? [user.id, problem.id]
184 print ",#{@results[[user.id,problem.id]][:points]}"
184 print ",#{@results[[user.id,problem.id]][:points]}"
185 else
185 else
186 print ","
186 print ","
187 end
187 end
188 end
188 end
189 print "\n"
189 print "\n"
190 end
190 end
191 end
191 end
192 end
192 end
193
193
194 def grader_general_loop(engine, grader_proc, options)
194 def grader_general_loop(engine, grader_proc, options)
195 runner = Grader::Runner.new(engine, grader_proc)
195 runner = Grader::Runner.new(engine, grader_proc)
196 while true
196 while true
197
197
198 if check_stopfile # created by calling grader stop
198 if check_stopfile # created by calling grader stop
199 clear_stopfile
199 clear_stopfile
200 log "stopped (with stop file)"
200 log "stopped (with stop file)"
201 break
201 break
202 end
202 end
203
203
204 task = yield(runner)
204 task = yield(runner)
205
205
206 if task==nil
206 if task==nil
207 sleep(1)
207 sleep(1)
208 end
208 end
209 end
209 end
210 end
210 end
211
211
212 def grader_queue_loop(grader_proc, options)
212 def grader_queue_loop(grader_proc, options)
213 log "Grader: queue"
213 log "Grader: queue"
214 engine = Grader::Engine.new
214 engine = Grader::Engine.new
215 grader_general_loop(engine, grader_proc, options) do |runner|
215 grader_general_loop(engine, grader_proc, options) do |runner|
216 runner.grade_oldest_task
216 runner.grade_oldest_task
217 end
217 end
218 end
218 end
219
219
220 def grader_test_request_loop(grader_proc, options)
220 def grader_test_request_loop(grader_proc, options)
221 log "Grader: test_request"
221 log "Grader: test_request"
222 engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new,
222 engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new,
223 :reporter => Grader::TestRequestReporter.new)
223 :reporter => Grader::TestRequestReporter.new)
224 grader_general_loop(engine, grader_proc, options) do |runner|
224 grader_general_loop(engine, grader_proc, options) do |runner|
225 runner.grade_oldest_test_request
225 runner.grade_oldest_test_request
226 end
226 end
227 end
227 end
228
228
229 def grader_autonew_loop(grader_proc, options)
229 def grader_autonew_loop(grader_proc, options)
230 log "Grader: autonew"
230 log "Grader: autonew"
231
231
232 if options[:report]
232 if options[:report]
233 result_collector = ResultCollector.new
233 result_collector = ResultCollector.new
234 else
234 else
235 result_collector = nil
235 result_collector = nil
236 end
236 end
237
237
238 if options[:dry_run]
238 if options[:dry_run]
239 puts "Running in dry mode"
239 puts "Running in dry mode"
240 end
240 end
241
241
242 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
242 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
243 :result_collector => result_collector)
243 :result_collector => result_collector)
244
244
245 engine = Grader::Engine.new(:reporter => prob_reporter)
245 engine = Grader::Engine.new(:reporter => prob_reporter)
246 runner = Grader::Runner.new(engine, grader_proc)
246 runner = Grader::Runner.new(engine, grader_proc)
247
247
248 grader_proc.report_active if grader_proc!=nil
248 grader_proc.report_active if grader_proc!=nil
249
249
250 latest_submitted_at = nil
250 latest_submitted_at = nil
251 graded_submission_ids = {}
251 graded_submission_ids = {}
252
252
253 while true
253 while true
254
254
255 if check_stopfile # created by calling grader stop
255 if check_stopfile # created by calling grader stop
256 clear_stopfile
256 clear_stopfile
257 log "stopped (with stop file)"
257 log "stopped (with stop file)"
258 break
258 break
259 end
259 end
260
260
261 if latest_submitted_at==nil
261 if latest_submitted_at==nil
262 submissions = Submission.all
262 submissions = Submission.all
263 else
263 else
264 submissions = Submission.all(:conditions => ["submitted_at >= :latest",
264 submissions = Submission.all(:conditions => ["submitted_at >= :latest",
265 {:latest => latest_submitted_at}])
265 {:latest => latest_submitted_at}])
266 end
266 end
267
267
268 graded_any = false
268 graded_any = false
269
269
270 if submissions.length != 0
270 if submissions.length != 0
271 submissions.each do |submission|
271 submissions.each do |submission|
272 if (submission.problem == nil) or (!submission.problem.available)
272 if (submission.problem == nil) or (!submission.problem.available)
273 next
273 next
274 end
274 end
275 if ! graded_submission_ids[submission.id]
275 if ! graded_submission_ids[submission.id]
276 runner.grade_submission(submission)
276 runner.grade_submission(submission)
277 graded_submission_ids[submission.id] = true
277 graded_submission_ids[submission.id] = true
278 if (!latest_submitted_at or
278 if (!latest_submitted_at or
279 latest_submitted_at < submission.submitted_at)
279 latest_submitted_at < submission.submitted_at)
280 latest_submitted_at = submission.submitted_at
280 latest_submitted_at = submission.submitted_at
281 end
281 end
282 puts "graded: #{submission.id}"
282 puts "graded: #{submission.id}"
283 puts "latest: #{latest_submitted_at}"
283 puts "latest: #{latest_submitted_at}"
284 graded_any = true
284 graded_any = true
285 end
285 end
286 end
286 end
287 end
287 end
288
288
289 if ! graded_any
289 if ! graded_any
290 sleep(1)
290 sleep(1)
291 end
291 end
292 end
292 end
293 end
293 end
294
294
295 def grader_grade_problems(grader_proc, options)
295 def grader_grade_problems(grader_proc, options)
296 if options[:report]
296 if options[:report]
297 result_collector = ResultCollector.new
297 result_collector = ResultCollector.new
298 else
298 else
299 result_collector = nil
299 result_collector = nil
300 end
300 end
301
301
302 if options[:dry_run]
302 if options[:dry_run]
303 puts "Running in dry mode"
303 puts "Running in dry mode"
304 end
304 end
305
305
306 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
306 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
307 :result_collector => result_collector)
307 :result_collector => result_collector)
308 engine = Grader::Engine.new(:reporter => prob_reporter)
308 engine = Grader::Engine.new(:reporter => prob_reporter)
309 runner = Grader::Runner.new(engine, grader_proc)
309 runner = Grader::Runner.new(engine, grader_proc)
310
310
311 grader_proc.report_active if grader_proc!=nil
311 grader_proc.report_active if grader_proc!=nil
312
312
313 ARGV.each do |prob_name|
313 ARGV.each do |prob_name|
314 prob = Problem.find_by_name(prob_name)
314 prob = Problem.find_by_name(prob_name)
315 if prob==nil
315 if prob==nil
316 puts "cannot find problem: #{prob_name}"
316 puts "cannot find problem: #{prob_name}"
317 else
317 else
318 runner.grade_problem(prob,options)
318 runner.grade_problem(prob,options)
319 end
319 end
320 end
320 end
321
321
322 if options[:report]
322 if options[:report]
323 result_collector.print_report_by_user
323 result_collector.print_report_by_user
324 end
324 end
325 end
325 end
326
326
327 def grader_grade_contests(grader_proc, options)
327 def grader_grade_contests(grader_proc, options)
328 # always use dry run when grading during contest
328 # always use dry run when grading during contest
329 dry_run = options[:dry_run] = true
329 dry_run = options[:dry_run] = true
330
330
331 contest_name = ARGV.shift
331 contest_name = ARGV.shift
332
332
333 contest = Contest.find_by_name(contest_name)
333 contest = Contest.find_by_name(contest_name)
334 if contest==nil
334 if contest==nil
335 puts "cannot find contest: #{contest_name}"
335 puts "cannot find contest: #{contest_name}"
336 exit(0)
336 exit(0)
337 end
337 end
338
338
339 if options[:report]
339 if options[:report]
340 result_collector = ResultCollector.new
340 result_collector = ResultCollector.new
341 else
341 else
342 result_collector = nil
342 result_collector = nil
343 end
343 end
344
344
345 if options[:dry_run]
345 if options[:dry_run]
346 puts "Running in dry mode"
346 puts "Running in dry mode"
347 end
347 end
348
348
349 prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run,
349 prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run,
350 :result_collector => result_collector)
350 :result_collector => result_collector)
351 engine = Grader::Engine.new(:reporter => prob_reporter)
351 engine = Grader::Engine.new(:reporter => prob_reporter)
352 runner = Grader::Runner.new(engine, grader_proc)
352 runner = Grader::Runner.new(engine, grader_proc)
353
353
354 grader_proc.report_active if grader_proc!=nil
354 grader_proc.report_active if grader_proc!=nil
355
355
356 contest.problems.each do |problem|
356 contest.problems.each do |problem|
357 puts "Grading: #{problem.name}"
357 puts "Grading: #{problem.name}"
358 runner.grade_problem(problem,
358 runner.grade_problem(problem,
359 :user_conditions => lambda do |u|
359 :user_conditions => lambda do |u|
360 u.contest_finished? and
360 u.contest_finished? and
361 u.contest_ids.include?(contest.id)
361 u.contest_ids.include?(contest.id)
362 end)
362 end)
363 end
363 end
364
364
365 if options[:report]
365 if options[:report]
366 result_collector.print_report_by_user
366 result_collector.print_report_by_user
367 end
367 end
368 end
368 end
369
369
370 def grader_grade_submissions(grader_proc, options)
370 def grader_grade_submissions(grader_proc, options)
371 engine = Grader::Engine.new
371 engine = Grader::Engine.new
372 runner = Grader::Runner.new(engine, grader_proc)
372 runner = Grader::Runner.new(engine, grader_proc)
373
373
374 grader_proc.report_active if grader_proc!=nil
374 grader_proc.report_active if grader_proc!=nil
375
375
376 ARGV.each do |sub_id|
376 ARGV.each do |sub_id|
377 puts "Grading #{sub_id}"
377 puts "Grading #{sub_id}"
378 begin
378 begin
379 submission = Submission.find(sub_id.to_i)
379 submission = Submission.find(sub_id.to_i)
380 rescue ActiveRecord::RecordNotFound
380 rescue ActiveRecord::RecordNotFound
381 puts "Submission #{sub_id} not found"
381 puts "Submission #{sub_id} not found"
382 submission = nil
382 submission = nil
383 end
383 end
384
384
385 if submission!=nil
385 if submission!=nil
386 runner.grade_submission(submission)
386 runner.grade_submission(submission)
387 end
387 end
388 end
388 end
389 end
389 end
390
390
391 #########################################
391 #########################################
392 # main program
392 # main program
393 #########################################
393 #########################################
394
394
395 options = process_options_and_stop_file
395 options = process_options_and_stop_file
396 GRADER_ENV = options[:environment]
396 GRADER_ENV = options[:environment]
397 grader_mode = options[:mode]
397 grader_mode = options[:mode]
398 dry_run = options[:dry_run]
398 dry_run = options[:dry_run]
399
399
400 puts "environment: #{GRADER_ENV}"
400 puts "environment: #{GRADER_ENV}"
401 puts "grader mode: #{grader_mode}"
401 puts "grader mode: #{grader_mode}"
402 require File.join(File.dirname(__FILE__),'config/environment')
402 require File.join(File.dirname(__FILE__),'config/environment')
403
403
404 # add grader_mode to config
404 # add grader_mode to config
405 # this is needed because method log needs it. TODO: clean this up
405 # this is needed because method log needs it. TODO: clean this up
406 class << config
406 class << config
407 attr_accessor :grader_mode
407 attr_accessor :grader_mode
408 end
408 end
409 config.grader_mode = grader_mode
409 config.grader_mode = grader_mode
410
410
411 # reading rails environment
411 # reading rails environment
412 log 'Reading rails environment'
412 log 'Reading rails environment'
413
413
414 RAILS_ENV = config.rails_env
414 RAILS_ENV = config.rails_env
415 require RAILS_ROOT + '/config/environment'
415 require RAILS_ROOT + '/config/environment'
416
416
417 # register grader process
417 # register grader process
418 if config.report_grader
418 if config.report_grader
419 grader_proc = GraderProcess.register(config.grader_hostname,
419 grader_proc = GraderProcess.register(config.grader_hostname,
420 Process.pid,
420 Process.pid,
421 grader_mode)
421 grader_mode)
422 else
422 else
423 grader_proc = nil
423 grader_proc = nil
424 end
424 end
425
425
426 #set loggin environment
426 #set loggin environment
427 ENV['GRADER_LOGGING'] = log_file_name
427 ENV['GRADER_LOGGING'] = log_file_name
428 if options[:err_log]
428 if options[:err_log]
429 err_file_name = log_file_name + '.err'
429 err_file_name = log_file_name + '.err'
430 $stderr.reopen(err_file_name,"a")
430 $stderr.reopen(err_file_name,"a")
431 log "STDERR log to file [#{err_file_name}]"
431 log "STDERR log to file [#{err_file_name}]"
432 - warn "start logging for grader PID #{Process.id} on #{Time.now.in_time_zone}"
432 + warn "start logging for grader PID #{Process.pid} on #{Time.now.in_time_zone}"
433 end
433 end
434
434
435
435
436 # register exit handler to report inactive, and terminated
436 # register exit handler to report inactive, and terminated
437 at_exit do
437 at_exit do
438 if grader_proc!=nil
438 if grader_proc!=nil
439 grader_proc.report_inactive
439 grader_proc.report_inactive
440 grader_proc.terminate
440 grader_proc.terminate
441 end
441 end
442 end
442 end
443
443
444 #
444 #
445 # MAIN LOOP
445 # MAIN LOOP
446 #
446 #
447
447
448 case grader_mode
448 case grader_mode
449 when "queue"
449 when "queue"
450 grader_queue_loop(grader_proc, options)
450 grader_queue_loop(grader_proc, options)
451
451
452 when "test_request"
452 when "test_request"
453 grader_test_request_loop(grader_proc, options)
453 grader_test_request_loop(grader_proc, options)
454
454
455 when "prob"
455 when "prob"
456 grader_grade_problems(grader_proc, options)
456 grader_grade_problems(grader_proc, options)
457
457
458 when "contest"
458 when "contest"
459 grader_grade_contests(grader_proc, options)
459 grader_grade_contests(grader_proc, options)
460
460
461 when "sub"
461 when "sub"
462 grader_grade_submissions(grader_proc, options)
462 grader_grade_submissions(grader_proc, options)
463
463
464 when "autonew"
464 when "autonew"
465 grader_autonew_loop(grader_proc, options)
465 grader_autonew_loop(grader_proc, options)
466
466
467 else
467 else
468 display_manual
468 display_manual
469 exit(0)
469 exit(0)
470 end
470 end
471
471
You need to be logged in to leave comments. Login now