Description:
logs out users after contests changed
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r294:069c049fbc3a - - 22 files changed: 191 inserted, 192 deleted

@@ -0,0 +1,87
1 + require 'spec_helper'
2 + require 'config_spec_helper'
3 + require 'delorean'
4 +
5 + describe "ContestManagements" do
6 + include ConfigSpecHelperMethods
7 +
8 + fixtures :users
9 + fixtures :problems
10 + fixtures :contests
11 + fixtures :roles
12 +
13 + before(:each) do
14 + @admin_user = users(:mary)
15 + @contest_b = contests(:contest_b)
16 + @james = users(:james)
17 + @jack = users(:jack)
18 +
19 + set_contest_time_limit('3:00')
20 + set_indv_contest_mode
21 + end
22 +
23 + it "should reset users' timer when their contests change" do
24 + james_session = open_session
25 + james_session.extend(MainSessionMethods)
26 +
27 + james_login_and_get_main_list(james_session)
28 + james_session.response.should_not have_text(/OVER/)
29 +
30 + Delorean.time_travel_to(190.minutes.since) do
31 + james_session.get_main_list
32 + james_session.response.should have_text(/OVER/)
33 +
34 + james_session.get '/' # logout
35 + james_session.get '/main/list' # clearly log out
36 + james_session.response.should_not render_template 'main/list'
37 +
38 + admin_change_users_contest_to("james", @contest_b, true)
39 +
40 + james_login_and_get_main_list(james_session)
41 + james_session.response.should_not have_text(/OVER/)
42 + end
43 + end
44 +
45 + private
46 +
47 + module MainSessionMethods
48 + def login(login_name, password)
49 + post '/login/login', :login => login_name, :password => password
50 + assert_redirected_to '/main/list'
51 + end
52 +
53 + def get_main_list
54 + get '/main/list'
55 + assert_template 'main/list'
56 + end
57 + end
58 +
59 + module ContestManagementSessionMethods
60 + def change_users_contest_to(user_login_list, contest, reset_timer=false)
61 + post_data = {
62 + :contest => {:id => contest.id},
63 + :operation => 'assign',
64 + :login_list => user_login_list
65 + }
66 + post_data[:reset_timer] = true if reset_timer
67 + post '/user_admin/manage_contest', post_data
68 + end
69 + end
70 +
71 + def admin_change_users_contest_to(user_list, contest, reset_timer)
72 + admin_session = open_session
73 + admin_session.extend(MainSessionMethods)
74 + admin_session.extend(ContestManagementSessionMethods)
75 +
76 + admin_session.login('mary','goodbye')
77 + admin_session.get '/main/list'
78 + admin_session.change_users_contest_to(user_list, contest, reset_timer)
79 + end
80 +
81 + def james_login_and_get_main_list(session)
82 + session.login('james', 'morning')
83 + session.get_main_list
84 + end
85 +
86 + end
87 +
@@ -103,225 +103,241
103 103 render :action => 'edit'
104 104 end
105 105 end
106 106
107 107 def destroy
108 108 User.find(params[:id]).destroy
109 109 redirect_to :action => 'list'
110 110 end
111 111
112 112 def user_stat
113 113 @problems = Problem.find_available_problems
114 114 @users = User.find(:all)
115 115 @scorearray = Array.new
116 116 @users.each do |u|
117 117 ustat = Array.new
118 118 ustat[0] = u
119 119 @problems.each do |p|
120 120 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
121 121 if (sub!=nil) and (sub.points!=nil)
122 122 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
123 123 else
124 124 ustat << [0,false]
125 125 end
126 126 end
127 127 @scorearray << ustat
128 128 end
129 129 end
130 130
131 131 def import
132 132 if params[:file]==''
133 133 flash[:notice] = 'Error importing no file'
134 134 redirect_to :action => 'list' and return
135 135 end
136 136 import_from_file(params[:file])
137 137 end
138 138
139 139 def random_all_passwords
140 140 users = User.find(:all)
141 141 @prefix = params[:prefix] || ''
142 142 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
143 143 @changed = false
144 144 if request.request_method == :post
145 145 @non_admin_users.each do |user|
146 146 password = random_password
147 147 user.password = password
148 148 user.password_confirmation = password
149 149 user.save
150 150 end
151 151 @changed = true
152 152 end
153 153 end
154 154
155 155 # contest management
156 156
157 157 def add_to_contest
158 158 user = User.find(params[:id])
159 159 contest = Contest.find(params[:contest_id])
160 160 if user and contest
161 161 user.contests << contest
162 162 end
163 163 redirect_to :action => 'list'
164 164 end
165 165
166 166 def remove_from_contest
167 167 user = User.find(params[:id])
168 168 contest = Contest.find(params[:contest_id])
169 169 if user and contest
170 170 user.contests.delete(contest)
171 171 end
172 172 redirect_to :action => 'list'
173 173 end
174 174
175 175 def contest_management
176 176 end
177 177
178 178 def manage_contest
179 179 contest = Contest.find(params[:contest][:id])
180 180 if !contest
181 181 flash[:notice] = 'You did not choose the contest.'
182 182 redirect_to :action => 'contest_management' and return
183 183 end
184 184
185 185 operation = params[:operation]
186 186
187 187 if not ['add','remove','assign'].include? operation
188 188 flash[:notice] = 'You did not choose the operation to perform.'
189 189 redirect_to :action => 'contest_management' and return
190 190 end
191 191
192 192 lines = params[:login_list]
193 193 if !lines or lines.blank?
194 194 flash[:notice] = 'You entered an empty list.'
195 195 redirect_to :action => 'contest_management' and return
196 196 end
197 197
198 198 note = []
199 + user_ids = {}
199 200 lines.split("\n").each do |line|
200 - puts line
201 201 user = User.find_by_login(line.chomp)
202 - puts user
203 202 if user
204 203 if operation=='add'
205 - user.contests << contest
204 + if ! user.contests.include? contest
205 + user.contests << contest
206 + end
206 207 elsif operation=='remove'
207 208 user.contests.delete(contest)
208 209 else
209 210 user.contests = [contest]
210 211 end
211 -
212 +
212 213 user.contest_stat.destroy if params[:reset_timer]
213 214
214 215 note << user.login
216 + user_ids[user.id] = true
215 217 end
216 218 end
219 +
220 + if params[:reset_timer]
221 + logout_users(user_ids)
222 + end
223 +
217 224 flash[:notice] = 'User(s) ' + note.join(', ') +
218 225 ' were successfully modified. '
219 226 redirect_to :action => 'contest_management'
220 227 end
221 228
222 229 # admin management
223 230
224 231 def admin
225 232 @admins = User.find(:all).find_all {|user| user.admin? }
226 233 end
227 234
228 235 def grant_admin
229 236 login = params[:login]
230 237 user = User.find_by_login(login)
231 238 if user!=nil
232 239 admin_role = Role.find_by_name('admin')
233 240 user.roles << admin_role
234 241 else
235 242 flash[:notice] = 'Unknown user'
236 243 end
237 244 flash[:notice] = 'User added as admins'
238 245 redirect_to :action => 'admin'
239 246 end
240 247
241 248 def revoke_admin
242 249 user = User.find(params[:id])
243 250 if user==nil
244 251 flash[:notice] = 'Unknown user'
245 252 redirect_to :action => 'admin' and return
246 253 elsif user.login == 'root'
247 254 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
248 255 redirect_to :action => 'admin' and return
249 256 end
250 257
251 258 admin_role = Role.find_by_name('admin')
252 259 user.roles.delete(admin_role)
253 260 flash[:notice] = 'User permission revoked'
254 261 redirect_to :action => 'admin'
255 262 end
256 263
257 264 protected
258 265
259 266 def random_password(length=5)
260 267 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
261 268 newpass = ""
262 269 length.times { newpass << chars[rand(chars.size-1)] }
263 270 return newpass
264 271 end
265 272
266 273 def import_from_file(f)
267 274 data_hash = YAML.load(f)
268 275 @import_log = ""
269 276
270 277 country_data = data_hash[:countries]
271 278 site_data = data_hash[:sites]
272 279 user_data = data_hash[:users]
273 280
274 281 # import country
275 282 countries = {}
276 283 country_data.each_pair do |id,country|
277 284 c = Country.find_by_name(country[:name])
278 285 if c!=nil
279 286 countries[id] = c
280 287 @import_log << "Found #{country[:name]}\n"
281 288 else
282 289 countries[id] = Country.new(:name => country[:name])
283 290 countries[id].save
284 291 @import_log << "Created #{country[:name]}\n"
285 292 end
286 293 end
287 294
288 295 # import sites
289 296 sites = {}
290 297 site_data.each_pair do |id,site|
291 298 s = Site.find_by_name(site[:name])
292 299 if s!=nil
293 300 @import_log << "Found #{site[:name]}\n"
294 301 else
295 302 s = Site.new(:name => site[:name])
296 303 @import_log << "Created #{site[:name]}\n"
297 304 end
298 305 s.password = site[:password]
299 306 s.country = countries[site[:country_id]]
300 307 s.save
301 308 sites[id] = s
302 309 end
303 310
304 311 # import users
305 312 user_data.each_pair do |id,user|
306 313 u = User.find_by_login(user[:login])
307 314 if u!=nil
308 315 @import_log << "Found #{user[:login]}\n"
309 316 else
310 317 u = User.new(:login => user[:login])
311 318 @import_log << "Created #{user[:login]}\n"
312 319 end
313 320 u.full_name = user[:name]
314 321 u.password = user[:password]
315 322 u.country = countries[user[:country_id]]
316 323 u.site = sites[user[:site_id]]
317 324 u.activated = true
318 325 u.email = "empty-#{u.login}@none.com"
319 326 if not u.save
320 327 @import_log << "Errors\n"
321 328 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
322 329 end
323 330 end
324 331
325 332 end
326 333
334 + def logout_users(user_ids)
335 + sessions = ActiveRecord::SessionStore::Session.find(:all, :conditions => ["updated_at >= ?", 60.minutes.ago])
336 + sessions.each do |session|
337 + if user_ids.has_key? session.data[:user_id]
338 + session.destroy
339 + end
340 + end
341 + end
342 +
327 343 end
@@ -1,183 +1,184
1 1 require 'yaml'
2 2
3 3 #
4 4 # This class also contains various login of the system.
5 5 #
6 6 class Configuration < ActiveRecord::Base
7 7
8 8 SYSTEM_MODE_CONF_KEY = 'system.mode'
9 9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
10 + MULTICONTESTS_KEY = 'system.multicontests'
11 + CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
10 12
11 13 cattr_accessor :cache
12 14 cattr_accessor :config_cache
13 15 cattr_accessor :task_grading_info_cache
14 16 cattr_accessor :contest_time_str
15 17 cattr_accessor :contest_time
16 18
17 19 # set @@cache = true to only reload once.
18 20 Configuration.cache = false
19 21
20 22 Configuration.config_cache = nil
21 23 Configuration.task_grading_info_cache = nil
22 24
23 25 def self.get(key)
24 26 if Configuration.cache
25 27 if Configuration.config_cache == nil
26 28 self.read_config
27 29 end
28 30 return Configuration.config_cache[key]
29 31 else
30 32 return Configuration.read_one_key(key)
31 33 end
32 34 end
33 35
34 36 def self.[](key)
35 37 self.get(key)
36 38 end
37 39
38 40 def self.reload
39 41 self.read_config
40 42 end
41 43
42 44 def self.clear
43 45 Configuration.config_cache = nil
44 46 end
45 47
46 48 def self.cache?
47 49 Configuration.cache
48 50 end
49 51
50 52 def self.enable_caching
51 53 Configuration.cache = true
52 54 end
53 55
54 56 #
55 57 # View decision
56 58 #
57 59 def self.show_submitbox_to?(user)
58 60 mode = get(SYSTEM_MODE_CONF_KEY)
59 61 return false if mode=='analysis'
60 62 if (mode=='contest')
61 63 return false if (user.site!=nil) and
62 64 ((user.site.started!=true) or (user.site.finished?))
63 65 end
64 66 return true
65 67 end
66 68
67 69 def self.show_tasks_to?(user)
68 70 if time_limit_mode?
69 71 return false if not user.contest_started?
70 72 end
71 73 return true
72 74 end
73 75
74 76 def self.show_grading_result
75 77 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
76 78 end
77 79
78 80 def self.allow_test_request(user)
79 81 mode = get(SYSTEM_MODE_CONF_KEY)
80 82 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
81 83 if (mode=='contest')
82 84 return false if ((user.site!=nil) and
83 85 ((user.site.started!=true) or
84 86 (early_timeout and (user.site.time_left < 30.minutes))))
85 87 end
86 88 return false if mode=='analysis'
87 89 return true
88 90 end
89 91
90 92 def self.task_grading_info
91 93 if Configuration.task_grading_info_cache==nil
92 94 read_grading_info
93 95 end
94 96 return Configuration.task_grading_info_cache
95 97 end
96 98
97 99 def self.standard_mode?
98 100 return get(SYSTEM_MODE_CONF_KEY) == 'standard'
99 101 end
100 102
101 103 def self.contest_mode?
102 104 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
103 105 end
104 106
105 107 def self.indv_contest_mode?
106 108 return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
107 109 end
108 110
109 111 def self.multicontests?
110 - g = get('system.multicontests')
111 - return get('system.multicontests') == true
112 + return get(MULTICONTESTS_KEY) == true
112 113 end
113 114
114 115 def self.time_limit_mode?
115 116 mode = get(SYSTEM_MODE_CONF_KEY)
116 117 return ((mode == 'contest') or (mode == 'indv-contest'))
117 118 end
118 119
119 120 def self.analysis_mode?
120 121 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
121 122 end
122 123
123 124 def self.contest_time_limit
124 - contest_time_str = Configuration['contest.time_limit']
125 + contest_time_str = Configuration[CONTEST_TIME_LIMIT_KEY]
125 126
126 127 if not defined? Configuration.contest_time_str
127 128 Configuration.contest_time_str = nil
128 129 end
129 130
130 131 if Configuration.contest_time_str != contest_time_str
131 132 Configuration.contest_time_str = contest_time_str
132 133 if tmatch = /(\d+):(\d+)/.match(contest_time_str)
133 134 h = tmatch[1].to_i
134 135 m = tmatch[2].to_i
135 136
136 137 Configuration.contest_time = h.hour + m.minute
137 138 else
138 139 Configuration.contest_time = nil
139 140 end
140 141 end
141 142 return Configuration.contest_time
142 143 end
143 144
144 145 protected
145 146
146 147 def self.convert_type(val,type)
147 148 case type
148 149 when 'string'
149 150 return val
150 151
151 152 when 'integer'
152 153 return val.to_i
153 154
154 155 when 'boolean'
155 156 return (val=='true')
156 157 end
157 158 end
158 159
159 160 def self.read_config
160 161 Configuration.config_cache = {}
161 162 Configuration.find(:all).each do |conf|
162 163 key = conf.key
163 164 val = conf.value
164 165 Configuration.config_cache[key] = Configuration.convert_type(val,conf.value_type)
165 166 end
166 167 end
167 168
168 169 def self.read_one_key(key)
169 170 conf = Configuration.find_by_key(key)
170 171 if conf
171 172 return Configuration.convert_type(conf.value,conf.value_type)
172 173 else
173 174 return nil
174 175 end
175 176 end
176 177
177 178 def self.read_grading_info
178 179 f = File.open(TASK_GRADING_INFO_FILENAME)
179 180 Configuration.task_grading_info_cache = YAML.load(f)
180 181 f.close
181 182 end
182 183
183 184 end
@@ -61,196 +61,193
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 - time_limit = Configuration.contest_time_limit
158 -
159 - return false if contest_stat==nil
160 -
157 + return false if self.contest_stat(true)==nil
161 158 return contest_time_left == 0
162 159 else
163 160 return false
164 161 end
165 162 end
166 163
167 164 def contest_started?
168 165 if Configuration.contest_mode?
169 166 return true if site==nil
170 167 return site.started
171 168 else
172 169 return true
173 170 end
174 171 end
175 172
176 173 def update_start_time
177 174 stat = self.contest_stat
178 175 if stat == nil
179 176 stat = UserContestStat.new(:user => self,
180 177 :started_at => Time.now.gmtime)
181 178 stat.save
182 179 end
183 180 end
184 181
185 182 def problem_in_user_contests?(problem)
186 183 problem_contests = problem.contests.all
187 184
188 185 if problem_contests.length == 0 # this is public contest
189 186 return true
190 187 end
191 188
192 189 contests.each do |contest|
193 190 if problem_contests.find {|c| c.id == contest.id }
194 191 return true
195 192 end
196 193 end
197 194 return false
198 195 end
199 196
200 197 def available_problems_group_by_contests
201 198 contest_problems = []
202 199 pin = {}
203 200 contests.enabled.each do |contest|
204 201 available_problems = contest.problems.available
205 202 contest_problems << {
206 203 :contest => contest,
207 204 :problems => available_problems
208 205 }
209 206 available_problems.each {|p| pin[p.id] = true}
210 207 end
211 208 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
212 209 contest_problems << {
213 210 :contest => nil,
214 211 :problems => other_avaiable_problems
215 212 }
216 213 return contest_problems
217 214 end
218 215
219 216 def available_problems
220 217 if not Configuration.multicontests?
221 218 return Problem.find_available_problems
222 219 else
223 220 contest_problems = []
224 221 pin = {}
225 222 contests.enabled.each do |contest|
226 223 contest.problems.available.each do |problem|
227 224 if not pin.has_key? problem.id
228 225 contest_problems << problem
229 226 end
230 227 pin[problem.id] = true
231 228 end
232 229 end
233 230 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
234 231 return contest_problems + other_avaiable_problems
235 232 end
236 233 end
237 234
238 235 def can_view_problem?(problem)
239 236 if not Configuration.multicontests?
240 237 return problem.available
241 238 else
242 239 return problem_in_user_contests? problem
243 240 end
244 241 end
245 242
246 243 protected
247 244 def encrypt_new_password
248 245 return if password.blank?
249 246 self.salt = (10+rand(90)).to_s
250 247 self.hashed_password = User.encrypt(self.password,self.salt)
251 248 end
252 249
253 250 def assign_default_site
254 251 # have to catch error when migrating (because self.site is not available).
255 252 begin
256 253 if self.site==nil
@@ -1,128 +1,128
1 1 CONFIGURATIONS =
2 2 [
3 3 {
4 4 :key => 'system.single_user_mode',
5 5 :value_type => 'boolean',
6 6 :default_value => 'false',
7 7 :description => 'Only admins can log in to the system when running under single user mode.'
8 8 },
9 9
10 10 {
11 11 :key => 'ui.front.title',
12 12 :value_type => 'string',
13 13 :default_value => 'Grader'
14 14 },
15 15
16 16 {
17 17 :key => 'ui.front.welcome_message',
18 18 :value_type => 'string',
19 19 :default_value => 'Welcome!'
20 20 },
21 21
22 22 {
23 23 :key => 'ui.show_score',
24 24 :value_type => 'boolean',
25 25 :default_value => 'true'
26 26 },
27 27
28 28 {
29 29 :key => 'contest.time_limit',
30 30 :value_type => 'string',
31 31 :default_value => 'unlimited',
32 - :description => 'Time limit in format hh:mm, or "unlimited" for contests with no time limits.'
32 + :description => 'Time limit in format hh:mm, or "unlimited" for contests with no time limits. This config is CACHED. Restart the server before the change can take effect.'
33 33 },
34 34
35 35 {
36 36 :key => 'system.mode',
37 37 :value_type => 'string',
38 38 :default_value => 'standard',
39 39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
40 40 },
41 41
42 42 {
43 43 :key => 'contest.name',
44 44 :value_type => 'string',
45 45 :default_value => 'Grader',
46 46 :description => 'This name will be shown on the user header bar.'
47 47 },
48 48
49 49 {
50 50 :key => 'contest.multisites',
51 51 :value_type => 'boolean',
52 52 :default_value => 'false',
53 53 :description => 'If the server is in contest mode and this option is true, on the log in of the admin a menu for site selections is shown.'
54 54 },
55 55
56 56 {
57 57 :key => 'system.online_registration',
58 58 :value_type => 'boolean',
59 59 :default_value => 'false',
60 60 :description => 'This option enables online registration.'
61 61 },
62 62
63 63 # If Configuration['system.online_registration'] is true, the
64 64 # system allows online registration, and will use these
65 65 # information for sending confirmation emails.
66 66 {
67 67 :key => 'system.online_registration.smtp',
68 68 :value_type => 'string',
69 69 :default_value => 'smtp.somehost.com'
70 70 },
71 71
72 72 {
73 73 :key => 'system.online_registration.from',
74 74 :value_type => 'string',
75 75 :default_value => 'your.email@address'
76 76 },
77 77
78 78 {
79 79 :key => 'system.admin_email',
80 80 :value_type => 'string',
81 81 :default_value => 'admin@admin.email'
82 82 },
83 83
84 84 {
85 85 :key => 'system.user_setting_enabled',
86 86 :value_type => 'boolean',
87 87 :default_value => 'true',
88 88 :description => 'If this option is true, users can change their settings'
89 89 },
90 90
91 91 # If Configuration['contest.test_request.early_timeout'] is true
92 92 # the user will not be able to use test request at 30 minutes
93 93 # before the contest ends.
94 94 {
95 95 :key => 'contest.test_request.early_timeout',
96 96 :value_type => 'boolean',
97 97 :default_value => 'false'
98 98 },
99 99
100 100 {
101 101 :key => 'system.multicontests',
102 102 :value_type => 'boolean',
103 103 :default_value => 'false'
104 104 }
105 105 ]
106 106
107 107
108 108 def create_configuration_key(key,
109 109 value_type,
110 110 default_value,
111 111 description='')
112 112 conf = (Configuration.find_by_key(key) ||
113 113 Configuration.new(:key => key,
114 114 :value_type => value_type,
115 115 :value => default_value))
116 116 conf.description = description
117 117 conf.save
118 118 end
119 119
120 120 def seed_config
121 121 CONFIGURATIONS.each do |conf|
122 122 if conf.has_key? :description
123 123 desc = conf[:description]
124 124 else
125 125 desc = ''
126 126 end
127 127 create_configuration_key(conf[:key],
128 128 conf[:value_type],
@@ -1,20 +1,38
1 1
2 2 module ConfigSpecHelperMethods
3 3
4 4 def find_or_create_and_set_config(key, type, value)
5 5 c = Configuration.find_by_key(key)
6 6 c ||= Configuration.new(:key => key,
7 7 :value_type => type)
8 8 c.value = value
9 9 c.save!
10 10 end
11 11
12 12 def enable_multicontest
13 - find_or_create_and_set_config('system.multicontests','boolean','true')
13 + find_or_create_and_set_config(Configuration::MULTICONTESTS_KEY,
14 + 'boolean','true')
14 15 end
15 16
16 17 def disable_multicontest
17 - find_or_create_and_set_config('system.multicontests','boolean','false')
18 + find_or_create_and_set_config(Configuration::MULTICONTESTS_KEY,
19 + 'boolean','false')
20 + end
21 +
22 + def set_indv_contest_mode
23 + find_or_create_and_set_config(Configuration::SYSTEM_MODE_CONF_KEY,
24 + 'string','indv-contest')
18 25 end
19 26
27 + def set_standard_mode
28 + find_or_create_and_set_config(Configuration::SYSTEM_MODE_CONF_KEY,
29 + 'string','standard')
30 + end
31 +
32 + def set_contest_time_limit(limit)
33 + find_or_create_and_set_config(Configuration::CONTEST_TIME_LIMIT_KEY,
34 + 'string',limit)
35 + # clear old value
36 + Configuration.contest_time_str = nil
37 + end
20 38 end
@@ -1,144 +1,193
1 + require 'delorean'
2 +
1 3 require File.dirname(__FILE__) + '/../spec_helper'
2 4 require File.dirname(__FILE__) + '/../config_spec_helper'
3 5
4 6 describe MainController, "when a user comes to list page" do
5 7
6 8 it "should redirect user to login page when unlogged-in user try to access main/list" do
7 9 get 'list'
8 10 response.should redirect_to(:action => 'login')
9 11 end
10 12
11 13 end
12 14
13 15 describe MainController, "when a logged in user comes to list page, with multicontests off" do
14 16 integrate_views
15 17
16 18 include ConfigSpecHelperMethods
17 19
18 20 fixtures :users
19 21 fixtures :problems
20 22 fixtures :contests
21 23
22 24 before(:each) do
23 25 disable_multicontest
24 26 end
25 27
26 28 it "should list available problems" do
27 29 john = users(:john)
28 30 get "list", {}, {:user_id => john.id}
29 31
30 32 response.should render_template 'main/list'
31 33 response.should have_text(/add/)
32 34 response.should have_text(/easy_problem/)
33 35 response.should have_text(/hard_problem/)
34 36 end
35 37
36 38 end
37 39
38 40 describe MainController, "when a logged in user comes to list page, with multicontests on" do
39 41 integrate_views
40 42
41 43 include ConfigSpecHelperMethods
42 44
43 45 fixtures :users
44 46 fixtures :problems
45 47 fixtures :contests
46 48
47 49 before(:each) do
48 50 enable_multicontest
49 51 end
50 52
51 53 it "should list only available public problems to users with no contest assigned" do
52 54 john = users(:john)
53 55 get "list", {}, {:user_id => john.id}
54 56
55 57 response.should render_template('main/list')
56 58 response.should have_text(/add/)
57 59 response.should_not have_text(/easy_problem/)
58 60 response.should_not have_text(/hard_problem/)
59 61 end
60 62
61 63 it "should list available problems on a specific contest" do
62 64 james = users(:james)
63 65 get "list", {}, {:user_id => james.id}
64 66
65 67 response.should render_template('main/list')
66 68 response.should have_text(/add/)
67 69 response.should have_text(/easy_problem/)
68 70 response.should_not have_text(/hard_problem/)
69 71 end
70 72
71 73 it "should shows available problems by contests" do
72 74 james = users(:james)
73 75 get "list", {}, {:user_id => james.id}
74 76
75 77 response.should render_template('main/list')
76 78 response.should have_text(Regexp.new('Contest A.*easy_problem', Regexp::MULTILINE))
77 79 end
78 80
79 81 it "should shows available problems by contests; problems belonging to more the one contest should appear many times" do
80 82 jack = users(:jack)
81 83 get "list", {}, {:user_id => jack.id}
82 84
83 85 response.should render_template('main/list')
84 86 response.should have_text(Regexp.new('Contest A.*easy_problem.*Contest B.*easy_problem', Regexp::MULTILINE))
85 87 response.should have_text(Regexp.new('Contest B.*hard_problem', Regexp::MULTILINE))
86 88 end
87 89 end
88 90
89 91 describe MainController, "when a user loads sources and compiler messages" do
90 92
91 93 before(:each) do
92 94 @problem = mock(Problem, :name => 'test', :output_only => false)
93 95 @language = mock(Language, :name => 'cpp', :ext => 'cpp')
94 96 @submission = mock(Submission,
95 97 :id => 1,
96 98 :user_id => 1,
97 99 :problem => @problem,
98 100 :language => @language,
99 101 :source => 'sample source',
100 102 :compiler_message => 'none')
101 103
102 104 @user = mock(User, :id => 1, :login => 'john')
103 105 @user.should_receive(:update_start_time).at_most(:once)
104 106
105 107 @another_user = mock(User, :id => 2, :login => 'mary')
106 108 @another_user.should_receive(:update_start_time).at_most(:once)
107 109
108 110 User.should_receive(:find).
109 111 with(1).any_number_of_times.
110 112 and_return(@user)
111 113 User.should_receive(:find).
112 114 with(2).any_number_of_times.
113 115 and_return(@another_user)
114 116 Submission.should_receive(:find).
115 117 any_number_of_times.with(@submission.id.to_s).
116 118 and_return(@submission)
117 119 end
118 120
119 121 it "should let user sees her own source" do
120 122 @submission.should_receive(:download_filename).and_return("foo.c")
121 123 get 'source', {:id => @submission.id}, {:user_id => 1}
122 124 response.should be_success
123 125 end
124 126
125 127 it "should let user sees her own compiler message" do
126 128 get 'compiler_msg', {:id => @submission.id}, {:user_id => 1}
127 129 response.should be_success
128 130 end
129 131
130 132 it "should not let user sees other user's source" do
131 133 get 'source', {:id => @submission.id}, {:user_id => 2}
132 134 flash[:notice].should =~ /[Ee]rror/
133 135 response.should redirect_to(:action => 'list')
134 136 end
135 137
136 138 it "should not let user sees other user's compiler message" do
137 139 get 'compiler_msg', {:id => @submission.id}, {:user_id => 2}
138 140 flash[:notice].should =~ /[Ee]rror/
139 141 response.should redirect_to(:action => 'list')
140 142 end
141 143
142 144 end
143 145
144 146
147 + describe MainController, "during individual contest mode" do
148 +
149 + integrate_views
150 +
151 + include ConfigSpecHelperMethods
152 +
153 + fixtures :users
154 + fixtures :problems
155 + fixtures :contests
156 +
157 + before(:each) do
158 + set_contest_time_limit('3:00') # 3 hours
159 + set_indv_contest_mode
160 + end
161 +
162 + it "should allow newly login user to see problem list" do
163 + john = users(:john)
164 + get "list", {}, {:user_id => john.id}
165 +
166 + response.should render_template 'main/list'
167 + response.should have_text(/add/)
168 + response.should have_text(/easy_problem/)
169 + response.should have_text(/hard_problem/)
170 + end
171 +
172 + it "should not show 'contest over' sign before the contest ends" do
173 + john = users(:john)
174 + get "list", {}, {:user_id => john.id}
175 +
176 + Delorean.time_travel_to(179.minutes.since) do
177 + get "list", {}, {:user_id => john.id}
178 + response.should_not have_text(/OVER/)
179 + end
180 + end
181 +
182 + it "should show 'contest over' sign after the contest ends" do
183 + john = users(:john)
184 + get "list", {}, {:user_id => john.id}
185 +
186 + Delorean.time_travel_to(181.minutes.since) do
187 + get "list", {}, {:user_id => john.id}
188 + response.should have_text(/OVER/)
189 + end
190 + end
191 +
192 + end
193 +
@@ -1,3 +1,4
1 1 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2 2 admin:
3 + name: admin
3 4 rights: graders_right, user_admin_right, problems_right No newline at end of file
@@ -1,11 +1,11
1 1 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2 2
3 - one:
4 - name: MyString
3 + first_site:
4 + name: First site
5 5 started: false
6 6 start_time: 2008-04-09 14:08:28
7 7
8 - two:
9 - name: MyString
8 + second_site:
9 + name: Second site
10 10 started: false
11 11 start_time: 2008-04-09 14:08:28
@@ -1,34 +1,38
1 1 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2 2
3 3 <%
4 4 User.public_class_method :encrypt
5 5
6 6 salt = "abc"
7 7 %>
8 8
9 9 john:
10 10 login: john
11 11 full_name: john
12 12 hashed_password: <%= User.encrypt("hello",salt) %>
13 13 salt: <%= salt %>
14 + activated: true
14 15
15 16 mary:
16 17 login: mary
17 18 full_name: mary
18 19 hashed_password: <%= User.encrypt("goodbye",salt) %>
19 20 salt: <%= salt %>
20 21 roles: admin
22 + activated: true
21 23
22 24 james:
23 25 login: james
24 26 full_name: James
25 27 hashed_password: <%= User.encrypt("morning",salt) %>
26 28 salt: <%= salt %>
27 29 contests: contest_a
30 + activated: true
28 31
29 32 jack:
30 33 login: jack
31 34 full_name: Jack
32 35 hashed_password: <%= User.encrypt("morning",salt) %>
33 36 salt: <%= salt %>
34 37 contests: contest_a, contest_b
38 + activated: true
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
You need to be logged in to leave comments. Login now