Description:
handle the case when problem id or submission id is null. Grader will simply skip such request. Add more report on console (for command line grading)
(mercurial grafted from d233105d3965c5368c9b33125f390e39b25f910e)
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
r181:5c6f0b732e70 - - 2 files changed: 30 inserted, 30 deleted
@@ -1,443 +1,443 | |||||
|
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] |
|
49 | (2) grader environment [mode] |
|
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 | (3) create stop-file to stop running grader in queue mode |
|
54 | (3) create stop-file to stop running grader in queue mode |
|
55 | (4) You are here. |
|
55 | (4) You are here. |
|
56 | USAGE |
|
56 | USAGE |
|
57 | end |
|
57 | end |
|
58 |
|
58 | ||
|
59 | def process_options_and_stop_file |
|
59 | def process_options_and_stop_file |
|
60 | # The list of options are: |
|
60 | # The list of options are: |
|
61 | # - stop [all|process ids] |
|
61 | # - stop [all|process ids] |
|
62 | # - |
|
62 | # - |
|
63 |
|
63 | ||
|
64 | # Process 'help' option |
|
64 | # Process 'help' option |
|
65 | if (ARGV.length==1) and (/help/.match(ARGV[0])) |
|
65 | if (ARGV.length==1) and (/help/.match(ARGV[0])) |
|
66 | display_manual |
|
66 | display_manual |
|
67 | exit(0) |
|
67 | exit(0) |
|
68 | end |
|
68 | end |
|
69 |
|
69 | ||
|
70 | # Process 'stop' option. |
|
70 | # Process 'stop' option. |
|
71 | if (ARGV.length >= 1) and (ARGV[0]=='stop') |
|
71 | if (ARGV.length >= 1) and (ARGV[0]=='stop') |
|
72 | if ARGV.length==1 |
|
72 | if ARGV.length==1 |
|
73 | puts "you should specify pid-list or 'all'" |
|
73 | puts "you should specify pid-list or 'all'" |
|
74 | display_manual |
|
74 | display_manual |
|
75 | elsif (ARGV.length==2) and (ARGV[1]=='all') |
|
75 | elsif (ARGV.length==2) and (ARGV[1]=='all') |
|
76 | stop_grader(:all) |
|
76 | stop_grader(:all) |
|
77 | puts "A global stop file ('stop.all') created." |
|
77 | puts "A global stop file ('stop.all') created." |
|
78 | puts "You should remove it manually later." |
|
78 | puts "You should remove it manually later." |
|
79 | else |
|
79 | else |
|
80 | (1..ARGV.length-1).each do |i| |
|
80 | (1..ARGV.length-1).each do |i| |
|
81 | stop_grader(ARGV[i]) |
|
81 | stop_grader(ARGV[i]) |
|
82 | end |
|
82 | end |
|
83 | puts "stop file(s) created" |
|
83 | puts "stop file(s) created" |
|
84 | end |
|
84 | end |
|
85 | exit(0) |
|
85 | exit(0) |
|
86 | end |
|
86 | end |
|
87 |
|
87 | ||
|
88 | # Check stop file. |
|
88 | # Check stop file. |
|
89 | if check_stopfile |
|
89 | if check_stopfile |
|
90 | puts "Stop file exists. Terminated." |
|
90 | puts "Stop file exists. Terminated." |
|
91 | clear_stopfile |
|
91 | clear_stopfile |
|
92 | exit(0) |
|
92 | exit(0) |
|
93 | end |
|
93 | end |
|
94 |
|
94 | ||
|
95 | #default options |
|
95 | #default options |
|
96 | options = { |
|
96 | options = { |
|
97 | :mode => 'queue', |
|
97 | :mode => 'queue', |
|
98 | :environment => 'exam', |
|
98 | :environment => 'exam', |
|
99 | :dry_run => false, |
|
99 | :dry_run => false, |
|
100 | } |
|
100 | } |
|
101 |
|
101 | ||
|
102 | # Process mode and environment option |
|
102 | # Process mode and environment option |
|
103 | if ARGV.length >= 1 |
|
103 | if ARGV.length >= 1 |
|
104 | options[:environment] = ARGV.shift |
|
104 | options[:environment] = ARGV.shift |
|
105 | if ARGV.length >=1 |
|
105 | if ARGV.length >=1 |
|
106 | options[:mode] = ARGV.shift |
|
106 | options[:mode] = ARGV.shift |
|
107 | end |
|
107 | end |
|
108 | end |
|
108 | end |
|
109 |
|
109 | ||
|
110 | options[:dry_run] = (ARGV.delete('--dry') != nil) |
|
110 | options[:dry_run] = (ARGV.delete('--dry') != nil) |
|
111 | if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode]) |
|
111 | if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode]) |
|
112 | puts "Dry run currently works only for 'prob' or 'contest' modes." |
|
112 | puts "Dry run currently works only for 'prob' or 'contest' modes." |
|
113 | exit(0) |
|
113 | exit(0) |
|
114 | end |
|
114 | end |
|
115 |
|
115 | ||
|
116 | options[:report] = (ARGV.delete('--report') != nil) |
|
116 | options[:report] = (ARGV.delete('--report') != nil) |
|
117 | if options[:report] and (not ['prob','contest','autonew'].include? options[:mode]) |
|
117 | if options[:report] and (not ['prob','contest','autonew'].include? options[:mode]) |
|
118 | puts "Report currently works only for 'prob' or 'contest' modes." |
|
118 | puts "Report currently works only for 'prob' or 'contest' modes." |
|
119 | exit(0) |
|
119 | exit(0) |
|
120 | end |
|
120 | end |
|
121 |
|
121 | ||
|
122 | return options |
|
122 | return options |
|
123 | end |
|
123 | end |
|
124 |
|
124 | ||
|
125 | class ResultCollector |
|
125 | class ResultCollector |
|
126 | def initialize |
|
126 | def initialize |
|
127 | @results = {} |
|
127 | @results = {} |
|
128 | @problems = {} |
|
128 | @problems = {} |
|
129 | @users = {} |
|
129 | @users = {} |
|
130 | end |
|
130 | end |
|
131 |
|
131 | ||
|
132 | def after_save_hook(submission, grading_result) |
|
132 | def after_save_hook(submission, grading_result) |
|
133 | end |
|
133 | end |
|
134 |
|
134 | ||
|
135 | def save(submission, grading_result) |
|
135 | def save(submission, grading_result) |
|
136 | user = submission.user |
|
136 | user = submission.user |
|
137 | problem = submission.problem |
|
137 | problem = submission.problem |
|
138 | if not @problems.has_key? problem.id |
|
138 | if not @problems.has_key? problem.id |
|
139 | @problems[problem.id] = problem |
|
139 | @problems[problem.id] = problem |
|
140 | end |
|
140 | end |
|
141 | if not @users.has_key? user.id |
|
141 | if not @users.has_key? user.id |
|
142 | @users[user.id] = user |
|
142 | @users[user.id] = user |
|
143 | end |
|
143 | end |
|
144 | @results[[user.id, problem.id]] = grading_result |
|
144 | @results[[user.id, problem.id]] = grading_result |
|
145 |
|
145 | ||
|
146 | after_save_hook(submission, grading_result) |
|
146 | after_save_hook(submission, grading_result) |
|
147 | end |
|
147 | end |
|
148 |
|
148 | ||
|
149 | def print_report_by_user |
|
149 | def print_report_by_user |
|
150 | puts "---------------------" |
|
150 | puts "---------------------" |
|
151 | puts " REPORT" |
|
151 | puts " REPORT" |
|
152 | puts "---------------------" |
|
152 | puts "---------------------" |
|
153 |
|
153 | ||
|
154 | print "login,email" |
|
154 | print "login,email" |
|
155 | @problems.each_value do |problem| |
|
155 | @problems.each_value do |problem| |
|
156 | print ",#{problem.name}" |
|
156 | print ",#{problem.name}" |
|
157 | end |
|
157 | end |
|
158 | print "\n" |
|
158 | print "\n" |
|
159 |
|
159 | ||
|
160 | @users.each_value do |user| |
|
160 | @users.each_value do |user| |
|
161 | print "#{user.login},#{user.email}" |
|
161 | print "#{user.login},#{user.email}" |
|
162 | @problems.each_value do |problem| |
|
162 | @problems.each_value do |problem| |
|
163 | if @results.has_key? [user.id, problem.id] |
|
163 | if @results.has_key? [user.id, problem.id] |
|
164 | print ",#{@results[[user.id,problem.id]][:points]}" |
|
164 | print ",#{@results[[user.id,problem.id]][:points]}" |
|
165 | else |
|
165 | else |
|
166 | print "," |
|
166 | print "," |
|
167 | end |
|
167 | end |
|
168 | end |
|
168 | end |
|
169 | print "\n" |
|
169 | print "\n" |
|
170 | end |
|
170 | end |
|
171 | end |
|
171 | end |
|
172 | end |
|
172 | end |
|
173 |
|
173 | ||
|
174 | def grader_general_loop(engine, grader_proc, options) |
|
174 | def grader_general_loop(engine, grader_proc, options) |
|
175 | runner = Grader::Runner.new(engine, grader_proc) |
|
175 | runner = Grader::Runner.new(engine, grader_proc) |
|
176 | while true |
|
176 | while true |
|
177 |
|
177 | ||
|
178 | if check_stopfile # created by calling grader stop |
|
178 | if check_stopfile # created by calling grader stop |
|
179 | clear_stopfile |
|
179 | clear_stopfile |
|
180 | log "stopped (with stop file)" |
|
180 | log "stopped (with stop file)" |
|
181 | break |
|
181 | break |
|
182 | end |
|
182 | end |
|
183 |
|
183 | ||
|
184 | task = yield(runner) |
|
184 | task = yield(runner) |
|
185 |
|
185 | ||
|
186 | if task==nil |
|
186 | if task==nil |
|
187 | sleep(1) |
|
187 | sleep(1) |
|
188 | end |
|
188 | end |
|
189 | end |
|
189 | end |
|
190 | end |
|
190 | end |
|
191 |
|
191 | ||
|
192 | def grader_queue_loop(grader_proc, options) |
|
192 | def grader_queue_loop(grader_proc, options) |
|
193 | log "Grader: queue" |
|
193 | log "Grader: queue" |
|
194 | engine = Grader::Engine.new |
|
194 | engine = Grader::Engine.new |
|
195 | grader_general_loop(engine, grader_proc, options) do |runner| |
|
195 | grader_general_loop(engine, grader_proc, options) do |runner| |
|
196 | runner.grade_oldest_task |
|
196 | runner.grade_oldest_task |
|
197 | end |
|
197 | end |
|
198 | end |
|
198 | end |
|
199 |
|
199 | ||
|
200 | def grader_test_request_loop(grader_proc, options) |
|
200 | def grader_test_request_loop(grader_proc, options) |
|
201 | log "Grader: test_request" |
|
201 | log "Grader: test_request" |
|
202 | engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new, |
|
202 | engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new, |
|
203 | :reporter => Grader::TestRequestReporter.new) |
|
203 | :reporter => Grader::TestRequestReporter.new) |
|
204 | grader_general_loop(engine, grader_proc, options) do |runner| |
|
204 | grader_general_loop(engine, grader_proc, options) do |runner| |
|
205 | runner.grade_oldest_test_request |
|
205 | runner.grade_oldest_test_request |
|
206 | end |
|
206 | end |
|
207 | end |
|
207 | end |
|
208 |
|
208 | ||
|
209 | def grader_autonew_loop(grader_proc, options) |
|
209 | def grader_autonew_loop(grader_proc, options) |
|
210 | log "Grader: autonew" |
|
210 | log "Grader: autonew" |
|
211 |
|
211 | ||
|
212 | if options[:report] |
|
212 | if options[:report] |
|
213 | result_collector = ResultCollector.new |
|
213 | result_collector = ResultCollector.new |
|
214 | else |
|
214 | else |
|
215 | result_collector = nil |
|
215 | result_collector = nil |
|
216 | end |
|
216 | end |
|
217 |
|
217 | ||
|
218 | if options[:dry_run] |
|
218 | if options[:dry_run] |
|
219 | puts "Running in dry mode" |
|
219 | puts "Running in dry mode" |
|
220 | end |
|
220 | end |
|
221 |
|
221 | ||
|
222 | prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run], |
|
222 | prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run], |
|
223 | :result_collector => result_collector) |
|
223 | :result_collector => result_collector) |
|
224 |
|
224 | ||
|
225 | engine = Grader::Engine.new(:reporter => prob_reporter) |
|
225 | engine = Grader::Engine.new(:reporter => prob_reporter) |
|
226 | runner = Grader::Runner.new(engine, grader_proc) |
|
226 | runner = Grader::Runner.new(engine, grader_proc) |
|
227 |
|
227 | ||
|
228 | grader_proc.report_active if grader_proc!=nil |
|
228 | grader_proc.report_active if grader_proc!=nil |
|
229 |
|
229 | ||
|
230 | latest_submitted_at = nil |
|
230 | latest_submitted_at = nil |
|
231 | graded_submission_ids = {} |
|
231 | graded_submission_ids = {} |
|
232 |
|
232 | ||
|
233 | while true |
|
233 | while true |
|
234 |
|
234 | ||
|
235 | if check_stopfile # created by calling grader stop |
|
235 | if check_stopfile # created by calling grader stop |
|
236 | clear_stopfile |
|
236 | clear_stopfile |
|
237 | log "stopped (with stop file)" |
|
237 | log "stopped (with stop file)" |
|
238 | break |
|
238 | break |
|
239 | end |
|
239 | end |
|
240 |
|
240 | ||
|
241 | if latest_submitted_at==nil |
|
241 | if latest_submitted_at==nil |
|
242 | submissions = Submission.all |
|
242 | submissions = Submission.all |
|
243 | else |
|
243 | else |
|
244 | submissions = Submission.all(:conditions => ["submitted_at >= :latest", |
|
244 | submissions = Submission.all(:conditions => ["submitted_at >= :latest", |
|
245 | {:latest => latest_submitted_at}]) |
|
245 | {:latest => latest_submitted_at}]) |
|
246 | end |
|
246 | end |
|
247 |
|
247 | ||
|
248 | graded_any = false |
|
248 | graded_any = false |
|
249 |
|
249 | ||
|
250 | if submissions.length != 0 |
|
250 | if submissions.length != 0 |
|
251 | submissions.each do |submission| |
|
251 | submissions.each do |submission| |
|
252 | if (submission.problem == nil) or (!submission.problem.available) |
|
252 | if (submission.problem == nil) or (!submission.problem.available) |
|
253 | next |
|
253 | next |
|
254 | end |
|
254 | end |
|
255 | if ! graded_submission_ids[submission.id] |
|
255 | if ! graded_submission_ids[submission.id] |
|
256 | runner.grade_submission(submission) |
|
256 | runner.grade_submission(submission) |
|
257 | graded_submission_ids[submission.id] = true |
|
257 | graded_submission_ids[submission.id] = true |
|
258 | if (!latest_submitted_at or |
|
258 | if (!latest_submitted_at or |
|
259 | latest_submitted_at < submission.submitted_at) |
|
259 | latest_submitted_at < submission.submitted_at) |
|
260 | latest_submitted_at = submission.submitted_at |
|
260 | latest_submitted_at = submission.submitted_at |
|
261 | end |
|
261 | end |
|
262 | puts "graded: #{submission.id}" |
|
262 | puts "graded: #{submission.id}" |
|
263 | puts "latest: #{latest_submitted_at}" |
|
263 | puts "latest: #{latest_submitted_at}" |
|
264 | graded_any = true |
|
264 | graded_any = true |
|
265 | end |
|
265 | end |
|
266 | end |
|
266 | end |
|
267 | end |
|
267 | end |
|
268 |
|
268 | ||
|
269 | if ! graded_any |
|
269 | if ! graded_any |
|
270 | sleep(1) |
|
270 | sleep(1) |
|
271 | end |
|
271 | end |
|
272 | end |
|
272 | end |
|
273 | end |
|
273 | end |
|
274 |
|
274 | ||
|
275 | def grader_grade_problems(grader_proc, options) |
|
275 | def grader_grade_problems(grader_proc, options) |
|
276 | if options[:report] |
|
276 | if options[:report] |
|
277 | result_collector = ResultCollector.new |
|
277 | result_collector = ResultCollector.new |
|
278 | else |
|
278 | else |
|
279 | result_collector = nil |
|
279 | result_collector = nil |
|
280 | end |
|
280 | end |
|
281 |
|
281 | ||
|
282 | if options[:dry_run] |
|
282 | if options[:dry_run] |
|
283 | puts "Running in dry mode" |
|
283 | puts "Running in dry mode" |
|
284 | end |
|
284 | end |
|
285 |
|
285 | ||
|
286 | prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run], |
|
286 | prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run], |
|
287 | :result_collector => result_collector) |
|
287 | :result_collector => result_collector) |
|
288 | engine = Grader::Engine.new(:reporter => prob_reporter) |
|
288 | engine = Grader::Engine.new(:reporter => prob_reporter) |
|
289 | runner = Grader::Runner.new(engine, grader_proc) |
|
289 | runner = Grader::Runner.new(engine, grader_proc) |
|
290 |
|
290 | ||
|
291 | grader_proc.report_active if grader_proc!=nil |
|
291 | grader_proc.report_active if grader_proc!=nil |
|
292 |
|
292 | ||
|
293 | ARGV.each do |prob_name| |
|
293 | ARGV.each do |prob_name| |
|
294 | prob = Problem.find_by_name(prob_name) |
|
294 | prob = Problem.find_by_name(prob_name) |
|
295 | if prob==nil |
|
295 | if prob==nil |
|
296 | puts "cannot find problem: #{prob_name}" |
|
296 | puts "cannot find problem: #{prob_name}" |
|
297 | else |
|
297 | else |
|
298 | runner.grade_problem(prob) |
|
298 | runner.grade_problem(prob) |
|
299 | end |
|
299 | end |
|
300 | end |
|
300 | end |
|
301 |
|
301 | ||
|
302 | if options[:report] |
|
302 | if options[:report] |
|
303 | result_collector.print_report_by_user |
|
303 | result_collector.print_report_by_user |
|
304 | end |
|
304 | end |
|
305 | end |
|
305 | end |
|
306 |
|
306 | ||
|
307 | def grader_grade_contests(grader_proc, options) |
|
307 | def grader_grade_contests(grader_proc, options) |
|
308 | # always use dry run when grading during contest |
|
308 | # always use dry run when grading during contest |
|
309 | dry_run = options[:dry_run] = true |
|
309 | dry_run = options[:dry_run] = true |
|
310 |
|
310 | ||
|
311 | contest_name = ARGV.shift |
|
311 | contest_name = ARGV.shift |
|
312 |
|
312 | ||
|
313 | contest = Contest.find_by_name(contest_name) |
|
313 | contest = Contest.find_by_name(contest_name) |
|
314 | if contest==nil |
|
314 | if contest==nil |
|
315 | puts "cannot find contest: #{contest_name}" |
|
315 | puts "cannot find contest: #{contest_name}" |
|
316 | exit(0) |
|
316 | exit(0) |
|
317 | end |
|
317 | end |
|
318 |
|
318 | ||
|
319 | if options[:report] |
|
319 | if options[:report] |
|
320 | result_collector = ResultCollector.new |
|
320 | result_collector = ResultCollector.new |
|
321 | else |
|
321 | else |
|
322 | result_collector = nil |
|
322 | result_collector = nil |
|
323 | end |
|
323 | end |
|
324 |
|
324 | ||
|
325 | if options[:dry_run] |
|
325 | if options[:dry_run] |
|
326 | puts "Running in dry mode" |
|
326 | puts "Running in dry mode" |
|
327 | end |
|
327 | end |
|
328 |
|
328 | ||
|
329 | prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run, |
|
329 | prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run, |
|
330 | :result_collector => result_collector) |
|
330 | :result_collector => result_collector) |
|
331 | engine = Grader::Engine.new(:reporter => prob_reporter) |
|
331 | engine = Grader::Engine.new(:reporter => prob_reporter) |
|
332 | runner = Grader::Runner.new(engine, grader_proc) |
|
332 | runner = Grader::Runner.new(engine, grader_proc) |
|
333 |
|
333 | ||
|
334 | grader_proc.report_active if grader_proc!=nil |
|
334 | grader_proc.report_active if grader_proc!=nil |
|
335 |
|
335 | ||
|
336 | contest.problems.each do |problem| |
|
336 | contest.problems.each do |problem| |
|
337 | puts "Grading: #{problem.name}" |
|
337 | puts "Grading: #{problem.name}" |
|
338 | runner.grade_problem(problem, |
|
338 | runner.grade_problem(problem, |
|
339 | :user_conditions => lambda do |u| |
|
339 | :user_conditions => lambda do |u| |
|
340 | u.contest_finished? and |
|
340 | u.contest_finished? and |
|
341 | u.contest_ids.include?(contest.id) |
|
341 | u.contest_ids.include?(contest.id) |
|
342 | end) |
|
342 | end) |
|
343 | end |
|
343 | end |
|
344 |
|
344 | ||
|
345 | if options[:report] |
|
345 | if options[:report] |
|
346 | result_collector.print_report_by_user |
|
346 | result_collector.print_report_by_user |
|
347 | end |
|
347 | end |
|
348 | end |
|
348 | end |
|
349 |
|
349 | ||
|
350 | def grader_grade_submissions(grader_proc, options) |
|
350 | def grader_grade_submissions(grader_proc, options) |
|
351 | engine = Grader::Engine.new |
|
351 | engine = Grader::Engine.new |
|
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 | ARGV.each do |sub_id| |
|
356 | ARGV.each do |sub_id| |
|
357 | puts "Grading #{sub_id}" |
|
357 | puts "Grading #{sub_id}" |
|
358 | begin |
|
358 | begin |
|
359 | submission = Submission.find(sub_id.to_i) |
|
359 | submission = Submission.find(sub_id.to_i) |
|
360 | rescue ActiveRecord::RecordNotFound |
|
360 | rescue ActiveRecord::RecordNotFound |
|
361 |
- puts " |
|
361 | + puts "Submission #{sub_id} not found" |
|
362 | submission = nil |
|
362 | submission = nil |
|
363 | end |
|
363 | end |
|
364 |
|
364 | ||
|
365 | if submission!=nil |
|
365 | if submission!=nil |
|
366 | runner.grade_submission(submission) |
|
366 | runner.grade_submission(submission) |
|
367 | end |
|
367 | end |
|
368 | end |
|
368 | end |
|
369 | end |
|
369 | end |
|
370 |
|
370 | ||
|
371 | ######################################### |
|
371 | ######################################### |
|
372 | # main program |
|
372 | # main program |
|
373 | ######################################### |
|
373 | ######################################### |
|
374 |
|
374 | ||
|
375 | options = process_options_and_stop_file |
|
375 | options = process_options_and_stop_file |
|
376 | GRADER_ENV = options[:environment] |
|
376 | GRADER_ENV = options[:environment] |
|
377 | grader_mode = options[:mode] |
|
377 | grader_mode = options[:mode] |
|
378 | dry_run = options[:dry_run] |
|
378 | dry_run = options[:dry_run] |
|
379 |
|
379 | ||
|
380 | puts "environment: #{GRADER_ENV}" |
|
380 | puts "environment: #{GRADER_ENV}" |
|
381 | require File.join(File.dirname(__FILE__),'config/environment') |
|
381 | require File.join(File.dirname(__FILE__),'config/environment') |
|
382 |
|
382 | ||
|
383 | # add grader_mode to config |
|
383 | # add grader_mode to config |
|
384 | # this is needed because method log needs it. TODO: clean this up |
|
384 | # this is needed because method log needs it. TODO: clean this up |
|
385 | class << config |
|
385 | class << config |
|
386 | attr_accessor :grader_mode |
|
386 | attr_accessor :grader_mode |
|
387 | end |
|
387 | end |
|
388 | config.grader_mode = grader_mode |
|
388 | config.grader_mode = grader_mode |
|
389 |
|
389 | ||
|
390 | # reading rails environment |
|
390 | # reading rails environment |
|
391 | log 'Reading rails environment' |
|
391 | log 'Reading rails environment' |
|
392 |
|
392 | ||
|
393 | RAILS_ENV = config.rails_env |
|
393 | RAILS_ENV = config.rails_env |
|
394 | require RAILS_ROOT + '/config/environment' |
|
394 | require RAILS_ROOT + '/config/environment' |
|
395 |
|
395 | ||
|
396 | # register grader process |
|
396 | # register grader process |
|
397 | if config.report_grader |
|
397 | if config.report_grader |
|
398 | grader_proc = GraderProcess.register(config.grader_hostname, |
|
398 | grader_proc = GraderProcess.register(config.grader_hostname, |
|
399 | Process.pid, |
|
399 | Process.pid, |
|
400 | grader_mode) |
|
400 | grader_mode) |
|
401 | else |
|
401 | else |
|
402 | grader_proc = nil |
|
402 | grader_proc = nil |
|
403 | end |
|
403 | end |
|
404 |
|
404 | ||
|
405 | #set loggin environment |
|
405 | #set loggin environment |
|
406 | ENV['GRADER_LOGGING'] = log_file_name |
|
406 | ENV['GRADER_LOGGING'] = log_file_name |
|
407 |
|
407 | ||
|
408 | # register exit handler to report inactive, and terminated |
|
408 | # register exit handler to report inactive, and terminated |
|
409 | at_exit do |
|
409 | at_exit do |
|
410 | if grader_proc!=nil |
|
410 | if grader_proc!=nil |
|
411 | grader_proc.report_inactive |
|
411 | grader_proc.report_inactive |
|
412 | grader_proc.terminate |
|
412 | grader_proc.terminate |
|
413 | end |
|
413 | end |
|
414 | end |
|
414 | end |
|
415 |
|
415 | ||
|
416 | # |
|
416 | # |
|
417 | # MAIN LOOP |
|
417 | # MAIN LOOP |
|
418 | # |
|
418 | # |
|
419 |
|
419 | ||
|
420 | case grader_mode |
|
420 | case grader_mode |
|
421 | when "queue" |
|
421 | when "queue" |
|
422 | grader_queue_loop(grader_proc, options) |
|
422 | grader_queue_loop(grader_proc, options) |
|
423 |
|
423 | ||
|
424 | when "test_request" |
|
424 | when "test_request" |
|
425 | grader_test_request_loop(grader_proc, options) |
|
425 | grader_test_request_loop(grader_proc, options) |
|
426 |
|
426 | ||
|
427 | when "prob" |
|
427 | when "prob" |
|
428 | grader_grade_problems(grader_proc, options) |
|
428 | grader_grade_problems(grader_proc, options) |
|
429 |
|
429 | ||
|
430 | when "contest" |
|
430 | when "contest" |
|
431 | grader_grade_contests(grader_proc, options) |
|
431 | grader_grade_contests(grader_proc, options) |
|
432 |
|
432 | ||
|
433 | when "sub" |
|
433 | when "sub" |
|
434 | grader_grade_submissions(grader_proc, options) |
|
434 | grader_grade_submissions(grader_proc, options) |
|
435 |
|
435 | ||
|
436 | when "autonew" |
|
436 | when "autonew" |
|
437 | grader_autonew_loop(grader_proc, options) |
|
437 | grader_autonew_loop(grader_proc, options) |
|
438 |
|
438 | ||
|
439 | else |
|
439 | else |
|
440 | display_manual |
|
440 | display_manual |
|
441 | exit(0) |
|
441 | exit(0) |
|
442 | end |
|
442 | end |
|
443 |
|
443 |
@@ -1,190 +1,190 | |||||
|
1 | require 'fileutils' |
|
1 | require 'fileutils' |
|
2 | require File.join(File.dirname(__FILE__),'dir_init') |
|
2 | require File.join(File.dirname(__FILE__),'dir_init') |
|
3 |
|
3 | ||
|
4 | module Grader |
|
4 | module Grader |
|
5 |
|
5 | ||
|
6 | # |
|
6 | # |
|
7 | # A grader engine grades a submission, against anything: a test |
|
7 | # A grader engine grades a submission, against anything: a test |
|
8 | # data, or a user submitted test data. It uses two helpers objects: |
|
8 | # data, or a user submitted test data. It uses two helpers objects: |
|
9 | # room_maker and reporter. |
|
9 | # room_maker and reporter. |
|
10 | # |
|
10 | # |
|
11 | class Engine |
|
11 | class Engine |
|
12 |
|
12 | ||
|
13 | attr_writer :room_maker |
|
13 | attr_writer :room_maker |
|
14 | attr_writer :reporter |
|
14 | attr_writer :reporter |
|
15 |
|
15 | ||
|
16 | def initialize(options={}) |
|
16 | def initialize(options={}) |
|
17 | # default options |
|
17 | # default options |
|
18 | if not options.include? :room_maker |
|
18 | if not options.include? :room_maker |
|
19 | options[:room_maker] = Grader::SubmissionRoomMaker.new |
|
19 | options[:room_maker] = Grader::SubmissionRoomMaker.new |
|
20 | end |
|
20 | end |
|
21 | if not options.include? :reporter |
|
21 | if not options.include? :reporter |
|
22 | options[:reporter] = Grader::SubmissionReporter.new |
|
22 | options[:reporter] = Grader::SubmissionReporter.new |
|
23 | end |
|
23 | end |
|
24 |
|
24 | ||
|
25 | @config = Grader::Configuration.get_instance |
|
25 | @config = Grader::Configuration.get_instance |
|
26 |
|
26 | ||
|
27 | @room_maker = options[:room_maker] |
|
27 | @room_maker = options[:room_maker] |
|
28 | @reporter = options[:reporter] |
|
28 | @reporter = options[:reporter] |
|
29 | end |
|
29 | end |
|
30 |
|
30 | ||
|
31 | # takes a submission, asks room_maker to produce grading directories, |
|
31 | # takes a submission, asks room_maker to produce grading directories, |
|
32 | # calls grader scripts, and asks reporter to save the result |
|
32 | # calls grader scripts, and asks reporter to save the result |
|
33 | def grade(submission) |
|
33 | def grade(submission) |
|
34 | current_dir = FileUtils.pwd |
|
34 | current_dir = FileUtils.pwd |
|
35 |
|
35 | ||
|
36 | user = submission.user |
|
36 | user = submission.user |
|
37 | problem = submission.problem |
|
37 | problem = submission.problem |
|
38 |
|
38 | ||
|
39 | - # TODO: will have to create real exception for this |
|
39 | + begin |
|
40 | - if user==nil or problem == nil |
|
40 | + # TODO: will have to create real exception for this |
|
41 | - @reporter.report_error(submission,"Grading error: problem with submission") |
|
41 | + if user==nil or problem == nil |
|
42 | - #raise "engine: user or problem is nil" |
|
42 | + @reporter.report_error(submission,"Grading error: problem with submission") |
|
43 | - end |
|
43 | + raise "engine: user or problem is nil" |
|
44 | - |
|
44 | + end |
|
45 | - # TODO: this is another hack so that output only task can be judged |
|
||
|
46 | - if submission.language!=nil |
|
||
|
47 | - language = submission.language.name |
|
||
|
48 | - lang_ext = submission.language.ext |
|
||
|
49 | - else |
|
||
|
50 | - language = 'c' |
|
||
|
51 | - lang_ext = 'c' |
|
||
|
52 | - end |
|
||
|
53 |
|
45 | ||
|
54 | - # This is needed because older version of std-scripts/compile |
|
46 | + # TODO: this is another hack so that output only task can be judged |
|
55 | - # only look for c++. |
|
47 | + if submission.language!=nil |
|
56 | - if language == 'cpp' |
|
48 | + language = submission.language.name |
|
57 | - language = 'c++' |
|
49 | + lang_ext = submission.language.ext |
|
58 |
- |
|
50 | + else |
|
|
51 | + language = 'c' | ||
|
|
52 | + lang_ext = 'c' | ||
|
|
53 | + end | ||
|
59 |
|
54 | ||
|
60 | - # COMMENT: should it be only source.ext? |
|
55 | + # This is needed because older version of std-scripts/compile |
|
61 | - if problem!=nil |
|
56 | + # only look for c++. |
|
62 | - source_name = "#{problem.name}.#{lang_ext}" |
|
57 | + if language == 'cpp' |
|
63 | - else |
|
58 | + language = 'c++' |
|
64 | - source_name = "source.#{lang_ext}" |
|
59 | + end |
|
65 | - end |
|
||
|
66 |
|
60 | ||
|
67 | - begin |
|
61 | + # COMMENT: should it be only source.ext? |
|
|
62 | + if problem!=nil | ||
|
|
63 | + source_name = "#{problem.name}.#{lang_ext}" | ||
|
|
64 | + else | ||
|
|
65 | + source_name = "source.#{lang_ext}" | ||
|
|
66 | + end | ||
|
|
67 | + | ||
|
68 | grading_dir = @room_maker.produce_grading_room(submission) |
|
68 | grading_dir = @room_maker.produce_grading_room(submission) |
|
69 | @room_maker.save_source(submission,source_name) |
|
69 | @room_maker.save_source(submission,source_name) |
|
70 | problem_home = @room_maker.find_problem_home(submission) |
|
70 | problem_home = @room_maker.find_problem_home(submission) |
|
71 |
|
71 | ||
|
72 | # puts "GRADING DIR: #{grading_dir}" |
|
72 | # puts "GRADING DIR: #{grading_dir}" |
|
73 | # puts "PROBLEM DIR: #{problem_home}" |
|
73 | # puts "PROBLEM DIR: #{problem_home}" |
|
74 |
|
74 | ||
|
75 | if !FileTest.exist?(problem_home) |
|
75 | if !FileTest.exist?(problem_home) |
|
76 | puts "PROBLEM DIR: #{problem_home}" |
|
76 | puts "PROBLEM DIR: #{problem_home}" |
|
77 |
- |
|
77 | + raise "engine: No test data." |
|
78 | - raise "No test data." |
|
||
|
79 | end |
|
78 | end |
|
80 |
|
79 | ||
|
81 | dinit = DirInit::Manager.new(problem_home) |
|
80 | dinit = DirInit::Manager.new(problem_home) |
|
82 |
|
81 | ||
|
83 | dinit.setup do |
|
82 | dinit.setup do |
|
84 | copy_log = copy_script(problem_home) |
|
83 | copy_log = copy_script(problem_home) |
|
85 | save_copy_log(problem_home,copy_log) |
|
84 | save_copy_log(problem_home,copy_log) |
|
86 | end |
|
85 | end |
|
87 |
|
86 | ||
|
88 | call_judge(problem_home,language,grading_dir,source_name) |
|
87 | call_judge(problem_home,language,grading_dir,source_name) |
|
89 |
|
88 | ||
|
90 | @reporter.report(submission,"#{grading_dir}/test-result") |
|
89 | @reporter.report(submission,"#{grading_dir}/test-result") |
|
91 |
|
90 | ||
|
92 | dinit.teardown do |
|
91 | dinit.teardown do |
|
93 | copy_log = load_copy_log(problem_home) |
|
92 | copy_log = load_copy_log(problem_home) |
|
94 | clear_copy_log(problem_home) |
|
93 | clear_copy_log(problem_home) |
|
95 | clear_script(copy_log,problem_home) |
|
94 | clear_script(copy_log,problem_home) |
|
96 | end |
|
95 | end |
|
97 |
|
96 | ||
|
98 | rescue RuntimeError => msg |
|
97 | rescue RuntimeError => msg |
|
99 | @reporter.report_error(submission, msg) |
|
98 | @reporter.report_error(submission, msg) |
|
|
99 | + puts "ERROR: #{msg}" | ||
|
100 |
|
100 | ||
|
101 | ensure |
|
101 | ensure |
|
102 | @room_maker.clean_up(submission) |
|
102 | @room_maker.clean_up(submission) |
|
103 | Dir.chdir(current_dir) # this is really important |
|
103 | Dir.chdir(current_dir) # this is really important |
|
104 | end |
|
104 | end |
|
105 | end |
|
105 | end |
|
106 |
|
106 | ||
|
107 | protected |
|
107 | protected |
|
108 |
|
108 | ||
|
109 | def talk(str) |
|
109 | def talk(str) |
|
110 | if @config.talkative |
|
110 | if @config.talkative |
|
111 | puts str |
|
111 | puts str |
|
112 | end |
|
112 | end |
|
113 | end |
|
113 | end |
|
114 |
|
114 | ||
|
115 | def call_judge(problem_home,language,grading_dir,fname) |
|
115 | def call_judge(problem_home,language,grading_dir,fname) |
|
116 | ENV['PROBLEM_HOME'] = problem_home |
|
116 | ENV['PROBLEM_HOME'] = problem_home |
|
117 | ENV['RUBYOPT'] = '' |
|
117 | ENV['RUBYOPT'] = '' |
|
118 |
|
118 | ||
|
119 | talk grading_dir |
|
119 | talk grading_dir |
|
120 | Dir.chdir grading_dir |
|
120 | Dir.chdir grading_dir |
|
121 | cmd = "#{problem_home}/script/judge #{language} #{fname}" |
|
121 | cmd = "#{problem_home}/script/judge #{language} #{fname}" |
|
122 | talk "CMD: #{cmd}" |
|
122 | talk "CMD: #{cmd}" |
|
123 | system(cmd) |
|
123 | system(cmd) |
|
124 | end |
|
124 | end |
|
125 |
|
125 | ||
|
126 | def get_std_script_dir |
|
126 | def get_std_script_dir |
|
127 | GRADER_ROOT + '/std-script' |
|
127 | GRADER_ROOT + '/std-script' |
|
128 | end |
|
128 | end |
|
129 |
|
129 | ||
|
130 | def copy_script(problem_home) |
|
130 | def copy_script(problem_home) |
|
131 | script_dir = "#{problem_home}/script" |
|
131 | script_dir = "#{problem_home}/script" |
|
132 | std_script_dir = get_std_script_dir |
|
132 | std_script_dir = get_std_script_dir |
|
133 |
|
133 | ||
|
134 | - raise "std-script directory not found" if !FileTest.exist?(std_script_dir) |
|
134 | + raise "engine: std-script directory not found" if !FileTest.exist?(std_script_dir) |
|
135 |
|
135 | ||
|
136 | scripts = Dir[std_script_dir + '/*'] |
|
136 | scripts = Dir[std_script_dir + '/*'] |
|
137 |
|
137 | ||
|
138 | copied = [] |
|
138 | copied = [] |
|
139 |
|
139 | ||
|
140 | scripts.each do |s| |
|
140 | scripts.each do |s| |
|
141 | fname = File.basename(s) |
|
141 | fname = File.basename(s) |
|
142 | next if FileTest.directory?(s) |
|
142 | next if FileTest.directory?(s) |
|
143 | if !FileTest.exist?("#{script_dir}/#{fname}") |
|
143 | if !FileTest.exist?("#{script_dir}/#{fname}") |
|
144 | copied << fname |
|
144 | copied << fname |
|
145 | FileUtils.cp(s, "#{script_dir}", :preserve => true) |
|
145 | FileUtils.cp(s, "#{script_dir}", :preserve => true) |
|
146 | end |
|
146 | end |
|
147 | end |
|
147 | end |
|
148 |
|
148 | ||
|
149 | return copied |
|
149 | return copied |
|
150 | end |
|
150 | end |
|
151 |
|
151 | ||
|
152 | def copy_log_filename(problem_home) |
|
152 | def copy_log_filename(problem_home) |
|
153 | return File.join(problem_home, '.scripts_copied') |
|
153 | return File.join(problem_home, '.scripts_copied') |
|
154 | end |
|
154 | end |
|
155 |
|
155 | ||
|
156 | def save_copy_log(problem_home, log) |
|
156 | def save_copy_log(problem_home, log) |
|
157 | f = File.new(copy_log_filename(problem_home),"w") |
|
157 | f = File.new(copy_log_filename(problem_home),"w") |
|
158 | log.each do |fname| |
|
158 | log.each do |fname| |
|
159 | f.write("#{fname}\n") |
|
159 | f.write("#{fname}\n") |
|
160 | end |
|
160 | end |
|
161 | f.close |
|
161 | f.close |
|
162 | end |
|
162 | end |
|
163 |
|
163 | ||
|
164 | def load_copy_log(problem_home) |
|
164 | def load_copy_log(problem_home) |
|
165 | f = File.new(copy_log_filename(problem_home),"r") |
|
165 | f = File.new(copy_log_filename(problem_home),"r") |
|
166 | log = [] |
|
166 | log = [] |
|
167 | f.readlines.each do |line| |
|
167 | f.readlines.each do |line| |
|
168 | log << line.strip |
|
168 | log << line.strip |
|
169 | end |
|
169 | end |
|
170 | f.close |
|
170 | f.close |
|
171 | log |
|
171 | log |
|
172 | end |
|
172 | end |
|
173 |
|
173 | ||
|
174 | def clear_copy_log(problem_home) |
|
174 | def clear_copy_log(problem_home) |
|
175 | File.delete(copy_log_filename(problem_home)) |
|
175 | File.delete(copy_log_filename(problem_home)) |
|
176 | end |
|
176 | end |
|
177 |
|
177 | ||
|
178 | def clear_script(log,problem_home) |
|
178 | def clear_script(log,problem_home) |
|
179 | log.each do |s| |
|
179 | log.each do |s| |
|
180 | FileUtils.rm("#{problem_home}/script/#{s}") |
|
180 | FileUtils.rm("#{problem_home}/script/#{s}") |
|
181 | end |
|
181 | end |
|
182 | end |
|
182 | end |
|
183 |
|
183 | ||
|
184 | def mkdir_if_does_not_exist(dirname) |
|
184 | def mkdir_if_does_not_exist(dirname) |
|
185 | Dir.mkdir(dirname) if !FileTest.exist?(dirname) |
|
185 | Dir.mkdir(dirname) if !FileTest.exist?(dirname) |
|
186 | end |
|
186 | end |
|
187 |
|
187 | ||
|
188 | end |
|
188 | end |
|
189 |
|
189 | ||
|
190 | end |
|
190 | end |
You need to be logged in to leave comments.
Login now