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