Description:
added test assignment time out
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r222:2b6293a64dbb - - 8 files changed: 42 inserted, 2 deleted

@@ -14,384 +14,389
14
14
15 # NOTE: This method is not actually needed, 'config/routes.rb' has
15 # NOTE: This method is not actually needed, 'config/routes.rb' has
16 # assigned action login as a default action.
16 # assigned action login as a default action.
17 def index
17 def index
18 redirect_to :action => 'login'
18 redirect_to :action => 'login'
19 end
19 end
20
20
21 def login
21 def login
22 saved_notice = flash[:notice]
22 saved_notice = flash[:notice]
23 reset_session
23 reset_session
24 flash[:notice] = saved_notice
24 flash[:notice] = saved_notice
25
25
26 # EXPERIMENT:
26 # EXPERIMENT:
27 # Hide login if in single user mode and the url does not
27 # Hide login if in single user mode and the url does not
28 # explicitly specify /login
28 # explicitly specify /login
29 #
29 #
30 # logger.info "PATH: #{request.path}"
30 # logger.info "PATH: #{request.path}"
31 # if Configuration['system.single_user_mode'] and
31 # if Configuration['system.single_user_mode'] and
32 # request.path!='/main/login'
32 # request.path!='/main/login'
33 # @hidelogin = true
33 # @hidelogin = true
34 # end
34 # end
35
35
36 @announcements = Announcement.find_for_frontpage
36 @announcements = Announcement.find_for_frontpage
37 render :action => 'login', :layout => 'empty'
37 render :action => 'login', :layout => 'empty'
38 end
38 end
39
39
40 def list
40 def list
41 prepare_list_information
41 prepare_list_information
42 end
42 end
43
43
44 def help
44 def help
45 @user = User.find(session[:user_id])
45 @user = User.find(session[:user_id])
46 end
46 end
47
47
48 def submit
48 def submit
49 user = User.find(session[:user_id])
49 user = User.find(session[:user_id])
50
50
51 @submission = Submission.new(params[:submission])
51 @submission = Submission.new(params[:submission])
52 @submission.user = user
52 @submission.user = user
53 @submission.language_id = 0
53 @submission.language_id = 0
54 if (params['file']) and (params['file']!='')
54 if (params['file']) and (params['file']!='')
55 @submission.source = params['file'].read
55 @submission.source = params['file'].read
56 @submission.source_filename = params['file'].original_filename
56 @submission.source_filename = params['file'].original_filename
57 end
57 end
58 @submission.submitted_at = Time.new.gmtime
58 @submission.submitted_at = Time.new.gmtime
59
59
60 if Configuration.time_limit_mode? and user.contest_finished?
60 if Configuration.time_limit_mode? and user.contest_finished?
61 @submission.errors.add_to_base "The contest is over."
61 @submission.errors.add_to_base "The contest is over."
62 prepare_list_information
62 prepare_list_information
63 render :action => 'list' and return
63 render :action => 'list' and return
64 end
64 end
65
65
66 if @submission.valid?
66 if @submission.valid?
67 if @submission.save == false
67 if @submission.save == false
68 flash[:notice] = 'Error saving your submission'
68 flash[:notice] = 'Error saving your submission'
69 elsif Task.create(:submission_id => @submission.id,
69 elsif Task.create(:submission_id => @submission.id,
70 :status => Task::STATUS_INQUEUE) == false
70 :status => Task::STATUS_INQUEUE) == false
71 flash[:notice] = 'Error adding your submission to task queue'
71 flash[:notice] = 'Error adding your submission to task queue'
72 end
72 end
73 else
73 else
74 prepare_list_information
74 prepare_list_information
75 render :action => 'list' and return
75 render :action => 'list' and return
76 end
76 end
77 redirect_to :action => 'list'
77 redirect_to :action => 'list'
78 end
78 end
79
79
80 def source
80 def source
81 submission = Submission.find(params[:id])
81 submission = Submission.find(params[:id])
82 if submission.user_id == session[:user_id]
82 if submission.user_id == session[:user_id]
83 send_data(submission.source,
83 send_data(submission.source,
84 {:filename => submission.download_filename,
84 {:filename => submission.download_filename,
85 :type => 'text/plain'})
85 :type => 'text/plain'})
86 else
86 else
87 flash[:notice] = 'Error viewing source'
87 flash[:notice] = 'Error viewing source'
88 redirect_to :action => 'list'
88 redirect_to :action => 'list'
89 end
89 end
90 end
90 end
91
91
92 def compiler_msg
92 def compiler_msg
93 @submission = Submission.find(params[:id])
93 @submission = Submission.find(params[:id])
94 if @submission.user_id == session[:user_id]
94 if @submission.user_id == session[:user_id]
95 render :action => 'compiler_msg', :layout => 'empty'
95 render :action => 'compiler_msg', :layout => 'empty'
96 else
96 else
97 flash[:notice] = 'Error viewing source'
97 flash[:notice] = 'Error viewing source'
98 redirect_to :action => 'list'
98 redirect_to :action => 'list'
99 end
99 end
100 end
100 end
101
101
102 def submission
102 def submission
103 @user = User.find(session[:user_id])
103 @user = User.find(session[:user_id])
104 @problems = Problem.find_available_problems
104 @problems = Problem.find_available_problems
105 if params[:id]==nil
105 if params[:id]==nil
106 @problem = nil
106 @problem = nil
107 @submissions = nil
107 @submissions = nil
108 else
108 else
109 @problem = Problem.find_by_name(params[:id])
109 @problem = Problem.find_by_name(params[:id])
110 if not @problem.available
110 if not @problem.available
111 redirect_to :action => 'list'
111 redirect_to :action => 'list'
112 flash[:notice] = 'Error: submissions for that problem are not viewable.'
112 flash[:notice] = 'Error: submissions for that problem are not viewable.'
113 return
113 return
114 end
114 end
115 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
115 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
116 end
116 end
117 end
117 end
118
118
119 def result
119 def result
120 if !Configuration.show_grading_result
120 if !Configuration.show_grading_result
121 redirect_to :action => 'list' and return
121 redirect_to :action => 'list' and return
122 end
122 end
123 @user = User.find(session[:user_id])
123 @user = User.find(session[:user_id])
124 @submission = Submission.find(params[:id])
124 @submission = Submission.find(params[:id])
125 if @submission.user!=@user
125 if @submission.user!=@user
126 flash[:notice] = 'You are not allowed to view result of other users.'
126 flash[:notice] = 'You are not allowed to view result of other users.'
127 redirect_to :action => 'list' and return
127 redirect_to :action => 'list' and return
128 end
128 end
129 prepare_grading_result(@submission)
129 prepare_grading_result(@submission)
130 end
130 end
131
131
132 def load_output
132 def load_output
133 if !Configuration.show_grading_result or params[:num]==nil
133 if !Configuration.show_grading_result or params[:num]==nil
134 redirect_to :action => 'list' and return
134 redirect_to :action => 'list' and return
135 end
135 end
136 @user = User.find(session[:user_id])
136 @user = User.find(session[:user_id])
137 @submission = Submission.find(params[:id])
137 @submission = Submission.find(params[:id])
138 if @submission.user!=@user
138 if @submission.user!=@user
139 flash[:notice] = 'You are not allowed to view result of other users.'
139 flash[:notice] = 'You are not allowed to view result of other users.'
140 redirect_to :action => 'list' and return
140 redirect_to :action => 'list' and return
141 end
141 end
142 case_num = params[:num].to_i
142 case_num = params[:num].to_i
143 out_filename = output_filename(@user.login,
143 out_filename = output_filename(@user.login,
144 @submission.problem.name,
144 @submission.problem.name,
145 @submission.id,
145 @submission.id,
146 case_num)
146 case_num)
147 if !FileTest.exists?(out_filename)
147 if !FileTest.exists?(out_filename)
148 flash[:notice] = 'Output not found.'
148 flash[:notice] = 'Output not found.'
149 redirect_to :action => 'list' and return
149 redirect_to :action => 'list' and return
150 end
150 end
151
151
152 response.headers['Content-Type'] = "application/force-download"
152 response.headers['Content-Type'] = "application/force-download"
153 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
153 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
154 response.headers["X-Sendfile"] = out_filename
154 response.headers["X-Sendfile"] = out_filename
155 response.headers['Content-length'] = File.size(out_filename)
155 response.headers['Content-length'] = File.size(out_filename)
156 render :nothing => true
156 render :nothing => true
157 end
157 end
158
158
159 def error
159 def error
160 @user = User.find(session[:user_id])
160 @user = User.find(session[:user_id])
161 end
161 end
162
162
163 # announcement refreshing and hiding methods
163 # announcement refreshing and hiding methods
164
164
165 def announcements
165 def announcements
166 if params.has_key? 'recent'
166 if params.has_key? 'recent'
167 prepare_announcements(params[:recent])
167 prepare_announcements(params[:recent])
168 else
168 else
169 prepare_announcements
169 prepare_announcements
170 end
170 end
171 render(:partial => 'announcement',
171 render(:partial => 'announcement',
172 :collection => @announcements,
172 :collection => @announcements,
173 :locals => {:announcement_effect => true})
173 :locals => {:announcement_effect => true})
174 end
174 end
175
175
176 #
176 #
177 # actions for Code Jom
177 # actions for Code Jom
178 #
178 #
179 def download_input
179 def download_input
180 problem = Problem.find(params[:id])
180 problem = Problem.find(params[:id])
181 user = User.find(session[:user_id])
181 user = User.find(session[:user_id])
182 if user.can_request_new_test_pair_for? problem
182 if user.can_request_new_test_pair_for? problem
183 assignment = user.get_new_test_pair_assignment_for problem
183 assignment = user.get_new_test_pair_assignment_for problem
184 assignment.save
184 assignment.save
185
185
186 send_data(assignment.test_pair.input,
186 send_data(assignment.test_pair.input,
187 { :filename => "#{problem.name}-#{assignment.request_number}.in",
187 { :filename => "#{problem.name}-#{assignment.request_number}.in",
188 :type => 'text/plain' })
188 :type => 'text/plain' })
189 else
189 else
190 recent_assignment = user.get_recent_test_pair_assignment_for problem
190 recent_assignment = user.get_recent_test_pair_assignment_for problem
191 send_data(recent_assignment.test_pair.input,
191 send_data(recent_assignment.test_pair.input,
192 { :filename => "#{problem.name}-#{recent_assignment.request_number}.in",
192 { :filename => "#{problem.name}-#{recent_assignment.request_number}.in",
193 :type => 'text/plain' })
193 :type => 'text/plain' })
194 end
194 end
195 end
195 end
196
196
197 def submit_solution
197 def submit_solution
198 problem = Problem.find(params[:id])
198 problem = Problem.find(params[:id])
199 user = User.find(session[:user_id])
199 user = User.find(session[:user_id])
200 recent_assignment = user.get_recent_test_pair_assignment_for problem
200 recent_assignment = user.get_recent_test_pair_assignment_for problem
201 if recent_assignment == nil
201 if recent_assignment == nil
202 flash[:notice] = 'You have not requested for any input data for this problem. Please download an input first.'
202 flash[:notice] = 'You have not requested for any input data for this problem. Please download an input first.'
203 redirect_to :action => 'list' and return
203 redirect_to :action => 'list' and return
204 end
204 end
205
205
206 + if recent_assignment.expired?
207 + flash[:notice] = 'The current input is expired. Please download a new input data.'
208 + redirect_to :action => 'list' and return
209 + end
210 +
206 if recent_assignment.submitted
211 if recent_assignment.submitted
207 flash[:notice] = 'You have already submitted an incorrect solution for this input. Please download a new input data.'
212 flash[:notice] = 'You have already submitted an incorrect solution for this input. Please download a new input data.'
208 redirect_to :action => 'list' and return
213 redirect_to :action => 'list' and return
209 end
214 end
210
215
211 if params[:file] == nil
216 if params[:file] == nil
212 flash[:notice] = 'You have not submitted any output.'
217 flash[:notice] = 'You have not submitted any output.'
213 redirect_to :action => 'list' and return
218 redirect_to :action => 'list' and return
214 end
219 end
215
220
216 submitted_solution = params[:file].read
221 submitted_solution = params[:file].read
217 test_pair = recent_assignment.test_pair
222 test_pair = recent_assignment.test_pair
218 passed = test_pair.grade(submitted_solution)
223 passed = test_pair.grade(submitted_solution)
219 points = passed ? 100 : 0
224 points = passed ? 100 : 0
220 submission = Submission.new(:user => user,
225 submission = Submission.new(:user => user,
221 :problem => problem,
226 :problem => problem,
222 :source => submitted_solution,
227 :source => submitted_solution,
223 :source_filename => params['file'].original_filename,
228 :source_filename => params['file'].original_filename,
224 :language_id => 0,
229 :language_id => 0,
225 :submitted_at => Time.new.gmtime,
230 :submitted_at => Time.new.gmtime,
226 :graded_at => Time.new.gmtime,
231 :graded_at => Time.new.gmtime,
227 :points => points)
232 :points => points)
228 submission.save
233 submission.save
229 recent_assignment.submitted = true
234 recent_assignment.submitted = true
230 recent_assignment.save
235 recent_assignment.save
231
236
232 status = user.get_submission_status_for(problem)
237 status = user.get_submission_status_for(problem)
233 if status == nil
238 if status == nil
234 status = SubmissionStatus.new :user => user, :problem => problem, :submission_count => 0
239 status = SubmissionStatus.new :user => user, :problem => problem, :submission_count => 0
235 end
240 end
236
241
237 status.submission_count += 1
242 status.submission_count += 1
238 status.passed = passed
243 status.passed = passed
239 status.save
244 status.save
240
245
241 if passed
246 if passed
242 flash[:notice] = 'Correct solution.'
247 flash[:notice] = 'Correct solution.'
243 user.update_codejom_status
248 user.update_codejom_status
244 else
249 else
245 flash[:notice] = 'Incorrect solution.'
250 flash[:notice] = 'Incorrect solution.'
246 end
251 end
247 redirect_to :action => 'list'
252 redirect_to :action => 'list'
248 end
253 end
249
254
250 protected
255 protected
251
256
252 def prepare_announcements(recent=nil)
257 def prepare_announcements(recent=nil)
253 if Configuration.show_tasks_to?(@user)
258 if Configuration.show_tasks_to?(@user)
254 @announcements = Announcement.find_published(true)
259 @announcements = Announcement.find_published(true)
255 else
260 else
256 @announcements = Announcement.find_published
261 @announcements = Announcement.find_published
257 end
262 end
258 if recent!=nil
263 if recent!=nil
259 recent_id = recent.to_i
264 recent_id = recent.to_i
260 @announcements = @announcements.find_all { |a| a.id > recent_id }
265 @announcements = @announcements.find_all { |a| a.id > recent_id }
261 end
266 end
262 end
267 end
263
268
264 def prepare_list_information
269 def prepare_list_information
265 @user = User.find(session[:user_id])
270 @user = User.find(session[:user_id])
266
271
267 all_problems = Problem.find_available_problems
272 all_problems = Problem.find_available_problems
268
273
269 passed = {}
274 passed = {}
270 sub_count = {}
275 sub_count = {}
271 @user.submission_statuses.each do |status|
276 @user.submission_statuses.each do |status|
272 if status.passed
277 if status.passed
273 passed[status.problem_id] = true
278 passed[status.problem_id] = true
274 end
279 end
275 sub_count[status.problem_id] = status.submission_count
280 sub_count[status.problem_id] = status.submission_count
276 end
281 end
277
282
278 @problems = all_problems.reject { |problem| passed.has_key? problem.id }
283 @problems = all_problems.reject { |problem| passed.has_key? problem.id }
279
284
280 @prob_submissions = Array.new
285 @prob_submissions = Array.new
281 @problems.each do |p|
286 @problems.each do |p|
282 if sub_count.has_key? p.id
287 if sub_count.has_key? p.id
283 @prob_submissions << { :count => sub_count[p.id] }
288 @prob_submissions << { :count => sub_count[p.id] }
284 else
289 else
285 @prob_submissions << { :count => 0 }
290 @prob_submissions << { :count => 0 }
286 end
291 end
287 end
292 end
288 prepare_announcements
293 prepare_announcements
289 end
294 end
290
295
291 def check_viewability
296 def check_viewability
292 @user = User.find(session[:user_id])
297 @user = User.find(session[:user_id])
293 if (!Configuration.show_tasks_to?(@user)) and
298 if (!Configuration.show_tasks_to?(@user)) and
294 ((action_name=='submission') or (action_name=='submit'))
299 ((action_name=='submission') or (action_name=='submit'))
295 redirect_to :action => 'list' and return
300 redirect_to :action => 'list' and return
296 end
301 end
297 end
302 end
298
303
299 def prepare_grading_result(submission)
304 def prepare_grading_result(submission)
300 if Configuration.task_grading_info.has_key? submission.problem.name
305 if Configuration.task_grading_info.has_key? submission.problem.name
301 grading_info = Configuration.task_grading_info[submission.problem.name]
306 grading_info = Configuration.task_grading_info[submission.problem.name]
302 else
307 else
303 # guess task info from problem.full_score
308 # guess task info from problem.full_score
304 cases = submission.problem.full_score / 10
309 cases = submission.problem.full_score / 10
305 grading_info = {
310 grading_info = {
306 'testruns' => cases,
311 'testruns' => cases,
307 'testcases' => cases
312 'testcases' => cases
308 }
313 }
309 end
314 end
310 @test_runs = []
315 @test_runs = []
311 if grading_info['testruns'].is_a? Integer
316 if grading_info['testruns'].is_a? Integer
312 trun_count = grading_info['testruns']
317 trun_count = grading_info['testruns']
313 trun_count.times do |i|
318 trun_count.times do |i|
314 @test_runs << [ read_grading_result(@user.login,
319 @test_runs << [ read_grading_result(@user.login,
315 submission.problem.name,
320 submission.problem.name,
316 submission.id,
321 submission.id,
317 i+1) ]
322 i+1) ]
318 end
323 end
319 else
324 else
320 grading_info['testruns'].keys.sort.each do |num|
325 grading_info['testruns'].keys.sort.each do |num|
321 run = []
326 run = []
322 testrun = grading_info['testruns'][num]
327 testrun = grading_info['testruns'][num]
323 testrun.each do |c|
328 testrun.each do |c|
324 run << read_grading_result(@user.login,
329 run << read_grading_result(@user.login,
325 submission.problem.name,
330 submission.problem.name,
326 submission.id,
331 submission.id,
327 c)
332 c)
328 end
333 end
329 @test_runs << run
334 @test_runs << run
330 end
335 end
331 end
336 end
332 end
337 end
333
338
334 def grading_result_dir(user_name, problem_name, submission_id, case_num)
339 def grading_result_dir(user_name, problem_name, submission_id, case_num)
335 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
340 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
336 end
341 end
337
342
338 def output_filename(user_name, problem_name, submission_id, case_num)
343 def output_filename(user_name, problem_name, submission_id, case_num)
339 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
344 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
340 return "#{dir}/output.txt"
345 return "#{dir}/output.txt"
341 end
346 end
342
347
343 def read_grading_result(user_name, problem_name, submission_id, case_num)
348 def read_grading_result(user_name, problem_name, submission_id, case_num)
344 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
349 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
345 result_file_name = "#{dir}/result"
350 result_file_name = "#{dir}/result"
346 if !FileTest.exists?(result_file_name)
351 if !FileTest.exists?(result_file_name)
347 return {:num => case_num, :msg => 'program did not run'}
352 return {:num => case_num, :msg => 'program did not run'}
348 else
353 else
349 results = File.open(result_file_name).readlines
354 results = File.open(result_file_name).readlines
350 run_stat = extract_running_stat(results)
355 run_stat = extract_running_stat(results)
351 output_filename = "#{dir}/output.txt"
356 output_filename = "#{dir}/output.txt"
352 if FileTest.exists?(output_filename)
357 if FileTest.exists?(output_filename)
353 output_file = true
358 output_file = true
354 output_size = File.size(output_filename)
359 output_size = File.size(output_filename)
355 else
360 else
356 output_file = false
361 output_file = false
357 output_size = 0
362 output_size = 0
358 end
363 end
359
364
360 return {
365 return {
361 :num => case_num,
366 :num => case_num,
362 :msg => results[0],
367 :msg => results[0],
363 :run_stat => run_stat,
368 :run_stat => run_stat,
364 :output => output_file,
369 :output => output_file,
365 :output_size => output_size
370 :output_size => output_size
366 }
371 }
367 end
372 end
368 end
373 end
369
374
370 # copied from grader/script/lib/test_request_helper.rb
375 # copied from grader/script/lib/test_request_helper.rb
371 def extract_running_stat(results)
376 def extract_running_stat(results)
372 running_stat_line = results[-1]
377 running_stat_line = results[-1]
373
378
374 # extract exit status line
379 # extract exit status line
375 run_stat = ""
380 run_stat = ""
376 if !(/[Cc]orrect/.match(results[0]))
381 if !(/[Cc]orrect/.match(results[0]))
377 run_stat = results[0].chomp
382 run_stat = results[0].chomp
378 else
383 else
379 run_stat = 'Program exited normally'
384 run_stat = 'Program exited normally'
380 end
385 end
381
386
382 logger.info "Stat line: #{running_stat_line}"
387 logger.info "Stat line: #{running_stat_line}"
383
388
384 # extract running time
389 # extract running time
385 if res = /r(.*)u(.*)s/.match(running_stat_line)
390 if res = /r(.*)u(.*)s/.match(running_stat_line)
386 seconds = (res[1].to_f + res[2].to_f)
391 seconds = (res[1].to_f + res[2].to_f)
387 time_stat = "Time used: #{seconds} sec."
392 time_stat = "Time used: #{seconds} sec."
388 else
393 else
389 seconds = nil
394 seconds = nil
390 time_stat = "Time used: n/a sec."
395 time_stat = "Time used: n/a sec."
391 end
396 end
392
397
393 # extract memory usage
398 # extract memory usage
394 if res = /s(.*)m/.match(running_stat_line)
399 if res = /s(.*)m/.match(running_stat_line)
395 memory_used = res[1].to_i
400 memory_used = res[1].to_i
396 else
401 else
397 memory_used = -1
402 memory_used = -1
@@ -1,5 +1,11
1 class TestPairAssignment < ActiveRecord::Base
1 class TestPairAssignment < ActiveRecord::Base
2 +
2 belongs_to :user
3 belongs_to :user
3 belongs_to :test_pair
4 belongs_to :test_pair
4 belongs_to :problem
5 belongs_to :problem
6 +
7 + def expired?
8 + return created_at + TEST_ASSIGNMENT_EXPIRATION_DURATION < Time.new.gmtime
9 + end
10 +
5 end
11 end
@@ -1,291 +1,291
1 require 'digest/sha1'
1 require 'digest/sha1'
2
2
3 class User < ActiveRecord::Base
3 class User < ActiveRecord::Base
4
4
5 has_and_belongs_to_many :roles
5 has_and_belongs_to_many :roles
6
6
7 has_many :test_requests, :order => "submitted_at DESC"
7 has_many :test_requests, :order => "submitted_at DESC"
8
8
9 has_many :messages,
9 has_many :messages,
10 :class_name => "Message",
10 :class_name => "Message",
11 :foreign_key => "sender_id",
11 :foreign_key => "sender_id",
12 :order => 'created_at DESC'
12 :order => 'created_at DESC'
13
13
14 has_many :replied_messages,
14 has_many :replied_messages,
15 :class_name => "Message",
15 :class_name => "Message",
16 :foreign_key => "receiver_id",
16 :foreign_key => "receiver_id",
17 :order => 'created_at DESC'
17 :order => 'created_at DESC'
18
18
19 has_many :test_pair_assignments, :dependent => :delete_all
19 has_many :test_pair_assignments, :dependent => :delete_all
20 has_many :submission_statuses
20 has_many :submission_statuses
21
21
22 has_one :contest_stat, :class_name => "UserContestStat"
22 has_one :contest_stat, :class_name => "UserContestStat"
23
23
24 belongs_to :site
24 belongs_to :site
25 belongs_to :country
25 belongs_to :country
26
26
27 # For Code Jom
27 # For Code Jom
28 has_one :codejom_status
28 has_one :codejom_status
29
29
30 named_scope :activated_users, :conditions => {:activated => true}
30 named_scope :activated_users, :conditions => {:activated => true}
31
31
32 validates_presence_of :login
32 validates_presence_of :login
33 validates_uniqueness_of :login
33 validates_uniqueness_of :login
34 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
34 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
35 validates_length_of :login, :within => 3..30
35 validates_length_of :login, :within => 3..30
36
36
37 validates_presence_of :full_name
37 validates_presence_of :full_name
38 validates_length_of :full_name, :minimum => 1
38 validates_length_of :full_name, :minimum => 1
39
39
40 validates_presence_of :password, :if => :password_required?
40 validates_presence_of :password, :if => :password_required?
41 validates_length_of :password, :within => 4..20, :if => :password_required?
41 validates_length_of :password, :within => 4..20, :if => :password_required?
42 validates_confirmation_of :password, :if => :password_required?
42 validates_confirmation_of :password, :if => :password_required?
43
43
44 validates_format_of :email,
44 validates_format_of :email,
45 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
45 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
46 :if => :email_validation?
46 :if => :email_validation?
47 validate :uniqueness_of_email_from_activated_users,
47 validate :uniqueness_of_email_from_activated_users,
48 :if => :email_validation?
48 :if => :email_validation?
49 validate :enough_time_interval_between_same_email_registrations,
49 validate :enough_time_interval_between_same_email_registrations,
50 :if => :email_validation?
50 :if => :email_validation?
51
51
52 # these are for ytopc
52 # these are for ytopc
53 # disable for now
53 # disable for now
54 #validates_presence_of :province
54 #validates_presence_of :province
55
55
56 attr_accessor :password
56 attr_accessor :password
57
57
58 before_save :encrypt_new_password
58 before_save :encrypt_new_password
59 before_save :assign_default_site
59 before_save :assign_default_site
60
60
61 def self.authenticate(login, password)
61 def self.authenticate(login, password)
62 user = find_by_login(login)
62 user = find_by_login(login)
63 return user if user && user.authenticated?(password)
63 return user if user && user.authenticated?(password)
64 end
64 end
65
65
66 def authenticated?(password)
66 def authenticated?(password)
67 if self.activated
67 if self.activated
68 hashed_password == User.encrypt(password,self.salt)
68 hashed_password == User.encrypt(password,self.salt)
69 else
69 else
70 false
70 false
71 end
71 end
72 end
72 end
73
73
74 def admin?
74 def admin?
75 self.roles.detect {|r| r.name == 'admin' }
75 self.roles.detect {|r| r.name == 'admin' }
76 end
76 end
77
77
78 # These are methods related to test pairs
78 # These are methods related to test pairs
79
79
80 def get_test_pair_assignments_for(problem)
80 def get_test_pair_assignments_for(problem)
81 test_pair_assignments.find_all { |a| a.problem_id == problem.id }
81 test_pair_assignments.find_all { |a| a.problem_id == problem.id }
82 end
82 end
83
83
84 def get_recent_test_pair_assignment_for(problem)
84 def get_recent_test_pair_assignment_for(problem)
85 assignments = get_test_pair_assignments_for problem
85 assignments = get_test_pair_assignments_for problem
86 if assignments.length == 0
86 if assignments.length == 0
87 return nil
87 return nil
88 else
88 else
89 recent = assignments[0]
89 recent = assignments[0]
90 assignments.each do |a|
90 assignments.each do |a|
91 recent = a if a.request_number > recent.request_number
91 recent = a if a.request_number > recent.request_number
92 end
92 end
93 return recent
93 return recent
94 end
94 end
95 end
95 end
96
96
97 def can_request_new_test_pair_for?(problem)
97 def can_request_new_test_pair_for?(problem)
98 recent = get_recent_test_pair_assignment_for problem
98 recent = get_recent_test_pair_assignment_for problem
99 - return (recent == nil or recent.submitted)
99 + return (recent == nil or recent.submitted or recent.expired?)
100 end
100 end
101
101
102 def get_new_test_pair_assignment_for(problem)
102 def get_new_test_pair_assignment_for(problem)
103 previous_assignment_numbers =
103 previous_assignment_numbers =
104 get_test_pair_assignments_for(problem).collect {|a| a.test_pair_number }
104 get_test_pair_assignments_for(problem).collect {|a| a.test_pair_number }
105 test_pair = problem.random_test_pair(previous_assignment_numbers)
105 test_pair = problem.random_test_pair(previous_assignment_numbers)
106 if test_pair
106 if test_pair
107 assignment = TestPairAssignment.new(:user => self,
107 assignment = TestPairAssignment.new(:user => self,
108 :problem => problem,
108 :problem => problem,
109 :test_pair => test_pair,
109 :test_pair => test_pair,
110 :test_pair_number => test_pair.number,
110 :test_pair_number => test_pair.number,
111 :request_number =>
111 :request_number =>
112 previous_assignment_numbers.length + 1,
112 previous_assignment_numbers.length + 1,
113 :submitted => false)
113 :submitted => false)
114 return assignment
114 return assignment
115 else
115 else
116 return nil
116 return nil
117 end
117 end
118 end
118 end
119
119
120 def get_submission_status_for(problem)
120 def get_submission_status_for(problem)
121 SubmissionStatus.find(:first,
121 SubmissionStatus.find(:first,
122 :conditions => {
122 :conditions => {
123 :user_id => id,
123 :user_id => id,
124 :problem_id => problem.id
124 :problem_id => problem.id
125 })
125 })
126 end
126 end
127
127
128 def email_for_editing
128 def email_for_editing
129 if self.email==nil
129 if self.email==nil
130 "(unknown)"
130 "(unknown)"
131 elsif self.email==''
131 elsif self.email==''
132 "(blank)"
132 "(blank)"
133 else
133 else
134 self.email
134 self.email
135 end
135 end
136 end
136 end
137
137
138 def email_for_editing=(e)
138 def email_for_editing=(e)
139 self.email=e
139 self.email=e
140 end
140 end
141
141
142 def alias_for_editing
142 def alias_for_editing
143 if self.alias==nil
143 if self.alias==nil
144 "(unknown)"
144 "(unknown)"
145 elsif self.alias==''
145 elsif self.alias==''
146 "(blank)"
146 "(blank)"
147 else
147 else
148 self.alias
148 self.alias
149 end
149 end
150 end
150 end
151
151
152 def alias_for_editing=(e)
152 def alias_for_editing=(e)
153 self.alias=e
153 self.alias=e
154 end
154 end
155
155
156 def activation_key
156 def activation_key
157 if self.hashed_password==nil
157 if self.hashed_password==nil
158 encrypt_new_password
158 encrypt_new_password
159 end
159 end
160 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
160 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
161 end
161 end
162
162
163 def verify_activation_key(key)
163 def verify_activation_key(key)
164 key == activation_key
164 key == activation_key
165 end
165 end
166
166
167 def self.random_password(length=5)
167 def self.random_password(length=5)
168 chars = 'abcdefghjkmnopqrstuvwxyz'
168 chars = 'abcdefghjkmnopqrstuvwxyz'
169 password = ''
169 password = ''
170 length.times { password << chars[rand(chars.length - 1)] }
170 length.times { password << chars[rand(chars.length - 1)] }
171 password
171 password
172 end
172 end
173
173
174 def self.find_non_admin_with_prefix(prefix='')
174 def self.find_non_admin_with_prefix(prefix='')
175 users = User.find(:all)
175 users = User.find(:all)
176 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
176 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
177 end
177 end
178
178
179 # Contest information
179 # Contest information
180
180
181 def contest_time_left
181 def contest_time_left
182 if Configuration.contest_mode?
182 if Configuration.contest_mode?
183 return nil if site==nil
183 return nil if site==nil
184 return site.time_left
184 return site.time_left
185 elsif Configuration.indv_contest_mode?
185 elsif Configuration.indv_contest_mode?
186 time_limit = Configuration.contest_time_limit
186 time_limit = Configuration.contest_time_limit
187 if contest_stat==nil
187 if contest_stat==nil
188 return (Time.now.gmtime + time_limit) - Time.now.gmtime
188 return (Time.now.gmtime + time_limit) - Time.now.gmtime
189 else
189 else
190 finish_time = contest_stat.started_at + time_limit
190 finish_time = contest_stat.started_at + time_limit
191 current_time = Time.now.gmtime
191 current_time = Time.now.gmtime
192 if current_time > finish_time
192 if current_time > finish_time
193 return 0
193 return 0
194 else
194 else
195 return finish_time - current_time
195 return finish_time - current_time
196 end
196 end
197 end
197 end
198 else
198 else
199 return nil
199 return nil
200 end
200 end
201 end
201 end
202
202
203 def contest_finished?
203 def contest_finished?
204 if Configuration.contest_mode?
204 if Configuration.contest_mode?
205 return false if site==nil
205 return false if site==nil
206 return site.finished?
206 return site.finished?
207 elsif Configuration.indv_contest_mode?
207 elsif Configuration.indv_contest_mode?
208 time_limit = Configuration.contest_time_limit
208 time_limit = Configuration.contest_time_limit
209
209
210 return false if contest_stat==nil
210 return false if contest_stat==nil
211
211
212 return contest_time_left == 0
212 return contest_time_left == 0
213 else
213 else
214 return false
214 return false
215 end
215 end
216 end
216 end
217
217
218 def contest_started?
218 def contest_started?
219 if Configuration.contest_mode?
219 if Configuration.contest_mode?
220 return true if site==nil
220 return true if site==nil
221 return site.started
221 return site.started
222 else
222 else
223 return true
223 return true
224 end
224 end
225 end
225 end
226
226
227 # For Code Jom
227 # For Code Jom
228 def update_codejom_status
228 def update_codejom_status
229 status = codejom_status || CodejomStatus.new(:user => self)
229 status = codejom_status || CodejomStatus.new(:user => self)
230 problem_count = Problem.available_problem_count
230 problem_count = Problem.available_problem_count
231 status.num_problems_passed = (self.submission_statuses.find_all {|s| s.passed}).length
231 status.num_problems_passed = (self.submission_statuses.find_all {|s| s.passed}).length
232 status.alive = (problem_count - (status.num_problems_passed)) <= CODEJOM_MAX_ALIVE_LEVEL
232 status.alive = (problem_count - (status.num_problems_passed)) <= CODEJOM_MAX_ALIVE_LEVEL
233 status.save
233 status.save
234 end
234 end
235
235
236 def codejom_level
236 def codejom_level
237 problem_count = Problem.available_problem_count
237 problem_count = Problem.available_problem_count
238 if codejom_status!=nil
238 if codejom_status!=nil
239 return problem_count - codejom_status.num_problems_passed
239 return problem_count - codejom_status.num_problems_passed
240 else
240 else
241 return problem_count
241 return problem_count
242 end
242 end
243 end
243 end
244
244
245 protected
245 protected
246 def encrypt_new_password
246 def encrypt_new_password
247 return if password.blank?
247 return if password.blank?
248 self.salt = (10+rand(90)).to_s
248 self.salt = (10+rand(90)).to_s
249 self.hashed_password = User.encrypt(self.password,self.salt)
249 self.hashed_password = User.encrypt(self.password,self.salt)
250 end
250 end
251
251
252 def assign_default_site
252 def assign_default_site
253 # have to catch error when migrating (because self.site is not available).
253 # have to catch error when migrating (because self.site is not available).
254 begin
254 begin
255 if self.site==nil
255 if self.site==nil
256 self.site = Site.find_by_name('default')
256 self.site = Site.find_by_name('default')
257 if self.site==nil
257 if self.site==nil
258 self.site = Site.find(1) # when 'default has be renamed'
258 self.site = Site.find(1) # when 'default has be renamed'
259 end
259 end
260 end
260 end
261 rescue
261 rescue
262 end
262 end
263 end
263 end
264
264
265 def password_required?
265 def password_required?
266 self.hashed_password.blank? || !self.password.blank?
266 self.hashed_password.blank? || !self.password.blank?
267 end
267 end
268
268
269 def self.encrypt(string,salt)
269 def self.encrypt(string,salt)
270 Digest::SHA1.hexdigest(salt + string)
270 Digest::SHA1.hexdigest(salt + string)
271 end
271 end
272
272
273 def uniqueness_of_email_from_activated_users
273 def uniqueness_of_email_from_activated_users
274 user = User.activated_users.find_by_email(self.email)
274 user = User.activated_users.find_by_email(self.email)
275 if user and (user.login != self.login)
275 if user and (user.login != self.login)
276 self.errors.add_to_base("Email has already been taken")
276 self.errors.add_to_base("Email has already been taken")
277 end
277 end
278 end
278 end
279
279
280 def enough_time_interval_between_same_email_registrations
280 def enough_time_interval_between_same_email_registrations
281 return if !self.new_record?
281 return if !self.new_record?
282 return if self.activated
282 return if self.activated
283 open_user = User.find_by_email(self.email,
283 open_user = User.find_by_email(self.email,
284 :order => 'created_at DESC')
284 :order => 'created_at DESC')
285 if open_user and open_user.created_at and
285 if open_user and open_user.created_at and
286 (open_user.created_at > Time.now.gmtime - 5.minutes)
286 (open_user.created_at > Time.now.gmtime - 5.minutes)
287 self.errors.add_to_base("There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
287 self.errors.add_to_base("There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
288 end
288 end
289 end
289 end
290
290
291 def email_validation?
291 def email_validation?
@@ -1,22 +1,22
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
3
4 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5 <head>
5 <head>
6 <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
6 <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
7 <title><%= Configuration['contest.name'] %></title>
7 <title><%= Configuration['contest.name'] %></title>
8 <%= stylesheet_link_tag 'application' %>
8 <%= stylesheet_link_tag 'application' %>
9 <%= yield :head %>
9 <%= yield :head %>
10 </head>
10 </head>
11 <body>
11 <body>
12
12
13 <div class="userbar">
13 <div class="userbar">
14 <%= user_header %>
14 <%= user_header %>
15 </div>
15 </div>
16
16
17 - <%= content_tag(:p,flash[:notice],:style => "color:green") if flash[:notice]!=nil %>
17 + <% if flash[:notice]!=nil %><div class="notice-bar"><span class="notice"><%= flash[:notice] %></span></div><% end %>
18
18
19 <%= yield %>
19 <%= yield %>
20
20
21 </body>
21 </body>
22 </html>
22 </html>
@@ -1,18 +1,19
1 .problem-panel{:id => "problem-panel-#{problem.id}", :style => "display:none"}
1 .problem-panel{:id => "problem-panel-#{problem.id}", :style => "display:none"}
2 .problem-form{:id => "problem-form-#{problem.id}"}
2 .problem-form{:id => "problem-form-#{problem.id}"}
3 - form_tag({ :action => 'download_input', :id => problem.id }, :method => :post) do
3 - form_tag({ :action => 'download_input', :id => problem.id }, :method => :post) do
4 %b Input:
4 %b Input:
5 %input{:type => "submit", :value => "Download input"}
5 %input{:type => "submit", :value => "Download input"}
6 + = "After downloading, you have #{TEST_ASSIGNMENT_EXPIRATION_DURATION/60} minutes to submit."
6 - form_tag({ :action => 'submit_solution', :id => problem.id }, :method => :post, :multipart => true) do
7 - form_tag({ :action => 'submit_solution', :id => problem.id }, :method => :post, :multipart => true) do
7 %b Submit output:
8 %b Submit output:
8 %input{:type => "file", :name => "file"}
9 %input{:type => "file", :name => "file"}
9 %input{:type => "submit", :value => "Submit solution"}
10 %input{:type => "submit", :value => "Submit solution"}
10
11
11 .problem-description
12 .problem-description
12 - if problem.description!=nil
13 - if problem.description!=nil
13 - if problem.description.markdowned
14 - if problem.description.markdowned
14 = markdown(problem.description.body)
15 = markdown(problem.description.body)
15 - else
16 - else
16 = problem.description.body
17 = problem.description.body
17 - else
18 - else
18 (not available)
19 (not available)
@@ -1,109 +1,110
1 # Be sure to restart your web server when you modify this file.
1 # Be sure to restart your web server when you modify this file.
2
2
3 # Uncomment below to force Rails into production mode when
3 # Uncomment below to force Rails into production mode when
4 # you don't control web/app server and can't set it the proper way
4 # you don't control web/app server and can't set it the proper way
5 # ENV['RAILS_ENV'] ||= 'production'
5 # ENV['RAILS_ENV'] ||= 'production'
6
6
7 # Specifies gem version of Rails to use when vendor/rails is not present
7 # Specifies gem version of Rails to use when vendor/rails is not present
8 RAILS_GEM_VERSION = '2.3.4' unless defined? RAILS_GEM_VERSION
8 RAILS_GEM_VERSION = '2.3.4' unless defined? RAILS_GEM_VERSION
9
9
10 # Bootstrap the Rails environment, frameworks, and default configuration
10 # Bootstrap the Rails environment, frameworks, and default configuration
11 require File.join(File.dirname(__FILE__), 'boot')
11 require File.join(File.dirname(__FILE__), 'boot')
12
12
13 Rails::Initializer.run do |config|
13 Rails::Initializer.run do |config|
14 # Settings in config/environments/* take precedence over those specified here
14 # Settings in config/environments/* take precedence over those specified here
15
15
16 # Skip frameworks you're not going to use (only works if using vendor/rails)
16 # Skip frameworks you're not going to use (only works if using vendor/rails)
17 # config.frameworks -= [ :action_web_service, :action_mailer ]
17 # config.frameworks -= [ :action_web_service, :action_mailer ]
18
18
19 # Only load the plugins named here, by default all plugins in vendor/plugins are loaded
19 # Only load the plugins named here, by default all plugins in vendor/plugins are loaded
20 # config.plugins = %W( exception_notification ssl_requirement )
20 # config.plugins = %W( exception_notification ssl_requirement )
21
21
22 # Add additional load paths for your own custom dirs
22 # Add additional load paths for your own custom dirs
23 # config.load_paths += %W( #{RAILS_ROOT}/extras )
23 # config.load_paths += %W( #{RAILS_ROOT}/extras )
24
24
25 # Force all environments to use the same logger level
25 # Force all environments to use the same logger level
26 # (by default production uses :info, the others :debug)
26 # (by default production uses :info, the others :debug)
27 # config.log_level = :debug
27 # config.log_level = :debug
28
28
29 # Use the database for sessions instead of the file system
29 # Use the database for sessions instead of the file system
30 # (create the session table with 'rake db:sessions:create')
30 # (create the session table with 'rake db:sessions:create')
31 config.action_controller.session_store = :active_record_store
31 config.action_controller.session_store = :active_record_store
32
32
33 # Use SQL instead of Active Record's schema dumper when creating the test database.
33 # Use SQL instead of Active Record's schema dumper when creating the test database.
34 # This is necessary if your schema can't be completely dumped by the schema dumper,
34 # This is necessary if your schema can't be completely dumped by the schema dumper,
35 # like if you have constraints or database-specific column types
35 # like if you have constraints or database-specific column types
36 # config.active_record.schema_format = :sql
36 # config.active_record.schema_format = :sql
37
37
38 # Activate observers that should always be running
38 # Activate observers that should always be running
39 # config.active_record.observers = :cacher, :garbage_collector
39 # config.active_record.observers = :cacher, :garbage_collector
40
40
41 # Make Active Record use UTC-base instead of local time
41 # Make Active Record use UTC-base instead of local time
42 config.time_zone = 'UTC'
42 config.time_zone = 'UTC'
43
43
44 # Setting locales
44 # Setting locales
45 config.i18n.default_locale = 'en'
45 config.i18n.default_locale = 'en'
46
46
47 # See Rails::Configuration for more options
47 # See Rails::Configuration for more options
48
48
49 # -------------
49 # -------------
50 # Required gems
50 # Required gems
51 # -------------
51 # -------------
52 config.gem "haml"
52 config.gem "haml"
53 config.gem "tmail"
53 config.gem "tmail"
54 config.gem "rdiscount", :lib => "rdiscount"
54 config.gem "rdiscount", :lib => "rdiscount"
55
55
56 # NOTES on rspec: if you wan to test with rspec, you have to install
56 # NOTES on rspec: if you wan to test with rspec, you have to install
57 # rspec yourself, just call: [sudo] gem install rspec-rails
57 # rspec yourself, just call: [sudo] gem install rspec-rails
58
58
59 end
59 end
60
60
61 # Add new inflection rules using the following format
61 # Add new inflection rules using the following format
62 # (all these examples are active by default):
62 # (all these examples are active by default):
63 # Inflector.inflections do |inflect|
63 # Inflector.inflections do |inflect|
64 # inflect.plural /^(ox)$/i, '\1en'
64 # inflect.plural /^(ox)$/i, '\1en'
65 # inflect.singular /^(ox)en/i, '\1'
65 # inflect.singular /^(ox)en/i, '\1'
66 # inflect.irregular 'person', 'people'
66 # inflect.irregular 'person', 'people'
67 # inflect.uncountable %w( fish sheep )
67 # inflect.uncountable %w( fish sheep )
68 # end
68 # end
69
69
70 # Add new mime types for use in respond_to blocks:
70 # Add new mime types for use in respond_to blocks:
71 # Mime::Type.register "text/richtext", :rtf
71 # Mime::Type.register "text/richtext", :rtf
72 # Mime::Type.register "application/x-mobile", :mobile
72 # Mime::Type.register "application/x-mobile", :mobile
73
73
74 # Include your application configuration below
74 # Include your application configuration below
75
75
76 # If you want to manage graders through web interface, set the path to
76 # If you want to manage graders through web interface, set the path to
77 # the grader directory below. This dir is where raw, ev, ev-exam,
77 # the grader directory below. This dir is where raw, ev, ev-exam,
78 # scripts reside. All grader scripts will be in
78 # scripts reside. All grader scripts will be in
79 # #{GRADER_ROOT_DIR}/scripts.
79 # #{GRADER_ROOT_DIR}/scripts.
80 GRADER_ROOT_DIR = ''
80 GRADER_ROOT_DIR = ''
81
81
82 # These are where inputs and outputs of test requests are stored
82 # These are where inputs and outputs of test requests are stored
83 TEST_REQUEST_INPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/input'
83 TEST_REQUEST_INPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/input'
84 TEST_REQUEST_OUTPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/output'
84 TEST_REQUEST_OUTPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/output'
85
85
86 # To use ANALYSIS MODE, provide the testcases/testruns breakdown,
86 # To use ANALYSIS MODE, provide the testcases/testruns breakdown,
87 # and the directory of the grading result (usually in judge's dir).
87 # and the directory of the grading result (usually in judge's dir).
88 TASK_GRADING_INFO_FILENAME = RAILS_ROOT + '/config/tasks.yml'
88 TASK_GRADING_INFO_FILENAME = RAILS_ROOT + '/config/tasks.yml'
89
89
90 # TODO: change this to where results are kept.
90 # TODO: change this to where results are kept.
91 GRADING_RESULT_DIR = 'RESULT-DIR'
91 GRADING_RESULT_DIR = 'RESULT-DIR'
92
92
93 # Change this to allow importing testdata into database as test-pairs.
93 # Change this to allow importing testdata into database as test-pairs.
94 # This is mainly for Code Jom contest.
94 # This is mainly for Code Jom contest.
95 ALLOW_TEST_PAIR_IMPORT = false
95 ALLOW_TEST_PAIR_IMPORT = false
96
96
97 # Uncomment so that the system validates user e-mails
97 # Uncomment so that the system validates user e-mails
98 # VALIDATE_USER_EMAILS = true
98 # VALIDATE_USER_EMAILS = true
99
99
100 # Uncomment so that Apache X-Sendfile is used when delivering files
100 # Uncomment so that Apache X-Sendfile is used when delivering files
101 # (e.g., in /tasks/view).
101 # (e.g., in /tasks/view).
102 # USE_APACHE_XSENDFILE = true
102 # USE_APACHE_XSENDFILE = true
103
103
104 # Uncomment so that configuration is read only once when the server is loaded
104 # Uncomment so that configuration is read only once when the server is loaded
105 # Configuration.enable_caching
105 # Configuration.enable_caching
106
106
107 # OPTIONS FOR CODE JOM
107 # OPTIONS FOR CODE JOM
108 # --------------------
108 # --------------------
109 CODEJOM_MAX_ALIVE_LEVEL = 10
109 CODEJOM_MAX_ALIVE_LEVEL = 10
110 + TEST_ASSIGNMENT_EXPIRATION_DURATION = 5.minute
@@ -75,192 +75,205
75 tr.info-head {
75 tr.info-head {
76 background: #777777;
76 background: #777777;
77 color: white; }
77 color: white; }
78
78
79 tr.info-odd {
79 tr.info-odd {
80 background: #eeeeee; }
80 background: #eeeeee; }
81
81
82 tr.info-even {
82 tr.info-even {
83 background: #f9f9f9; }
83 background: #f9f9f9; }
84
84
85 .submitbox {
85 .submitbox {
86 background: #eeeeff;
86 background: #eeeeff;
87 border: 1px dotted #99aaee;
87 border: 1px dotted #99aaee;
88 padding: 5px;
88 padding: 5px;
89 margin: 10px 0px;
89 margin: 10px 0px;
90 color: black;
90 color: black;
91 font-size: 13px; }
91 font-size: 13px; }
92
92
93 .errorExplanation {
93 .errorExplanation {
94 border: 1px dotted gray;
94 border: 1px dotted gray;
95 color: #bb2222;
95 color: #bb2222;
96 padding: 5px 15px 5px 15px;
96 padding: 5px 15px 5px 15px;
97 margin-bottom: 5px;
97 margin-bottom: 5px;
98 background-color: white;
98 background-color: white;
99 font-weight: normal; }
99 font-weight: normal; }
100 .errorExplanation h2 {
100 .errorExplanation h2 {
101 color: #cc1111;
101 color: #cc1111;
102 font-weight: bold; }
102 font-weight: bold; }
103
103
104 table.uinfo {
104 table.uinfo {
105 border-collapse: collapse;
105 border-collapse: collapse;
106 border: 1px solid black;
106 border: 1px solid black;
107 font-size: 13px; }
107 font-size: 13px; }
108
108
109 td.uinfo {
109 td.uinfo {
110 vertical-align: top;
110 vertical-align: top;
111 border: 1px solid black;
111 border: 1px solid black;
112 padding: 5px; }
112 padding: 5px; }
113
113
114 th.uinfo {
114 th.uinfo {
115 background: lightgreen;
115 background: lightgreen;
116 vertical-align: top;
116 vertical-align: top;
117 text-align: right;
117 text-align: right;
118 border: 1px solid black;
118 border: 1px solid black;
119 padding: 5px; }
119 padding: 5px; }
120
120
121 .compilermsgbody {
121 .compilermsgbody {
122 font-family: monospace; }
122 font-family: monospace; }
123
123
124 .task-menu {
124 .task-menu {
125 text-align: center;
125 text-align: center;
126 font-size: 13px;
126 font-size: 13px;
127 line-height: 1.75em;
127 line-height: 1.75em;
128 font-weight: bold;
128 font-weight: bold;
129 border-top: 1px dashed gray;
129 border-top: 1px dashed gray;
130 border-bottom: 1px dashed gray;
130 border-bottom: 1px dashed gray;
131 margin-top: 2px;
131 margin-top: 2px;
132 margin-bottom: 4px; }
132 margin-bottom: 4px; }
133
133
134 table.taskdesc {
134 table.taskdesc {
135 border: 2px solid #dddddd;
135 border: 2px solid #dddddd;
136 border-collapse: collapse;
136 border-collapse: collapse;
137 margin: 10px auto;
137 margin: 10px auto;
138 width: 90%;
138 width: 90%;
139 font-size: 13px; }
139 font-size: 13px; }
140 table.taskdesc p {
140 table.taskdesc p {
141 font-size: 13px; }
141 font-size: 13px; }
142 table.taskdesc tr.name {
142 table.taskdesc tr.name {
143 border: 2px solid #dddddd;
143 border: 2px solid #dddddd;
144 background: #dddddd;
144 background: #dddddd;
145 color: #333333;
145 color: #333333;
146 font-weight: bold;
146 font-weight: bold;
147 font-size: 14px;
147 font-size: 14px;
148 line-height: 1.5em;
148 line-height: 1.5em;
149 text-align: center; }
149 text-align: center; }
150 table.taskdesc td.desc-odd {
150 table.taskdesc td.desc-odd {
151 padding: 5px;
151 padding: 5px;
152 padding-left: 20px;
152 padding-left: 20px;
153 background: #fefeee; }
153 background: #fefeee; }
154 table.taskdesc td.desc-even {
154 table.taskdesc td.desc-even {
155 padding: 5px;
155 padding: 5px;
156 padding-left: 20px;
156 padding-left: 20px;
157 background: #feeefe; }
157 background: #feeefe; }
158
158
159 .announcementbox {
159 .announcementbox {
160 margin: 10px 0px;
160 margin: 10px 0px;
161 background: #bbddee;
161 background: #bbddee;
162 padding: 1px; }
162 padding: 1px; }
163 .announcementbox span.title {
163 .announcementbox span.title {
164 font-weight: bold;
164 font-weight: bold;
165 color: #224455;
165 color: #224455;
166 padding-left: 10px;
166 padding-left: 10px;
167 line-height: 1.6em; }
167 line-height: 1.6em; }
168 .announcementbox .announcement {
168 .announcementbox .announcement {
169 margin: 2px;
169 margin: 2px;
170 background: white;
170 background: white;
171 padding: 1px;
171 padding: 1px;
172 padding-left: 10px;
172 padding-left: 10px;
173 padding-right: 10px;
173 padding-right: 10px;
174 padding-top: 5px;
174 padding-top: 5px;
175 padding-bottom: 5px; }
175 padding-bottom: 5px; }
176 .announcementbox .announcement p {
176 .announcementbox .announcement p {
177 font-size: 12px;
177 font-size: 12px;
178 margin: 2px; }
178 margin: 2px; }
179
179
180 .pub-info {
180 .pub-info {
181 text-align: right;
181 text-align: right;
182 font-style: italic;
182 font-style: italic;
183 font-size: 9px; }
183 font-size: 9px; }
184 .pub-info p {
184 .pub-info p {
185 text-align: right;
185 text-align: right;
186 font-style: italic;
186 font-style: italic;
187 font-size: 9px; }
187 font-size: 9px; }
188
188
189 .announcement .toggles {
189 .announcement .toggles {
190 font-weight: normal;
190 font-weight: normal;
191 float: right;
191 float: right;
192 font-size: 80%; }
192 font-size: 80%; }
193 .announcement .announcement-title {
193 .announcement .announcement-title {
194 font-weight: bold; }
194 font-weight: bold; }
195
195
196 .message {
196 .message {
197 margin: 10px 0 0; }
197 margin: 10px 0 0; }
198 .message .message {
198 .message .message {
199 margin: 0 0 0 30px; }
199 margin: 0 0 0 30px; }
200 .message .message .stat {
200 .message .message .stat {
201 font-size: 10px;
201 font-size: 10px;
202 line-height: 1.75em;
202 line-height: 1.75em;
203 padding: 0 5px;
203 padding: 0 5px;
204 color: #444444;
204 color: #444444;
205 background: #bbbbbb;
205 background: #bbbbbb;
206 font-weight: bold; }
206 font-weight: bold; }
207 .message .body {
207 .message .body {
208 border: 2px solid #dddddd;
208 border: 2px solid #dddddd;
209 background: #fff8f8;
209 background: #fff8f8;
210 padding-left: 5px; }
210 padding-left: 5px; }
211 .message .reply-body {
211 .message .reply-body {
212 border: 2px solid #bbbbbb;
212 border: 2px solid #bbbbbb;
213 background: #fffff8;
213 background: #fffff8;
214 padding-left: 5px; }
214 padding-left: 5px; }
215 .message .stat {
215 .message .stat {
216 font-size: 10px;
216 font-size: 10px;
217 line-height: 1.75em;
217 line-height: 1.75em;
218 padding: 0 5px;
218 padding: 0 5px;
219 color: #333333;
219 color: #333333;
220 background: #dddddd;
220 background: #dddddd;
221 font-weight: bold; }
221 font-weight: bold; }
222
222
223 .contest-title {
223 .contest-title {
224 color: white;
224 color: white;
225 text-align: center;
225 text-align: center;
226 line-height: 2em; }
226 line-height: 2em; }
227
227
228 .registration-desc {
228 .registration-desc {
229 border: 1px dotted gray;
229 border: 1px dotted gray;
230 background: #f5f5f5;
230 background: #f5f5f5;
231 padding: 5px;
231 padding: 5px;
232 margin: 10px 0;
232 margin: 10px 0;
233 font-size: 12px;
233 font-size: 12px;
234 line-height: 1.5em; }
234 line-height: 1.5em; }
235
235
236 .test-desc {
236 .test-desc {
237 border: 1px dotted gray;
237 border: 1px dotted gray;
238 background: #f5f5f5;
238 background: #f5f5f5;
239 padding: 5px;
239 padding: 5px;
240 margin: 10px 0;
240 margin: 10px 0;
241 font-size: 12px;
241 font-size: 12px;
242 line-height: 1.5em; }
242 line-height: 1.5em; }
243
243
244 .problem-list {
244 .problem-list {
245 width: 200px;
245 width: 200px;
246 float: left; }
246 float: left; }
247
247
248 .problem-bar {
248 .problem-bar {
249 margin-top: 5px;
249 margin-top: 5px;
250 padding: 5px;
250 padding: 5px;
251 background: #e0e0e0; }
251 background: #e0e0e0; }
252 .problem-bar span.problem-title {
252 .problem-bar span.problem-title {
253 font-weight: bold;
253 font-weight: bold;
254 font-size: 110%; }
254 font-size: 110%; }
255
255
256 .problem-content {
256 .problem-content {
257 float: left;
257 float: left;
258 margin-left: 10px;
258 margin-left: 10px;
259 width: 700px; }
259 width: 700px; }
260
260
261 .problem-panel {
261 .problem-panel {
262 border: 1px black solid;
262 border: 1px black solid;
263 padding: 5px; }
263 padding: 5px; }
264 .problem-panel .problem-form {
264 .problem-panel .problem-form {
265 border: 1px dotted #99aaee;
265 border: 1px dotted #99aaee;
266 background: #eeeeff; }
266 background: #eeeeff; }
267 +
268 + .notice-bar {
269 + margin-top: 3px;
270 + margin-bottom: 3px;
271 + text-align: center; }
272 + .notice-bar span.notice {
273 + color: white;
274 + font-weight: bold;
275 + background: #000070;
276 + padding: 3px 20px 3px 20px;
277 + -moz-border-radius: 2px;
278 + -webkit-border-radius: 5px;
279 + border-radius: 5px; }
@@ -121,192 +121,206
121 font-weight: bold
121 font-weight: bold
122
122
123
123
124 table.uinfo
124 table.uinfo
125 border-collapse: collapse
125 border-collapse: collapse
126 border: 1px solid black
126 border: 1px solid black
127 font-size: 13px
127 font-size: 13px
128
128
129
129
130 td.uinfo
130 td.uinfo
131 vertical-align: top
131 vertical-align: top
132 border: 1px solid black
132 border: 1px solid black
133 padding: 5px
133 padding: 5px
134
134
135
135
136 th.uinfo
136 th.uinfo
137 background: lightgreen
137 background: lightgreen
138 vertical-align: top
138 vertical-align: top
139 text-align: right
139 text-align: right
140 border: 1px solid black
140 border: 1px solid black
141 padding: 5px
141 padding: 5px
142
142
143
143
144 .compilermsgbody
144 .compilermsgbody
145 font-family: monospace
145 font-family: monospace
146
146
147 .task-menu
147 .task-menu
148 text-align: center
148 text-align: center
149 font-size: 13px
149 font-size: 13px
150 line-height: 1.75em
150 line-height: 1.75em
151 font-weight: bold
151 font-weight: bold
152 border-top: 1px dashed gray
152 border-top: 1px dashed gray
153 border-bottom: 1px dashed gray
153 border-bottom: 1px dashed gray
154 margin-top: 2px
154 margin-top: 2px
155 margin-bottom: 4px
155 margin-bottom: 4px
156
156
157
157
158 table.taskdesc
158 table.taskdesc
159 border: 2px solid #dddddd
159 border: 2px solid #dddddd
160 border-collapse: collapse
160 border-collapse: collapse
161 margin: 10px auto
161 margin: 10px auto
162 width: 90%
162 width: 90%
163 font-size: 13px
163 font-size: 13px
164
164
165 p
165 p
166 font-size: 13px
166 font-size: 13px
167
167
168 tr.name
168 tr.name
169 border: 2px solid #dddddd
169 border: 2px solid #dddddd
170 background: #dddddd
170 background: #dddddd
171 color: #333333
171 color: #333333
172 font-weight: bold
172 font-weight: bold
173 font-size: 14px
173 font-size: 14px
174 line-height: 1.5em
174 line-height: 1.5em
175 text-align: center
175 text-align: center
176
176
177 td
177 td
178 &.desc-odd
178 &.desc-odd
179 padding: 5px
179 padding: 5px
180 padding-left: 20px
180 padding-left: 20px
181 background: #fefeee
181 background: #fefeee
182
182
183 &.desc-even
183 &.desc-even
184 padding: 5px
184 padding: 5px
185 padding-left: 20px
185 padding-left: 20px
186 background: #feeefe
186 background: #feeefe
187
187
188
188
189 .announcementbox
189 .announcementbox
190 margin: 10px 0px
190 margin: 10px 0px
191 background: #bbddee
191 background: #bbddee
192 padding: 1px
192 padding: 1px
193
193
194 span.title
194 span.title
195 font-weight: bold
195 font-weight: bold
196 color: #224455
196 color: #224455
197 padding-left: 10px
197 padding-left: 10px
198 line-height: 1.6em
198 line-height: 1.6em
199
199
200 .announcement
200 .announcement
201 margin: 2px
201 margin: 2px
202 background: white
202 background: white
203 padding: 1px
203 padding: 1px
204 padding-left: 10px
204 padding-left: 10px
205 padding-right: 10px
205 padding-right: 10px
206 padding-top: 5px
206 padding-top: 5px
207 padding-bottom: 5px
207 padding-bottom: 5px
208
208
209 p
209 p
210 font-size: 12px
210 font-size: 12px
211 margin: 2px
211 margin: 2px
212
212
213
213
214 .pub-info
214 .pub-info
215 text-align: right
215 text-align: right
216 font-style: italic
216 font-style: italic
217 font-size: 9px
217 font-size: 9px
218
218
219 p
219 p
220 text-align: right
220 text-align: right
221 font-style: italic
221 font-style: italic
222 font-size: 9px
222 font-size: 9px
223
223
224
224
225 .announcement
225 .announcement
226 .toggles
226 .toggles
227 font-weight: normal
227 font-weight: normal
228 float: right
228 float: right
229 font-size: 80%
229 font-size: 80%
230
230
231 .announcement-title
231 .announcement-title
232 font-weight: bold
232 font-weight: bold
233
233
234
234
235 .message
235 .message
236 margin: 10px 0 0
236 margin: 10px 0 0
237
237
238 .message
238 .message
239 margin: 0 0 0 30px
239 margin: 0 0 0 30px
240
240
241 .stat
241 .stat
242 font-size: 10px
242 font-size: 10px
243 line-height: 1.75em
243 line-height: 1.75em
244 padding: 0 5px
244 padding: 0 5px
245 color: #444444
245 color: #444444
246 background: #bbbbbb
246 background: #bbbbbb
247 font-weight: bold
247 font-weight: bold
248
248
249 .body
249 .body
250 border: 2px solid #dddddd
250 border: 2px solid #dddddd
251 background: #fff8f8
251 background: #fff8f8
252 padding-left: 5px
252 padding-left: 5px
253
253
254 .reply-body
254 .reply-body
255 border: 2px solid #bbbbbb
255 border: 2px solid #bbbbbb
256 background: #fffff8
256 background: #fffff8
257 padding-left: 5px
257 padding-left: 5px
258
258
259 .stat
259 .stat
260 font-size: 10px
260 font-size: 10px
261 line-height: 1.75em
261 line-height: 1.75em
262 padding: 0 5px
262 padding: 0 5px
263 color: #333333
263 color: #333333
264 background: #dddddd
264 background: #dddddd
265 font-weight: bold
265 font-weight: bold
266
266
267 .contest-title
267 .contest-title
268 color: white
268 color: white
269 text-align: center
269 text-align: center
270 line-height: 2em
270 line-height: 2em
271
271
272 .registration-desc
272 .registration-desc
273 border: 1px dotted gray
273 border: 1px dotted gray
274 background: #f5f5f5
274 background: #f5f5f5
275 padding: 5px
275 padding: 5px
276 margin: 10px 0
276 margin: 10px 0
277 font-size: 12px
277 font-size: 12px
278 line-height: 1.5em
278 line-height: 1.5em
279
279
280 .test-desc
280 .test-desc
281 border: 1px dotted gray
281 border: 1px dotted gray
282 background: #f5f5f5
282 background: #f5f5f5
283 padding: 5px
283 padding: 5px
284 margin: 10px 0
284 margin: 10px 0
285 font-size: 12px
285 font-size: 12px
286 line-height: 1.5em
286 line-height: 1.5em
287
287
288 .problem-list
288 .problem-list
289 width: 200px
289 width: 200px
290 float: left
290 float: left
291
291
292 .problem-bar
292 .problem-bar
293 margin-top: 5px
293 margin-top: 5px
294 padding: 5px
294 padding: 5px
295 background: #e0e0e0
295 background: #e0e0e0
296
296
297 span.problem-title
297 span.problem-title
298 font-weight: bold
298 font-weight: bold
299 font-size: 110%
299 font-size: 110%
300
300
301 .problem-content
301 .problem-content
302 float: left
302 float: left
303 margin-left: 10px
303 margin-left: 10px
304 width: 700px
304 width: 700px
305
305
306 .problem-panel
306 .problem-panel
307 border: 1px black solid
307 border: 1px black solid
308 padding: 5px
308 padding: 5px
309
309
310 .problem-form
310 .problem-form
311 border: 1px dotted #99aaee
311 border: 1px dotted #99aaee
312 background: #eeeeff
312 background: #eeeeff
313 +
314 + .notice-bar
315 + margin-top: 3px
316 + margin-bottom: 3px
317 + text-align: center
318 +
319 + span.notice
320 + color: white
321 + font-weight: bold
322 + background: #000070
323 + padding: 3px 20px 3px 20px
324 + -moz-border-radius: 2px
325 + -webkit-border-radius: 5px
326 + border-radius: 5px
You need to be logged in to leave comments. Login now