Description:
controllers get available problems from current user
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r288:74436ad04ad0 - - 3 files changed: 42 inserted, 45 deleted

@@ -1,386 +1,345
1 1 class MainController < ApplicationController
2 2
3 3 before_filter :authenticate, :except => [:index, :login]
4 4 before_filter :check_viewability, :except => [:index, :login]
5 5
6 6 append_before_filter :update_user_start_time, :except => [:index, :login]
7 7
8 8 # to prevent log in box to be shown when user logged out of the
9 9 # system only in some tab
10 10 prepend_before_filter :reject_announcement_refresh_when_logged_out, :only => [:announcements]
11 11
12 12 # COMMENTED OUT: filter in each action instead
13 13 # before_filter :verify_time_limit, :only => [:submit]
14 14
15 15 verify :method => :post, :only => [:submit],
16 16 :redirect_to => { :action => :index }
17 17
18 18 # COMMENT OUT: only need when having high load
19 19 # caches_action :index, :login
20 20
21 21 # NOTE: This method is not actually needed, 'config/routes.rb' has
22 22 # assigned action login as a default action.
23 23 def index
24 24 redirect_to :action => 'login'
25 25 end
26 26
27 27 def login
28 28 saved_notice = flash[:notice]
29 29 reset_session
30 30 flash.now[:notice] = saved_notice
31 31
32 32 # EXPERIMENT:
33 33 # Hide login if in single user mode and the url does not
34 34 # explicitly specify /login
35 35 #
36 36 # logger.info "PATH: #{request.path}"
37 37 # if Configuration['system.single_user_mode'] and
38 38 # request.path!='/main/login'
39 39 # @hidelogin = true
40 40 # end
41 41
42 42 @announcements = Announcement.find_for_frontpage
43 43 render :action => 'login', :layout => 'empty'
44 44 end
45 45
46 46 def list
47 47 prepare_list_information
48 48 end
49 49
50 50 def help
51 51 @user = User.find(session[:user_id])
52 52 end
53 53
54 54 def submit
55 55 user = User.find(session[:user_id])
56 56
57 57 @submission = Submission.new(params[:submission])
58 58 @submission.user = user
59 59 @submission.language_id = 0
60 60 if (params['file']) and (params['file']!='')
61 61 @submission.source = params['file'].read
62 62 @submission.source_filename = params['file'].original_filename
63 63 end
64 64 @submission.submitted_at = Time.new.gmtime
65 65
66 66 if Configuration.time_limit_mode? and user.contest_finished?
67 67 @submission.errors.add_to_base "The contest is over."
68 68 prepare_list_information
69 69 render :action => 'list' and return
70 70 end
71 71
72 72 if @submission.valid?
73 73 if @submission.save == false
74 74 flash[:notice] = 'Error saving your submission'
75 75 elsif Task.create(:submission_id => @submission.id,
76 76 :status => Task::STATUS_INQUEUE) == false
77 77 flash[:notice] = 'Error adding your submission to task queue'
78 78 end
79 79 else
80 80 prepare_list_information
81 81 render :action => 'list' and return
82 82 end
83 83 redirect_to :action => 'list'
84 84 end
85 85
86 86 def source
87 87 submission = Submission.find(params[:id])
88 88 if submission.user_id == session[:user_id]
89 89 send_data(submission.source,
90 90 {:filename => submission.download_filename,
91 91 :type => 'text/plain'})
92 92 else
93 93 flash[:notice] = 'Error viewing source'
94 94 redirect_to :action => 'list'
95 95 end
96 96 end
97 97
98 98 def compiler_msg
99 99 @submission = Submission.find(params[:id])
100 100 if @submission.user_id == session[:user_id]
101 101 render :action => 'compiler_msg', :layout => 'empty'
102 102 else
103 103 flash[:notice] = 'Error viewing source'
104 104 redirect_to :action => 'list'
105 105 end
106 106 end
107 107
108 108 def submission
109 109 @user = User.find(session[:user_id])
110 - @problems = Problem.find_available_problems
110 + @problems = @user.available_problems
111 111 if params[:id]==nil
112 112 @problem = nil
113 113 @submissions = nil
114 114 else
115 115 @problem = Problem.find_by_name(params[:id])
116 116 if not @problem.available
117 117 redirect_to :action => 'list'
118 118 flash[:notice] = 'Error: submissions for that problem are not viewable.'
119 119 return
120 120 end
121 121 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
122 122 end
123 123 end
124 124
125 125 def result
126 126 if !Configuration.show_grading_result
127 127 redirect_to :action => 'list' and return
128 128 end
129 129 @user = User.find(session[:user_id])
130 130 @submission = Submission.find(params[:id])
131 131 if @submission.user!=@user
132 132 flash[:notice] = 'You are not allowed to view result of other users.'
133 133 redirect_to :action => 'list' and return
134 134 end
135 135 prepare_grading_result(@submission)
136 136 end
137 137
138 138 def load_output
139 139 if !Configuration.show_grading_result or params[:num]==nil
140 140 redirect_to :action => 'list' and return
141 141 end
142 142 @user = User.find(session[:user_id])
143 143 @submission = Submission.find(params[:id])
144 144 if @submission.user!=@user
145 145 flash[:notice] = 'You are not allowed to view result of other users.'
146 146 redirect_to :action => 'list' and return
147 147 end
148 148 case_num = params[:num].to_i
149 149 out_filename = output_filename(@user.login,
150 150 @submission.problem.name,
151 151 @submission.id,
152 152 case_num)
153 153 if !FileTest.exists?(out_filename)
154 154 flash[:notice] = 'Output not found.'
155 155 redirect_to :action => 'list' and return
156 156 end
157 157
158 158 response.headers['Content-Type'] = "application/force-download"
159 159 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
160 160 response.headers["X-Sendfile"] = out_filename
161 161 response.headers['Content-length'] = File.size(out_filename)
162 162 render :nothing => true
163 163 end
164 164
165 165 def error
166 166 @user = User.find(session[:user_id])
167 167 end
168 168
169 169 # announcement refreshing and hiding methods
170 170
171 171 def announcements
172 172 if params.has_key? 'recent'
173 173 prepare_announcements(params[:recent])
174 174 else
175 175 prepare_announcements
176 176 end
177 177 render(:partial => 'announcement',
178 178 :collection => @announcements,
179 179 :locals => {:announcement_effect => true})
180 180 end
181 181
182 182 protected
183 183
184 184 def prepare_announcements(recent=nil)
185 185 if Configuration.show_tasks_to?(@user)
186 186 @announcements = Announcement.find_published(true)
187 187 else
188 188 @announcements = Announcement.find_published
189 189 end
190 190 if recent!=nil
191 191 recent_id = recent.to_i
192 192 @announcements = @announcements.find_all { |a| a.id > recent_id }
193 193 end
194 194 end
195 195
196 - def problem_list_by_user_contests(user)
197 - contest_problems = []
198 - pin = {}
199 - user.contests.enabled.each do |contest|
200 - available_problems = contest.problems.available
201 - contest_problems << {
202 - :contest => contest,
203 - :problems => available_problems
204 - }
205 - available_problems.each {|p| pin[p.id] = true}
206 - end
207 - other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
208 - contest_problems << {
209 - :contest => nil,
210 - :problems => other_avaiable_problems
211 - }
212 - return contest_problems
213 - end
214 -
215 - def problem_list_for_user(user, contest_problems=nil)
216 - if not Configuration.multicontests?
217 - return Problem.find_available_problems
218 - else
219 - if contest_problems==nil
220 - contest_problems = problem_list_by_user_contests(user)
221 - end
222 -
223 - problems = []
224 - collected = {}
225 - contest_problems.each do |cp|
226 - cp[:problems].each do |problem|
227 - if not collected[problem.id]
228 - problems << problem
229 - collected[problem.id] = true
230 - end
231 - end
232 - end
233 - return problems
234 - end
235 - end
236 -
237 196 def prepare_list_information
238 197 @user = User.find(session[:user_id])
239 198 if not Configuration.multicontests?
240 199 @problems = problem_list_for_user(@user)
241 200 else
242 - @contest_problems = problem_list_by_user_contests(@user)
243 - @problems = problem_list_for_user(@user, @contest_problems)
201 + @contest_problems = @user.available_problems_group_by_contests
202 + @problems = @user.available_problems
244 203 end
245 204 @prob_submissions = {}
246 205 @problems.each do |p|
247 206 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
248 207 if sub!=nil
249 208 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
250 209 else
251 210 @prob_submissions[p.id] = { :count => 0, :submission => nil }
252 211 end
253 212 end
254 213 prepare_announcements
255 214 end
256 215
257 216 def check_viewability
258 217 @user = User.find(session[:user_id])
259 218 if (!Configuration.show_tasks_to?(@user)) and
260 219 ((action_name=='submission') or (action_name=='submit'))
261 220 redirect_to :action => 'list' and return
262 221 end
263 222 end
264 223
265 224 def prepare_grading_result(submission)
266 225 if Configuration.task_grading_info.has_key? submission.problem.name
267 226 grading_info = Configuration.task_grading_info[submission.problem.name]
268 227 else
269 228 # guess task info from problem.full_score
270 229 cases = submission.problem.full_score / 10
271 230 grading_info = {
272 231 'testruns' => cases,
273 232 'testcases' => cases
274 233 }
275 234 end
276 235 @test_runs = []
277 236 if grading_info['testruns'].is_a? Integer
278 237 trun_count = grading_info['testruns']
279 238 trun_count.times do |i|
280 239 @test_runs << [ read_grading_result(@user.login,
281 240 submission.problem.name,
282 241 submission.id,
283 242 i+1) ]
284 243 end
285 244 else
286 245 grading_info['testruns'].keys.sort.each do |num|
287 246 run = []
288 247 testrun = grading_info['testruns'][num]
289 248 testrun.each do |c|
290 249 run << read_grading_result(@user.login,
291 250 submission.problem.name,
292 251 submission.id,
293 252 c)
294 253 end
295 254 @test_runs << run
296 255 end
297 256 end
298 257 end
299 258
300 259 def grading_result_dir(user_name, problem_name, submission_id, case_num)
301 260 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
302 261 end
303 262
304 263 def output_filename(user_name, problem_name, submission_id, case_num)
305 264 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
306 265 return "#{dir}/output.txt"
307 266 end
308 267
309 268 def read_grading_result(user_name, problem_name, submission_id, case_num)
310 269 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
311 270 result_file_name = "#{dir}/result"
312 271 if !FileTest.exists?(result_file_name)
313 272 return {:num => case_num, :msg => 'program did not run'}
314 273 else
315 274 results = File.open(result_file_name).readlines
316 275 run_stat = extract_running_stat(results)
317 276 output_filename = "#{dir}/output.txt"
318 277 if FileTest.exists?(output_filename)
319 278 output_file = true
320 279 output_size = File.size(output_filename)
321 280 else
322 281 output_file = false
323 282 output_size = 0
324 283 end
325 284
326 285 return {
327 286 :num => case_num,
328 287 :msg => results[0],
329 288 :run_stat => run_stat,
330 289 :output => output_file,
331 290 :output_size => output_size
332 291 }
333 292 end
334 293 end
335 294
336 295 # copied from grader/script/lib/test_request_helper.rb
337 296 def extract_running_stat(results)
338 297 running_stat_line = results[-1]
339 298
340 299 # extract exit status line
341 300 run_stat = ""
342 301 if !(/[Cc]orrect/.match(results[0]))
343 302 run_stat = results[0].chomp
344 303 else
345 304 run_stat = 'Program exited normally'
346 305 end
347 306
348 307 logger.info "Stat line: #{running_stat_line}"
349 308
350 309 # extract running time
351 310 if res = /r(.*)u(.*)s/.match(running_stat_line)
352 311 seconds = (res[1].to_f + res[2].to_f)
353 312 time_stat = "Time used: #{seconds} sec."
354 313 else
355 314 seconds = nil
356 315 time_stat = "Time used: n/a sec."
357 316 end
358 317
359 318 # extract memory usage
360 319 if res = /s(.*)m/.match(running_stat_line)
361 320 memory_used = res[1].to_i
362 321 else
363 322 memory_used = -1
364 323 end
365 324
366 325 return {
367 326 :msg => "#{run_stat}\n#{time_stat}",
368 327 :running_time => seconds,
369 328 :exit_status => run_stat,
370 329 :memory_usage => memory_used
371 330 }
372 331 end
373 332
374 333 def update_user_start_time
375 334 user = User.find(session[:user_id])
376 335 user.update_start_time
377 336 end
378 337
379 338 def reject_announcement_refresh_when_logged_out
380 339 if not session[:user_id]
381 340 render :text => 'Access forbidden', :status => 403
382 341 end
383 342 end
384 343
385 344 end
386 345
@@ -1,75 +1,75
1 1 class TasksController < ApplicationController
2 2
3 3 before_filter :authenticate, :check_viewability
4 4
5 5 def index
6 6 redirect_to :action => 'list'
7 7 end
8 8
9 9 def list
10 - @problems = Problem.find_available_problems
10 + @problems = @user.available_problems
11 11 end
12 12
13 13 # this has contest-wide access control
14 14 def view
15 15 base_name = params[:file]
16 16 base_filename = File.basename("#{base_name}.#{params[:ext]}")
17 17 filename = "#{Problem.download_file_basedir}/#{base_filename}"
18 18
19 19 if !FileTest.exists?(filename)
20 20 redirect_to :action => 'index' and return
21 21 end
22 22
23 23 send_file_to_user(filename, base_filename)
24 24 end
25 25
26 26 # this has problem-level access control
27 27 def download
28 28 problem = Problem.find(params[:id])
29 29 if !problem or !problem.available or !@user.can_view_problem? problem
30 30 redirect_to :action => 'index' and return
31 31 end
32 32
33 33 base_name = params[:file]
34 34 base_filename = File.basename("#{base_name}.#{params[:ext]}")
35 35 filename = "#{Problem.download_file_basedir}/#{params[:id]}/#{base_filename}"
36 36 puts "SENDING: #{filename}"
37 37
38 38 if !FileTest.exists?(filename)
39 39 redirect_to :action => 'index' and return
40 40 end
41 41
42 42 puts "SENDING: #{filename}"
43 43
44 44 send_file_to_user(filename, base_filename)
45 45 end
46 46
47 47 protected
48 48
49 49 def send_file_to_user(filename, base_filename)
50 50 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
51 51 response.headers['Content-Type'] = "application/force-download"
52 52 response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filename)}\""
53 53 response.headers["X-Sendfile"] = filename
54 54 response.headers['Content-length'] = File.size(filename)
55 55 render :nothing => true
56 56 else
57 57 if params[:ext]=='pdf'
58 58 content_type = 'application/pdf'
59 59 else
60 60 content_type = 'application/octet-stream'
61 61 end
62 62
63 63 send_file filename, :stream => false, :filename => base_filename, :type => content_type
64 64 end
65 65 end
66 66
67 67 def check_viewability
68 68 @user = User.find(session[:user_id])
69 69 if @user==nil or !Configuration.show_tasks_to?(@user)
70 70 redirect_to :controller => 'main', :action => 'list'
71 71 return false
72 72 end
73 73 end
74 74
75 75 end
@@ -8,254 +8,292
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_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
20 20
21 21 belongs_to :site
22 22 belongs_to :country
23 23
24 24 has_and_belongs_to_many :contests, :uniq => true, :order => 'name'
25 25
26 26 named_scope :activated_users, :conditions => {:activated => true}
27 27
28 28 validates_presence_of :login
29 29 validates_uniqueness_of :login
30 30 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
31 31 validates_length_of :login, :within => 3..30
32 32
33 33 validates_presence_of :full_name
34 34 validates_length_of :full_name, :minimum => 1
35 35
36 36 validates_presence_of :password, :if => :password_required?
37 37 validates_length_of :password, :within => 4..20, :if => :password_required?
38 38 validates_confirmation_of :password, :if => :password_required?
39 39
40 40 validates_format_of :email,
41 41 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
42 42 :if => :email_validation?
43 43 validate :uniqueness_of_email_from_activated_users,
44 44 :if => :email_validation?
45 45 validate :enough_time_interval_between_same_email_registrations,
46 46 :if => :email_validation?
47 47
48 48 # these are for ytopc
49 49 # disable for now
50 50 #validates_presence_of :province
51 51
52 52 attr_accessor :password
53 53
54 54 before_save :encrypt_new_password
55 55 before_save :assign_default_site
56 56
57 57 def self.authenticate(login, password)
58 58 user = find_by_login(login)
59 59 return user if user && user.authenticated?(password)
60 60 end
61 61
62 62 def authenticated?(password)
63 63 if self.activated
64 64 hashed_password == User.encrypt(password,self.salt)
65 65 else
66 66 false
67 67 end
68 68 end
69 69
70 70 def admin?
71 71 self.roles.detect {|r| r.name == 'admin' }
72 72 end
73 73
74 74 def email_for_editing
75 75 if self.email==nil
76 76 "(unknown)"
77 77 elsif self.email==''
78 78 "(blank)"
79 79 else
80 80 self.email
81 81 end
82 82 end
83 83
84 84 def email_for_editing=(e)
85 85 self.email=e
86 86 end
87 87
88 88 def alias_for_editing
89 89 if self.alias==nil
90 90 "(unknown)"
91 91 elsif self.alias==''
92 92 "(blank)"
93 93 else
94 94 self.alias
95 95 end
96 96 end
97 97
98 98 def alias_for_editing=(e)
99 99 self.alias=e
100 100 end
101 101
102 102 def activation_key
103 103 if self.hashed_password==nil
104 104 encrypt_new_password
105 105 end
106 106 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
107 107 end
108 108
109 109 def verify_activation_key(key)
110 110 key == activation_key
111 111 end
112 112
113 113 def self.random_password(length=5)
114 114 chars = 'abcdefghjkmnopqrstuvwxyz'
115 115 password = ''
116 116 length.times { password << chars[rand(chars.length - 1)] }
117 117 password
118 118 end
119 119
120 120 def self.find_non_admin_with_prefix(prefix='')
121 121 users = User.find(:all)
122 122 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
123 123 end
124 124
125 125 # Contest information
126 126
127 127 def contest_time_left
128 128 if Configuration.contest_mode?
129 129 return nil if site==nil
130 130 return site.time_left
131 131 elsif Configuration.indv_contest_mode?
132 132 time_limit = Configuration.contest_time_limit
133 133 if time_limit == nil
134 134 return nil
135 135 end
136 136 if contest_stat==nil
137 137 return (Time.now.gmtime + time_limit) - Time.now.gmtime
138 138 else
139 139 finish_time = contest_stat.started_at + time_limit
140 140 current_time = Time.now.gmtime
141 141 if current_time > finish_time
142 142 return 0
143 143 else
144 144 return finish_time - current_time
145 145 end
146 146 end
147 147 else
148 148 return nil
149 149 end
150 150 end
151 151
152 152 def contest_finished?
153 153 if Configuration.contest_mode?
154 154 return false if site==nil
155 155 return site.finished?
156 156 elsif Configuration.indv_contest_mode?
157 157 time_limit = Configuration.contest_time_limit
158 158
159 159 return false if contest_stat==nil
160 160
161 161 return contest_time_left == 0
162 162 else
163 163 return false
164 164 end
165 165 end
166 166
167 167 def contest_started?
168 168 if Configuration.contest_mode?
169 169 return true if site==nil
170 170 return site.started
171 171 else
172 172 return true
173 173 end
174 174 end
175 175
176 176 def update_start_time
177 177 stat = self.contest_stat
178 178 if stat == nil
179 179 stat = UserContestStat.new(:user => self,
180 180 :started_at => Time.now.gmtime)
181 181 stat.save
182 182 end
183 183 end
184 184
185 185 def problem_in_user_contests?(problem)
186 186 problem_contests = problem.contests.all
187 187
188 188 if problem_contests.length == 0 # this is public contest
189 189 return true
190 190 end
191 191
192 192 contests.each do |contest|
193 193 if problem_contests.find {|c| c.id == contest.id }
194 194 return true
195 195 end
196 196 end
197 197 return false
198 198 end
199 199
200 + def available_problems_group_by_contests
201 + contest_problems = []
202 + pin = {}
203 + contests.enabled.each do |contest|
204 + available_problems = contest.problems.available
205 + contest_problems << {
206 + :contest => contest,
207 + :problems => available_problems
208 + }
209 + available_problems.each {|p| pin[p.id] = true}
210 + end
211 + other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
212 + contest_problems << {
213 + :contest => nil,
214 + :problems => other_avaiable_problems
215 + }
216 + return contest_problems
217 + end
218 +
219 + def available_problems
220 + if not Configuration.multicontests?
221 + return Problem.find_available_problems
222 + else
223 + contest_problems = []
224 + pin = {}
225 + contests.enabled.each do |contest|
226 + contest.problems.available.each do |problem|
227 + if not pin.has_key? problem.id
228 + contest_problems << problem
229 + end
230 + pin[problem.id] = true
231 + end
232 + end
233 + other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
234 + return contest_problems + other_avaiable_problems
235 + end
236 + end
237 +
200 238 def can_view_problem?(problem)
201 239 if not Configuration.multicontests?
202 240 return problem.available
203 241 else
204 242 return problem_in_user_contests? problem
205 243 end
206 244 end
207 245
208 246 protected
209 247 def encrypt_new_password
210 248 return if password.blank?
211 249 self.salt = (10+rand(90)).to_s
212 250 self.hashed_password = User.encrypt(self.password,self.salt)
213 251 end
214 252
215 253 def assign_default_site
216 254 # have to catch error when migrating (because self.site is not available).
217 255 begin
218 256 if self.site==nil
219 257 self.site = Site.find_by_name('default')
220 258 if self.site==nil
221 259 self.site = Site.find(1) # when 'default has be renamed'
222 260 end
223 261 end
224 262 rescue
225 263 end
226 264 end
227 265
228 266 def password_required?
229 267 self.hashed_password.blank? || !self.password.blank?
230 268 end
231 269
232 270 def self.encrypt(string,salt)
233 271 Digest::SHA1.hexdigest(salt + string)
234 272 end
235 273
236 274 def uniqueness_of_email_from_activated_users
237 275 user = User.activated_users.find_by_email(self.email)
238 276 if user and (user.login != self.login)
239 277 self.errors.add_to_base("Email has already been taken")
240 278 end
241 279 end
242 280
243 281 def enough_time_interval_between_same_email_registrations
244 282 return if !self.new_record?
245 283 return if self.activated
246 284 open_user = User.find_by_email(self.email,
247 285 :order => 'created_at DESC')
248 286 if open_user and open_user.created_at and
249 287 (open_user.created_at > Time.now.gmtime - 5.minutes)
250 288 self.errors.add_to_base("There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
251 289 end
252 290 end
253 291
254 292 def email_validation?
255 293 begin
256 294 return VALIDATE_USER_EMAILS
257 295 rescue
258 296 return false
259 297 end
260 298 end
261 299 end
You need to be logged in to leave comments. Login now