Description:
fixed indv contest timing bug (same as in codejom), added user contest stat reset
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r247:bdb708ab847b - - 4 files changed: 16 inserted, 2 deleted

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