Description:
merge to master
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r253:0654ec1bf773 - - 6 files changed: 23 inserted, 12 deleted

@@ -0,0 +1,9
1 + #!/bin/bash
2 + count=`ps aux | grep "algo_grader" | grep "grader grading queue" | wc -l`
3 + install_dir=/home/john/cafe_grader
4 + ruby_executable=/home/john/.rvm/wrappers/ruby-2.3.0/ruby
5 + if [ $count -lt 1 ]; then
6 + cd $judge_dir/judge
7 + $ruby_executable $install_dir/judge/scripts/grader grading queue > $install_dir/judge/grading.log &
8 + fi
9 +
@@ -1,330 +1,332
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2
3 def stop_grader(id)
3 def stop_grader(id)
4 if id==:all
4 if id==:all
5 File.open(File.dirname(__FILE__) + "/stop.all",'w').close
5 File.open(File.dirname(__FILE__) + "/stop.all",'w').close
6 else
6 else
7 File.open(File.dirname(__FILE__) + "/stop.#{id}",'w').close
7 File.open(File.dirname(__FILE__) + "/stop.#{id}",'w').close
8 end
8 end
9 end
9 end
10
10
11 def check_stopfile
11 def check_stopfile
12 FileTest.exist?(File.dirname(__FILE__) + "/stop.all") or
12 FileTest.exist?(File.dirname(__FILE__) + "/stop.all") or
13 FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}")
13 FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}")
14 end
14 end
15
15
16 def clear_stopfile
16 def clear_stopfile
17 if FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}")
17 if FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}")
18 File.delete(File.dirname(__FILE__) + "/stop.#{Process.pid}")
18 File.delete(File.dirname(__FILE__) + "/stop.#{Process.pid}")
19 end
19 end
20 end
20 end
21
21
22 def config
22 def config
23 Grader::Configuration.get_instance
23 Grader::Configuration.get_instance
24 end
24 end
25
25
26 def log_file_name
26 def log_file_name
27 if !File.exists?(config.log_dir)
27 if !File.exists?(config.log_dir)
28 raise "Log directory does not exist: #{config.log_dir}"
28 raise "Log directory does not exist: #{config.log_dir}"
29 end
29 end
30 config.log_dir +
30 config.log_dir +
31 "/#{GRADER_ENV}_#{config.grader_mode}.#{Process.pid}"
31 "/#{GRADER_ENV}_#{config.grader_mode}.#{Process.pid}"
32 end
32 end
33
33
34 def log(str)
34 def log(str)
35 if config.talkative
35 if config.talkative
36 puts str
36 puts str
37 end
37 end
38 if config.logging
38 if config.logging
39 fp = File.open(log_file_name,"a")
39 fp = File.open(log_file_name,"a")
40 fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}")
40 fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}")
41 fp.close
41 fp.close
42 end
42 end
43 end
43 end
44
44
45 def display_manual
45 def display_manual
46 puts <<USAGE
46 puts <<USAGE
47 Grader.
47 Grader.
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 + --only-error re-grade only submissions that are "error during grading"
61
62
62 sub: re-grader the specified submission.
63 sub: re-grader the specified submission.
63 The submission ID to be re-graded must be specified by the next argument.
64 The submission ID to be re-graded must be specified by the next argument.
64
65
65 options:
66 options:
66 --err-log log error to a file in the log dir
67 --err-log log error to a file in the log dir
67
68
68 (3) create stop-file to stop running grader in queue mode
69 (3) create stop-file to stop running grader in queue mode
69 (4) You are here.
70 (4) You are here.
70 USAGE
71 USAGE
71 end
72 end
72
73
73 def process_options_and_stop_file
74 def process_options_and_stop_file
74 # The list of options are:
75 # The list of options are:
75 # - stop [all|process ids]
76 # - stop [all|process ids]
76 # -
77 # -
77
78
78 # Process 'help' option
79 # Process 'help' option
79 if (ARGV.length==1) and (/help/.match(ARGV[0]))
80 if (ARGV.length==1) and (/help/.match(ARGV[0]))
80 display_manual
81 display_manual
81 exit(0)
82 exit(0)
82 end
83 end
83
84
84 # Process 'stop' option.
85 # Process 'stop' option.
85 if (ARGV.length >= 1) and (ARGV[0]=='stop')
86 if (ARGV.length >= 1) and (ARGV[0]=='stop')
86 if ARGV.length==1
87 if ARGV.length==1
87 puts "you should specify pid-list or 'all'"
88 puts "you should specify pid-list or 'all'"
88 display_manual
89 display_manual
89 elsif (ARGV.length==2) and (ARGV[1]=='all')
90 elsif (ARGV.length==2) and (ARGV[1]=='all')
90 stop_grader(:all)
91 stop_grader(:all)
91 puts "A global stop file ('stop.all') created."
92 puts "A global stop file ('stop.all') created."
92 puts "You should remove it manually later."
93 puts "You should remove it manually later."
93 else
94 else
94 (1..ARGV.length-1).each do |i|
95 (1..ARGV.length-1).each do |i|
95 stop_grader(ARGV[i])
96 stop_grader(ARGV[i])
96 end
97 end
97 puts "stop file(s) created"
98 puts "stop file(s) created"
98 end
99 end
99 exit(0)
100 exit(0)
100 end
101 end
101
102
102 # Check stop file.
103 # Check stop file.
103 if check_stopfile
104 if check_stopfile
104 puts "Stop file exists. Terminated."
105 puts "Stop file exists. Terminated."
105 clear_stopfile
106 clear_stopfile
106 exit(0)
107 exit(0)
107 end
108 end
108
109
109 #default options
110 #default options
110 options = {
111 options = {
111 :mode => 'queue',
112 :mode => 'queue',
112 :environment => 'exam',
113 :environment => 'exam',
113 :dry_run => false,
114 :dry_run => false,
114 }
115 }
115
116
116 # Process mode and environment option
117 # Process mode and environment option
117 if ARGV.length >= 1
118 if ARGV.length >= 1
118 options[:environment] = ARGV.shift
119 options[:environment] = ARGV.shift
119 if ARGV.length >=1
120 if ARGV.length >=1
120 options[:mode] = ARGV.shift
121 options[:mode] = ARGV.shift
121 end
122 end
122 else
123 else
123 puts 'no argument specified, using default mode and environment.'
124 puts 'no argument specified, using default mode and environment.'
124 end
125 end
125
126
126 options[:dry_run] = (ARGV.delete('--dry') != nil)
127 options[:dry_run] = (ARGV.delete('--dry') != nil)
127 if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode])
128 if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode])
128 puts "Dry run currently works only for 'prob' or 'contest' modes."
129 puts "Dry run currently works only for 'prob' or 'contest' modes."
129 exit(0)
130 exit(0)
130 end
131 end
131
132
132 options[:report] = (ARGV.delete('--report') != nil)
133 options[:report] = (ARGV.delete('--report') != nil)
133 if options[:report] and (not ['prob','contest','autonew'].include? options[:mode])
134 if options[:report] and (not ['prob','contest','autonew'].include? options[:mode])
134 puts "Report currently works only for 'prob' or 'contest' modes."
135 puts "Report currently works only for 'prob' or 'contest' modes."
135 exit(0)
136 exit(0)
136 end
137 end
137
138
138 options[:all_sub] = (ARGV.delete('--all-sub') != nil)
139 options[:all_sub] = (ARGV.delete('--all-sub') != nil)
140 + options[:only_err] = (ARGV.delete('--only-error') != nil)
139
141
140 options[:err_log] = (ARGV.delete('--err-log') != nil)
142 options[:err_log] = (ARGV.delete('--err-log') != nil)
141
143
142 return options
144 return options
143 end
145 end
144
146
145 class ResultCollector
147 class ResultCollector
146 def initialize
148 def initialize
147 @results = {}
149 @results = {}
148 @problems = {}
150 @problems = {}
149 @users = {}
151 @users = {}
150 end
152 end
151
153
152 def after_save_hook(submission, grading_result)
154 def after_save_hook(submission, grading_result)
153 end
155 end
154
156
155 def save(submission, grading_result)
157 def save(submission, grading_result)
156 user = submission.user
158 user = submission.user
157 problem = submission.problem
159 problem = submission.problem
158 if not @problems.has_key? problem.id
160 if not @problems.has_key? problem.id
159 @problems[problem.id] = problem
161 @problems[problem.id] = problem
160 end
162 end
161 if not @users.has_key? user.id
163 if not @users.has_key? user.id
162 @users[user.id] = user
164 @users[user.id] = user
163 end
165 end
164 @results[[user.id, problem.id]] = grading_result
166 @results[[user.id, problem.id]] = grading_result
165
167
166 after_save_hook(submission, grading_result)
168 after_save_hook(submission, grading_result)
167 end
169 end
168
170
169 def print_report_by_user
171 def print_report_by_user
170 puts "---------------------"
172 puts "---------------------"
171 puts " REPORT"
173 puts " REPORT"
172 puts "---------------------"
174 puts "---------------------"
173
175
174 print "login,email"
176 print "login,email"
175 @problems.each_value do |problem|
177 @problems.each_value do |problem|
176 print ",#{problem.name}"
178 print ",#{problem.name}"
177 end
179 end
178 print "\n"
180 print "\n"
179
181
180 @users.each_value do |user|
182 @users.each_value do |user|
181 print "#{user.login},#{user.email}"
183 print "#{user.login},#{user.email}"
182 @problems.each_value do |problem|
184 @problems.each_value do |problem|
183 if @results.has_key? [user.id, problem.id]
185 if @results.has_key? [user.id, problem.id]
184 print ",#{@results[[user.id,problem.id]][:points]}"
186 print ",#{@results[[user.id,problem.id]][:points]}"
185 else
187 else
186 print ","
188 print ","
187 end
189 end
188 end
190 end
189 print "\n"
191 print "\n"
190 end
192 end
191 end
193 end
192 end
194 end
193
195
194 def grader_general_loop(engine, grader_proc, options)
196 def grader_general_loop(engine, grader_proc, options)
195 runner = Grader::Runner.new(engine, grader_proc)
197 runner = Grader::Runner.new(engine, grader_proc)
196 while true
198 while true
197
199
198 if check_stopfile # created by calling grader stop
200 if check_stopfile # created by calling grader stop
199 clear_stopfile
201 clear_stopfile
200 log "stopped (with stop file)"
202 log "stopped (with stop file)"
201 break
203 break
202 end
204 end
203
205
204 task = yield(runner)
206 task = yield(runner)
205
207
206 if task==nil
208 if task==nil
207 sleep(1)
209 sleep(1)
208 end
210 end
209 end
211 end
210 end
212 end
211
213
212 def grader_queue_loop(grader_proc, options)
214 def grader_queue_loop(grader_proc, options)
213 log "Grader: queue"
215 log "Grader: queue"
214 engine = Grader::Engine.new
216 engine = Grader::Engine.new
215 grader_general_loop(engine, grader_proc, options) do |runner|
217 grader_general_loop(engine, grader_proc, options) do |runner|
216 runner.grade_oldest_task
218 runner.grade_oldest_task
217 end
219 end
218 end
220 end
219
221
220 def grader_test_request_loop(grader_proc, options)
222 def grader_test_request_loop(grader_proc, options)
221 log "Grader: test_request"
223 log "Grader: test_request"
222 engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new,
224 engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new,
223 :reporter => Grader::TestRequestReporter.new)
225 :reporter => Grader::TestRequestReporter.new)
224 grader_general_loop(engine, grader_proc, options) do |runner|
226 grader_general_loop(engine, grader_proc, options) do |runner|
225 runner.grade_oldest_test_request
227 runner.grade_oldest_test_request
226 end
228 end
227 end
229 end
228
230
229 def grader_autonew_loop(grader_proc, options)
231 def grader_autonew_loop(grader_proc, options)
230 log "Grader: autonew"
232 log "Grader: autonew"
231
233
232 if options[:report]
234 if options[:report]
233 result_collector = ResultCollector.new
235 result_collector = ResultCollector.new
234 else
236 else
235 result_collector = nil
237 result_collector = nil
236 end
238 end
237
239
238 if options[:dry_run]
240 if options[:dry_run]
239 puts "Running in dry mode"
241 puts "Running in dry mode"
240 end
242 end
241
243
242 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
244 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
243 :result_collector => result_collector)
245 :result_collector => result_collector)
244
246
245 engine = Grader::Engine.new(:reporter => prob_reporter)
247 engine = Grader::Engine.new(:reporter => prob_reporter)
246 runner = Grader::Runner.new(engine, grader_proc)
248 runner = Grader::Runner.new(engine, grader_proc)
247
249
248 grader_proc.report_active if grader_proc!=nil
250 grader_proc.report_active if grader_proc!=nil
249
251
250 latest_submitted_at = nil
252 latest_submitted_at = nil
251 graded_submission_ids = {}
253 graded_submission_ids = {}
252
254
253 while true
255 while true
254
256
255 if check_stopfile # created by calling grader stop
257 if check_stopfile # created by calling grader stop
256 clear_stopfile
258 clear_stopfile
257 log "stopped (with stop file)"
259 log "stopped (with stop file)"
258 break
260 break
259 end
261 end
260
262
261 if latest_submitted_at==nil
263 if latest_submitted_at==nil
262 submissions = Submission.all
264 submissions = Submission.all
263 else
265 else
264 submissions = Submission.all(:conditions => ["submitted_at >= :latest",
266 submissions = Submission.all(:conditions => ["submitted_at >= :latest",
265 {:latest => latest_submitted_at}])
267 {:latest => latest_submitted_at}])
266 end
268 end
267
269
268 graded_any = false
270 graded_any = false
269
271
270 if submissions.length != 0
272 if submissions.length != 0
271 submissions.each do |submission|
273 submissions.each do |submission|
272 if (submission.problem == nil) or (!submission.problem.available)
274 if (submission.problem == nil) or (!submission.problem.available)
273 next
275 next
274 end
276 end
275 if ! graded_submission_ids[submission.id]
277 if ! graded_submission_ids[submission.id]
276 runner.grade_submission(submission)
278 runner.grade_submission(submission)
277 graded_submission_ids[submission.id] = true
279 graded_submission_ids[submission.id] = true
278 if (!latest_submitted_at or
280 if (!latest_submitted_at or
279 latest_submitted_at < submission.submitted_at)
281 latest_submitted_at < submission.submitted_at)
280 latest_submitted_at = submission.submitted_at
282 latest_submitted_at = submission.submitted_at
281 end
283 end
282 puts "graded: #{submission.id}"
284 puts "graded: #{submission.id}"
283 puts "latest: #{latest_submitted_at}"
285 puts "latest: #{latest_submitted_at}"
284 graded_any = true
286 graded_any = true
285 end
287 end
286 end
288 end
287 end
289 end
288
290
289 if ! graded_any
291 if ! graded_any
290 sleep(1)
292 sleep(1)
291 end
293 end
292 end
294 end
293 end
295 end
294
296
295 def grader_grade_problems(grader_proc, options)
297 def grader_grade_problems(grader_proc, options)
296 if options[:report]
298 if options[:report]
297 result_collector = ResultCollector.new
299 result_collector = ResultCollector.new
298 else
300 else
299 result_collector = nil
301 result_collector = nil
300 end
302 end
301
303
302 if options[:dry_run]
304 if options[:dry_run]
303 puts "Running in dry mode"
305 puts "Running in dry mode"
304 end
306 end
305
307
306 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
308 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
307 :result_collector => result_collector)
309 :result_collector => result_collector)
308 engine = Grader::Engine.new(:reporter => prob_reporter)
310 engine = Grader::Engine.new(:reporter => prob_reporter)
309 runner = Grader::Runner.new(engine, grader_proc)
311 runner = Grader::Runner.new(engine, grader_proc)
310
312
311 grader_proc.report_active if grader_proc!=nil
313 grader_proc.report_active if grader_proc!=nil
312
314
313 ARGV.each do |prob_name|
315 ARGV.each do |prob_name|
314 prob = Problem.find_by_name(prob_name)
316 prob = Problem.find_by_name(prob_name)
315 if prob==nil
317 if prob==nil
316 puts "cannot find problem: #{prob_name}"
318 puts "cannot find problem: #{prob_name}"
317 else
319 else
318 runner.grade_problem(prob,options)
320 runner.grade_problem(prob,options)
319 end
321 end
320 end
322 end
321
323
322 if options[:report]
324 if options[:report]
323 result_collector.print_report_by_user
325 result_collector.print_report_by_user
324 end
326 end
325 end
327 end
326
328
327 def grader_grade_contests(grader_proc, options)
329 def grader_grade_contests(grader_proc, options)
328 # always use dry run when grading during contest
330 # always use dry run when grading during contest
329 dry_run = options[:dry_run] = true
331 dry_run = options[:dry_run] = true
330
332
@@ -1,67 +1,71
1 #
1 #
2 # A runner drives the engine into various tasks.
2 # A runner drives the engine into various tasks.
3 #
3 #
4
4
5 module Grader
5 module Grader
6
6
7 class Runner
7 class Runner
8
8
9 def initialize(engine, grader_process=nil)
9 def initialize(engine, grader_process=nil)
10 @engine = engine
10 @engine = engine
11 @grader_process = grader_process
11 @grader_process = grader_process
12 end
12 end
13
13
14 def grade_oldest_task
14 def grade_oldest_task
15 task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING)
15 task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING)
16 if task!=nil
16 if task!=nil
17 @grader_process.report_active(task) if @grader_process!=nil
17 @grader_process.report_active(task) if @grader_process!=nil
18
18
19 submission = Submission.find(task.submission_id)
19 submission = Submission.find(task.submission_id)
20 @engine.grade(submission)
20 @engine.grade(submission)
21 task.status_complete!
21 task.status_complete!
22 @grader_process.report_inactive(task) if @grader_process!=nil
22 @grader_process.report_inactive(task) if @grader_process!=nil
23 end
23 end
24 return task
24 return task
25 end
25 end
26
26
27 def grade_problem(problem, options={})
27 def grade_problem(problem, options={})
28 + user_index = 0
29 + user_count = User.count
28 User.find_each do |u|
30 User.find_each do |u|
29 - puts "user: #{u.login}"
31 + puts "user: #{u.login} (#{user_index}/#{user_count})"
32 + user_index += 1
30 if options[:user_conditions]!=nil
33 if options[:user_conditions]!=nil
31 con_proc = options[:user_conditions]
34 con_proc = options[:user_conditions]
32 next if not con_proc.call(u)
35 next if not con_proc.call(u)
33 end
36 end
34 if options[:all_sub]
37 if options[:all_sub]
35 Submission.where(user_id: u.id,problem_id: problem.id).find_each do |sub|
38 Submission.where(user_id: u.id,problem_id: problem.id).find_each do |sub|
39 + next if options[:only_err] and sub.grader_comment != 'error during grading'
36 @engine.grade(sub)
40 @engine.grade(sub)
37 end
41 end
38 else
42 else
39 last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id)
43 last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id)
40 if last_sub!=nil
44 if last_sub!=nil
41 - @engine.grade(last_sub)
45 + @engine.grade(last_sub) unless options[:only_err] and last_sub.grader_comment != 'error during grading'
42 end
46 end
43 end
47 end
44 end
48 end
45 end
49 end
46
50
47 def grade_submission(submission)
51 def grade_submission(submission)
48 puts "Submission: #{submission.id} by #{submission.try(:user).try(:full_name)}"
52 puts "Submission: #{submission.id} by #{submission.try(:user).try(:full_name)}"
49 @engine.grade(submission)
53 @engine.grade(submission)
50 end
54 end
51
55
52 def grade_oldest_test_request
56 def grade_oldest_test_request
53 test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING)
57 test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING)
54 if test_request!=nil
58 if test_request!=nil
55 @grader_process.report_active(test_request) if @grader_process!=nil
59 @grader_process.report_active(test_request) if @grader_process!=nil
56
60
57 @engine.grade(test_request)
61 @engine.grade(test_request)
58 test_request.status_complete!
62 test_request.status_complete!
59 @grader_process.report_inactive(test_request) if @grader_process!=nil
63 @grader_process.report_inactive(test_request) if @grader_process!=nil
60 end
64 end
61 return test_request
65 return test_request
62 end
66 end
63
67
64 end
68 end
65
69
66 end
70 end
67
71
@@ -1,152 +1,154
1 module Grader
1 module Grader
2
2
3 class SubmissionRoomMaker
3 class SubmissionRoomMaker
4 def initialize
4 def initialize
5 @config = Grader::Configuration.get_instance
5 @config = Grader::Configuration.get_instance
6 end
6 end
7
7
8 def produce_grading_room(submission)
8 def produce_grading_room(submission)
9 user = submission.user
9 user = submission.user
10 problem = submission.problem
10 problem = submission.problem
11 grading_room = "#{@config.user_result_dir}/" +
11 grading_room = "#{@config.user_result_dir}/" +
12 "#{user.login}/#{problem.name}/#{submission.id}"
12 "#{user.login}/#{problem.name}/#{submission.id}"
13
13
14 FileUtils.mkdir_p(grading_room)
14 FileUtils.mkdir_p(grading_room)
15 grading_room
15 grading_room
16 end
16 end
17
17
18 def find_problem_home(submission)
18 def find_problem_home(submission)
19 problem = submission.problem
19 problem = submission.problem
20 "#{@config.problems_dir}/#{problem.name}"
20 "#{@config.problems_dir}/#{problem.name}"
21 end
21 end
22
22
23 def save_source(submission,source_name)
23 def save_source(submission,source_name)
24 dir = self.produce_grading_room(submission)
24 dir = self.produce_grading_room(submission)
25 f = File.open("#{dir}/#{source_name}","w")
25 f = File.open("#{dir}/#{source_name}","w")
26 f.write(submission.source)
26 f.write(submission.source)
27 f.close
27 f.close
28 end
28 end
29
29
30 def clean_up(submission)
30 def clean_up(submission)
31 end
31 end
32 end
32 end
33
33
34 class SubmissionReporter
34 class SubmissionReporter
35 def initialize(options={})
35 def initialize(options={})
36 options = {:dry_run => false, :result_collector => nil}.merge(options)
36 options = {:dry_run => false, :result_collector => nil}.merge(options)
37 @config = Grader::Configuration.get_instance
37 @config = Grader::Configuration.get_instance
38 @dry_run = options[:dry_run]
38 @dry_run = options[:dry_run]
39 @result_collector = options[:result_collector]
39 @result_collector = options[:result_collector]
40 end
40 end
41
41
42 def report(sub,test_result_dir)
42 def report(sub,test_result_dir)
43 result = read_result(test_result_dir)
43 result = read_result(test_result_dir)
44 if @result_collector
44 if @result_collector
45 @result_collector.save(sub,
45 @result_collector.save(sub,
46 result)
46 result)
47 end
47 end
48 save_result(sub,result)
48 save_result(sub,result)
49 end
49 end
50
50
51 def report_error(sub,msg)
51 def report_error(sub,msg)
52 save_result(sub,{:points => 0,
52 save_result(sub,{:points => 0,
53 :comment => "Grading error: #{msg}" })
53 :comment => "Grading error: #{msg}" })
54 end
54 end
55
55
56 protected
56 protected
57 def read_result(test_result_dir)
57 def read_result(test_result_dir)
58 cmp_msg_fname = "#{test_result_dir}/compiler_message"
58 cmp_msg_fname = "#{test_result_dir}/compiler_message"
59 if FileTest.exist?(cmp_msg_fname)
59 if FileTest.exist?(cmp_msg_fname)
60 cmp_file = File.open(cmp_msg_fname)
60 cmp_file = File.open(cmp_msg_fname)
61 cmp_msg = cmp_file.read
61 cmp_msg = cmp_file.read
62 cmp_file.close
62 cmp_file.close
63 else
63 else
64 cmp_msg = ""
64 cmp_msg = ""
65 end
65 end
66
66
67 result_fname = "#{test_result_dir}/result"
67 result_fname = "#{test_result_dir}/result"
68 comment_fname = "#{test_result_dir}/comment"
68 comment_fname = "#{test_result_dir}/comment"
69 runstat_fname = "#{test_result_dir}/run_stat"
69 runstat_fname = "#{test_result_dir}/run_stat"
70 if FileTest.exist?(result_fname)
70 if FileTest.exist?(result_fname)
71 comment = ""
71 comment = ""
72 begin
72 begin
73 result_file = File.open(result_fname)
73 result_file = File.open(result_fname)
74 result = result_file.readline.to_i
74 result = result_file.readline.to_i
75 result_file.close
75 result_file.close
76 rescue
76 rescue
77 result = 0
77 result = 0
78 comment = "error reading result file."
78 comment = "error reading result file."
79 end
79 end
80
80
81 begin
81 begin
82 comment_file = File.open(comment_fname)
82 comment_file = File.open(comment_fname)
83 comment += comment_file.readline.chomp
83 comment += comment_file.readline.chomp
84 comment_file.close
84 comment_file.close
85 rescue
85 rescue
86 comment += ""
86 comment += ""
87 end
87 end
88
88
89 begin
89 begin
90 runstat_file = File.open(runstat_fname)
90 runstat_file = File.open(runstat_fname)
91 max_runtime = runstat_file.readline.to_f
91 max_runtime = runstat_file.readline.to_f
92 peak_memory = runstat_file.readline.to_i
92 peak_memory = runstat_file.readline.to_i
93 rescue
93 rescue
94 max_runtime = -1
94 max_runtime = -1
95 peak_memory = -1
95 peak_memory = -1
96 end
96 end
97
97
98
98
99 return {points: result,
99 return {points: result,
100 comment: comment,
100 comment: comment,
101 cmp_msg: cmp_msg,
101 cmp_msg: cmp_msg,
102 max_runtime: max_runtime,
102 max_runtime: max_runtime,
103 peak_memory: peak_memory
103 peak_memory: peak_memory
104 }
104 }
105 else
105 else
106 if FileTest.exist?("#{test_result_dir}/a.out")
106 if FileTest.exist?("#{test_result_dir}/a.out")
107 return {:points => 0,
107 return {:points => 0,
108 :comment => 'error during grading',
108 :comment => 'error during grading',
109 :cmp_msg => cmp_msg}
109 :cmp_msg => cmp_msg}
110 else
110 else
111 return {:points => 0,
111 return {:points => 0,
112 :comment => 'compilation error',
112 :comment => 'compilation error',
113 :cmp_msg => cmp_msg}
113 :cmp_msg => cmp_msg}
114 end
114 end
115 end
115 end
116 end
116 end
117
117
118 def save_result(submission,result)
118 def save_result(submission,result)
119 problem = submission.problem
119 problem = submission.problem
120 submission.graded_at = Time.now.gmtime
120 submission.graded_at = Time.now.gmtime
121 points = result[:points]
121 points = result[:points]
122 submission.points = points
122 submission.points = points
123 comment = @config.report_comment(result[:comment])
123 comment = @config.report_comment(result[:comment])
124
124
125 submission.peak_memory = result[:peak_memory]
125 submission.peak_memory = result[:peak_memory]
126 submission.max_runtime = result[:max_runtime]
126 submission.max_runtime = result[:max_runtime]
127 submission.effective_code_length =submission.source.length
127 submission.effective_code_length =submission.source.length
128
128
129 #
129 #
130 # TODO: FIX THIS MESSAGE
130 # TODO: FIX THIS MESSAGE
131 #
131 #
132 if problem == nil
132 if problem == nil
133 submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)'
133 submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)'
134 elsif points == problem.full_score
134 elsif points == problem.full_score
135 #submission.grader_comment = 'PASSED: ' + comment
135 #submission.grader_comment = 'PASSED: ' + comment
136 submission.grader_comment = comment
136 submission.grader_comment = comment
137 elsif result[:comment].chomp =~ /^[\[\]P]+$/
137 elsif result[:comment].chomp =~ /^[\[\]P]+$/
138 submission.grader_comment = 'PASSED: ' + comment + '(inconsistent score)'
138 submission.grader_comment = 'PASSED: ' + comment + '(inconsistent score)'
139 else
139 else
140 #submission.grader_comment = 'FAILED: ' + comment
140 #submission.grader_comment = 'FAILED: ' + comment
141 submission.grader_comment = comment
141 submission.grader_comment = comment
142 end
142 end
143 - submission.compiler_message = result[:cmp_msg] or ''
143 +
144 + #very lazy trim the string
145 + submission.compiler_message = result[:cmp_msg][0..60000] or ''
144
146
145 if not @dry_run
147 if not @dry_run
146 submission.save
148 submission.save
147 end
149 end
148 end
150 end
149
151
150 end
152 end
151
153
152 end
154 end
@@ -1,192 +1,192
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2
3 require 'fileutils'
3 require 'fileutils'
4
4
5 def log(str='')
5 def log(str='')
6 if ENV['TALKATIVE']!=nil
6 if ENV['TALKATIVE']!=nil
7 puts str
7 puts str
8 end
8 end
9 if ENV['GRADER_LOGGING']!=nil
9 if ENV['GRADER_LOGGING']!=nil
10 log_fname = ENV['GRADER_LOGGING']
10 log_fname = ENV['GRADER_LOGGING']
11 fp = File.open(log_fname,"a")
11 fp = File.open(log_fname,"a")
12 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
12 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
13 fp.close
13 fp.close
14 end
14 end
15 end
15 end
16
16
17 def extract_time(t)
17 def extract_time(t)
18 # puts "TIME: #{t}"
18 # puts "TIME: #{t}"
19 if (result=/^(.*)r(.*)u(.*)s/.match(t))
19 if (result=/^(.*)r(.*)u(.*)s/.match(t))
20 {:real => result[1], :user => result[2], :sys => result[3]}
20 {:real => result[1], :user => result[2], :sys => result[3]}
21 else
21 else
22 #{:real => 0, :user => 0, :sys => 0}
22 #{:real => 0, :user => 0, :sys => 0}
23 #puts "ERROR READING RUNNING TIME: #{t}"
23 #puts "ERROR READING RUNNING TIME: #{t}"
24 raise "Error reading running time: #{t}"
24 raise "Error reading running time: #{t}"
25 end
25 end
26 end
26 end
27
27
28 def compile_box(source,bin)
28 def compile_box(source,bin)
29 system("g++ #{source} -o #{bin}")
29 system("g++ #{source} -o #{bin}")
30 end
30 end
31
31
32 if ARGV.length < 2 || ARGV.length > 3
32 if ARGV.length < 2 || ARGV.length > 3
33 puts "Usage: run <language> <test-num> [<program-name>]"
33 puts "Usage: run <language> <test-num> [<program-name>]"
34 exit(127)
34 exit(127)
35 end
35 end
36
36
37 language = ARGV[0]
37 language = ARGV[0]
38 test_num = ARGV[1].to_i
38 test_num = ARGV[1].to_i
39 if ARGV.length > 2
39 if ARGV.length > 2
40 program_name = ARGV[2]
40 program_name = ARGV[2]
41 else
41 else
42 program_name = "a.out"
42 program_name = "a.out"
43 end
43 end
44
44
45 problem_home = ENV['PROBLEM_HOME']
45 problem_home = ENV['PROBLEM_HOME']
46 source_name = ENV['SOURCE_NAME']
46 source_name = ENV['SOURCE_NAME']
47 require "#{problem_home}/script/test_dsl.rb"
47 require "#{problem_home}/script/test_dsl.rb"
48 load "#{problem_home}/test_cases/all_tests.cfg"
48 load "#{problem_home}/test_cases/all_tests.cfg"
49 problem = Problem.get_instance
49 problem = Problem.get_instance
50
50
51 sandbox_dir = Dir.getwd
51 sandbox_dir = Dir.getwd
52
52
53 if problem.well_formed? == false
53 if problem.well_formed? == false
54 log "The problem specification is not well formed."
54 log "The problem specification is not well formed."
55 exit(127)
55 exit(127)
56 end
56 end
57
57
58 # Check if the test number is okay.
58 # Check if the test number is okay.
59 if test_num <= 0 || test_num > problem.num_tests
59 if test_num <= 0 || test_num > problem.num_tests
60 log "You have specified a wrong test number."
60 log "You have specified a wrong test number."
61 exit(127)
61 exit(127)
62 end
62 end
63
63
64 #####################################
64 #####################################
65 # Set the relavant file names here. #
65 # Set the relavant file names here. #
66 #####################################
66 #####################################
67
67
68 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
68 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
69
69
70 #####################################
70 #####################################
71
71
72 time_limit = problem.get_time_limit test_num
72 time_limit = problem.get_time_limit test_num
73 mem_limit = problem.get_mem_limit(test_num) * 1024
73 mem_limit = problem.get_mem_limit(test_num) * 1024
74
74
75 # Copy the input file.
75 # Copy the input file.
76 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
76 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
77
77
78 # check if box is there, if not, compile it!
78 # check if box is there, if not, compile it!
79 if !File.exists?("#{problem_home}/script/box")
79 if !File.exists?("#{problem_home}/script/box")
80 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
80 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
81 compile_box("#{problem_home}/script/box.cc",
81 compile_box("#{problem_home}/script/box.cc",
82 "#{problem_home}/script/box")
82 "#{problem_home}/script/box")
83 end
83 end
84
84
85 # Hide PROBLEM_HOME
85 # Hide PROBLEM_HOME
86 ENV['PROBLEM_HOME'] = nil
86 ENV['PROBLEM_HOME'] = nil
87 ENV['SOURCE_NAME'] = nil
87 ENV['SOURCE_NAME'] = nil
88
88
89 # Run the program.
89 # Run the program.
90 #run_command = "/usr/bin/time -f \"#{time_output_format}\" 2>run_result #{problem_home}/script/box_new -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}"
90 #run_command = "/usr/bin/time -f \"#{time_output_format}\" 2>run_result #{problem_home}/script/box_new -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}"
91 #
91 #
92
92
93 JAVA_OPTION = "-s set_robust_list -s futex -s clone -s getppid -s clone -s wait4 -p /usr/bin/ -p ./"
93 JAVA_OPTION = "-s set_robust_list -s futex -s clone -s getppid -s clone -s wait4 -p /usr/bin/ -p ./"
94 - RUBY_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /lib64/ -p /dev/urandom -p #{sandbox_dir}/#{program_name} -p #{sandbox_dir}/ -s set_robust_list -s sched_getaffinity -s clock_gettime -s sigaltstack -s pipe2 -s clone -s futex -s openat -s pipe"
94 + RUBY_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /lib64/ -p /dev/urandom -p #{sandbox_dir}/#{program_name} -p #{sandbox_dir}/ -s set_robust_list -s sched_getaffinity -s clock_gettime -s sigaltstack -s pipe2 -s clone -s futex -s openat -s pipe -s getrandom"
95 PYTHON_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /usr/bin/ -p /lib64/ -p /dev/urandom -p /usr/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p #{sandbox_dir}/#{source_name} -p /proc/sys/crypto/fips_enabled -p /proc/self/status -p /proc/mounts -p /var/lib/dpkg/status -s statfs -s set_robust_list -s openat -s sysinfo -s recvmsg -s connect -s socket -s sendto -s futex -s sigaltstack -s getrandom -E PYTHONNOUSERSITE=yes"
95 PYTHON_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /usr/bin/ -p /lib64/ -p /dev/urandom -p /usr/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p #{sandbox_dir}/#{source_name} -p /proc/sys/crypto/fips_enabled -p /proc/self/status -p /proc/mounts -p /var/lib/dpkg/status -s statfs -s set_robust_list -s openat -s sysinfo -s recvmsg -s connect -s socket -s sendto -s futex -s sigaltstack -s getrandom -E PYTHONNOUSERSITE=yes"
96 PHP_OPTION = "-p /usr/lib64/ -p/lib64/ -p /usr/bin/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p /usr/share/ -s setfsuid -s setfsgid -s openat -s set_robust_list -s futex -s clone -s socket -s connect"
96 PHP_OPTION = "-p /usr/lib64/ -p/lib64/ -p /usr/bin/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p /usr/share/ -s setfsuid -s setfsgid -s openat -s set_robust_list -s futex -s clone -s socket -s connect"
97 HASKELL_OPTION = "-s set_robust_list -s clock_gettime -s sysinfo -s timer_create -s timer_settime -s futex -s timer_delete"
97 HASKELL_OPTION = "-s set_robust_list -s clock_gettime -s sysinfo -s timer_create -s timer_settime -s futex -s timer_delete"
98
98
99 case language
99 case language
100 when "java"
100 when "java"
101 # for java, extract the classname
101 # for java, extract the classname
102 # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
102 # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
103 classname = 'DUMMY'
103 classname = 'DUMMY'
104 File.open(program_name,"r").each do |line|
104 File.open(program_name,"r").each do |line|
105 classname = line
105 classname = line
106 end
106 end
107 #for java, we cannot really check the memory limit...
107 #for java, we cannot really check the memory limit...
108 run_command = "#{problem_home}/script/box -a 3 -f -T -t #{time_limit} #{JAVA_OPTION} -i #{input_file_name} -o output.txt /usr/bin/java -A -Xmx#{mem_limit}k -A #{classname} "
108 run_command = "#{problem_home}/script/box -a 3 -f -T -t #{time_limit} #{JAVA_OPTION} -i #{input_file_name} -o output.txt /usr/bin/java -A -Xmx#{mem_limit}k -A #{classname} "
109 when "ruby"
109 when "ruby"
110 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{mem_limit} #{RUBY_OPTION} -i #{input_file_name} -o output.txt /usr/bin/ruby #{program_name} "
110 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{mem_limit} #{RUBY_OPTION} -i #{input_file_name} -o output.txt /usr/bin/ruby #{program_name} "
111 when "python"
111 when "python"
112 - run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[128 * 1024,mem_limit].max} #{PYTHON_OPTION} -i #{input_file_name} -o output.txt /usr/bin/python3 #{program_name} "
112 + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[512 * 1024,mem_limit].max} #{PYTHON_OPTION} -i #{input_file_name} -o output.txt /usr/bin/python3 #{program_name} "
113 when "haskell"
113 when "haskell"
114 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{[512 * 1024,mem_limit].max} #{HASKELL_OPTION} -i #{input_file_name} -o output.txt #{program_name} "
114 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{[512 * 1024,mem_limit].max} #{HASKELL_OPTION} -i #{input_file_name} -o output.txt #{program_name} "
115 when "php"
115 when "php"
116 - run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[128 * 1024,mem_limit].max} #{PHP_OPTION} -i #{input_file_name} -o output.txt /usr/bin/php -A -d -A memory_limit=#{mem_limit}k -A #{program_name} "
116 + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[512 * 1024,mem_limit].max} #{PHP_OPTION} -i #{input_file_name} -o output.txt /usr/bin/php -A -d -A memory_limit=#{mem_limit}k -A #{program_name} "
117 else # for c++, pascal, we do the normal checking
117 else # for c++, pascal, we do the normal checking
118 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} "
118 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} "
119 end
119 end
120
120
121
121
122 log "Running test #{test_num}..."
122 log "Running test #{test_num}..."
123 log run_command
123 log run_command
124 log
124 log
125 system(run_command,err: 'run_result')
125 system(run_command,err: 'run_result')
126
126
127 # Restore PROBLEM_HOME
127 # Restore PROBLEM_HOME
128 ENV['PROBLEM_HOME'] = problem_home
128 ENV['PROBLEM_HOME'] = problem_home
129
129
130 # Create the result file.
130 # Create the result file.
131 result_file = File.new("result", "w")
131 result_file = File.new("result", "w")
132 comment_file = File.new("comment", "w")
132 comment_file = File.new("comment", "w")
133
133
134 # Check if the program actually produced any output.
134 # Check if the program actually produced any output.
135 run_result_file = File.new("run_result", "r")
135 run_result_file = File.new("run_result", "r")
136 run_result = run_result_file.readlines
136 run_result = run_result_file.readlines
137 run_result_file.close
137 run_result_file.close
138
138
139 run_stat = run_result[run_result.length-1]
139 run_stat = run_result[run_result.length-1]
140 running_time = extract_time(run_stat)
140 running_time = extract_time(run_stat)
141
141
142 report = lambda{ |status, points, comment|
142 report = lambda{ |status, points, comment|
143 result_file.write status.strip
143 result_file.write status.strip
144 result_file.write "\n"
144 result_file.write "\n"
145 result_file.write points.to_s.strip
145 result_file.write points.to_s.strip
146 result_file.write "\n"
146 result_file.write "\n"
147 result_file.write run_stat.strip
147 result_file.write run_stat.strip
148 result_file.write "\n"
148 result_file.write "\n"
149 result_file.close
149 result_file.close
150 FileUtils.rm "run_result"
150 FileUtils.rm "run_result"
151 # `rm output.txt` --- keep the output
151 # `rm output.txt` --- keep the output
152
152
153 comment_file.write comment
153 comment_file.write comment
154
154
155 # added for debuggin --- jittat
155 # added for debuggin --- jittat
156 comment_file.write "--run-result--\n"
156 comment_file.write "--run-result--\n"
157 run_result.each do |l|
157 run_result.each do |l|
158 comment_file.write l
158 comment_file.write l
159 end
159 end
160
160
161 comment_file.close
161 comment_file.close
162
162
163 log "Done!"
163 log "Done!"
164 exit(0)
164 exit(0)
165 }
165 }
166
166
167
167
168 if run_result[0][0,2] != "OK"
168 if run_result[0][0,2] != "OK"
169 log "There was a runtime error."
169 log "There was a runtime error."
170 report.call(run_result[0], 0, "No comment.\n")
170 report.call(run_result[0], 0, "No comment.\n")
171 end
171 end
172
172
173 if running_time[:user].to_f > time_limit
173 if running_time[:user].to_f > time_limit
174 log "Time limit exceeded."
174 log "Time limit exceeded."
175 report.call("Time limit exceeded", 0, "No comment.\n")
175 report.call("Time limit exceeded", 0, "No comment.\n")
176 end
176 end
177
177
178 # Run 'check' to evaluate the output.
178 # Run 'check' to evaluate the output.
179 #puts "There was no runtime error. Proceed to checking the output."
179 #puts "There was no runtime error. Proceed to checking the output."
180 check_command = "#{problem_home}/script/check #{language} #{test_num}"
180 check_command = "#{problem_home}/script/check #{language} #{test_num}"
181 log "Checking the output..."
181 log "Checking the output..."
182 log check_command
182 log check_command
183 if not system(check_command)
183 if not system(check_command)
184 log "Problem with check script"
184 log "Problem with check script"
185 report.call("Incorrect",0,"Check script error.\n")
185 report.call("Incorrect",0,"Check script error.\n")
186 exit(127)
186 exit(127)
187 end
187 end
188
188
189 check_file = File.new("check_result", "r")
189 check_file = File.new("check_result", "r")
190 check_file_lines = check_file.readlines
190 check_file_lines = check_file.readlines
191
191
192 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
192 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
You need to be logged in to leave comments. Login now