Description:
merge with algo-bm
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r787:6a311ff47066 - - 13 files changed: 73 inserted, 67 deleted

@@ -1,167 +1,167
1 1 require 'ipaddr'
2 2
3 3 class ApplicationController < ActionController::Base
4 4 protect_from_forgery
5 5
6 6 before_action :current_user
7 7
8 8 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
9 9 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
10 - ALLOW_WHITELIST_IP_ONLY_CONF_KEY = 'right.allow_whitelist_ip_only'
10 + WHITELIST_IGNORE_CONF_KEY = 'right.whitelist_ignore'
11 11 WHITELIST_IP_CONF_KEY = 'right.whitelist_ip'
12 12
13 13 #report and redirect for unauthorized activities
14 14 def unauthorized_redirect(notice = 'You are not authorized to view the page you requested')
15 15 flash[:notice] = notice
16 16 redirect_to login_main_path
17 17 end
18 18
19 19 # Returns the current logged-in user (if any).
20 20 def current_user
21 21 return nil unless session[:user_id]
22 22 @current_user ||= User.find(session[:user_id])
23 23 end
24 24
25 25 def admin_authorization
26 26 return false unless check_valid_login
27 27 user = User.includes(:roles).find(session[:user_id])
28 28 unless user.admin?
29 29 unauthorized_redirect
30 30 return false
31 31 end
32 32 return true
33 33 end
34 34
35 35 def authorization_by_roles(allowed_roles)
36 36 return false unless check_valid_login
37 37 user = User.find(session[:user_id])
38 38 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
39 39 unauthorized_redirect
40 40 return false
41 41 end
42 42 end
43 43
44 44 def testcase_authorization
45 45 #admin always has privileged
46 46 if @current_user.admin?
47 47 return true
48 48 end
49 49
50 50 unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
51 51 end
52 52
53 53
54 54 protected
55 55
56 56 #redirect to root (and also force logout)
57 57 #if the user is not logged_in or the system is in "ADMIN ONLY" mode
58 58 def check_valid_login
59 59 #check if logged in
60 60 unless session[:user_id]
61 61 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
62 62 unauthorized_redirect('You need to login but you cannot log in at this time')
63 63 else
64 64 unauthorized_redirect('You need to login')
65 65 end
66 66 return false
67 67 end
68 68
69 69 # check if run in single user mode
70 70 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
71 71 if @current_user==nil || (!@current_user.admin?)
72 72 unauthorized_redirect('You cannot log in at this time')
73 73 return false
74 74 end
75 75 end
76 76
77 77 # check if the user is enabled
78 78 unless @current_user.enabled? || @current_user.admin?
79 79 unauthorized_redirect 'Your account is disabled'
80 80 return false
81 81 end
82 82
83 83 # check if user ip is allowed
84 - unless @current_user.admin? || !GraderConfiguration[ALLOW_WHITELIST_IP_ONLY_CONF_KEY]
84 + unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
85 85 unless is_request_ip_allowed?
86 - unauthorized_redirect 'Your IP is not allowed'
86 + unauthorized_redirect 'Your IP is not allowed to login at this time.'
87 87 return false
88 88 end
89 89 end
90 90
91 91 if GraderConfiguration.multicontests?
92 92 return true if @current_user.admin?
93 93 begin
94 94 if @current_user.contest_stat(true).forced_logout
95 95 flash[:notice] = 'You have been automatically logged out.'
96 96 redirect_to :controller => 'main', :action => 'index'
97 97 end
98 98 rescue
99 99 end
100 100 end
101 101 return true
102 102 end
103 103
104 104 #redirect to root (and also force logout)
105 105 #if the user use different ip from the previous connection
106 106 # only applicable when MULTIPLE_IP_LOGIN options is false only
107 107 def authenticate_by_ip_address
108 108 #this assume that we have already authenticate normally
109 109 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
110 110 user = User.find(session[:user_id])
111 - puts "User admin #{user.admin?}"
112 111 if (!user.admin? && user.last_ip && user.last_ip != request.remote_ip)
113 112 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
114 - puts "hahaha"
115 113 redirect_to :controller => 'main', :action => 'login'
116 114 return false
117 115 end
118 116 unless user.last_ip
119 117 user.last_ip = request.remote_ip
120 118 user.save
121 119 end
122 120 end
123 121 return true
124 122 end
125 123
126 124 def authorization
127 125 return false unless check_valid_login
128 126 user = User.find(session[:user_id])
129 127 unless user.roles.detect { |role|
130 128 role.rights.detect{ |right|
131 129 right.controller == self.class.controller_name and
132 130 (right.action == 'all' || right.action == action_name)
133 131 }
134 132 }
135 133 flash[:notice] = 'You are not authorized to view the page you requested'
136 134 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
137 135 redirect_to :controller => 'main', :action => 'login'
138 136 return false
139 137 end
140 138 end
141 139
142 140 def verify_time_limit
143 141 return true if session[:user_id]==nil
144 142 user = User.find(session[:user_id], :include => :site)
145 143 return true if user==nil || user.site == nil
146 144 if user.contest_finished?
147 145 flash[:notice] = 'Error: the contest you are participating is over.'
148 146 redirect_to :back
149 147 return false
150 148 end
151 149 return true
152 150 end
153 151
154 152 def is_request_ip_allowed?
155 - if GraderConfiguration[ALLOW_WHITELIST_IP_ONLY_CONF_KEY]
153 + unless GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
156 154 user_ip = IPAddr.new(request.remote_ip)
157 - GraderConfiguration[WHITELIST_IP_LIST_CONF_KEY].delete(' ').split(',').each do |ips|
155 +
156 + GraderConfiguration[WHITELIST_IP_CONF_KEY].delete(' ').split(',').each do |ips|
158 157 allow_ips = IPAddr.new(ips)
159 - unless allow_ips.includes(user_ip)
160 - return false
158 + if allow_ips.include?(user_ip)
159 + return true
161 160 end
162 161 end
162 + return false
163 163 end
164 164 return true
165 165 end
166 166
167 167 end
@@ -1,368 +1,369
1 1 class MainController < ApplicationController
2 2
3 3 before_action :check_valid_login, :except => [:login]
4 4 before_action :check_viewability, :except => [:index, :login]
5 5
6 6 append_before_action :confirm_and_update_start_time,
7 7 :except => [:index,
8 8 :login,
9 9 :confirm_contest_start]
10 10
11 11 # to prevent log in box to be shown when user logged out of the
12 12 # system only in some tab
13 13 prepend_before_action :reject_announcement_refresh_when_logged_out,
14 14 :only => [:announcements]
15 15
16 16 before_action :authenticate_by_ip_address, :only => [:list]
17 17
18 18 #reset login, clear session
19 19 #front page
20 20 def login
21 21 saved_notice = flash[:notice]
22 22 reset_session
23 23 flash.now[:notice] = saved_notice
24 + @remote_ip = request.remote_ip
24 25
25 26 # EXPERIMENT:
26 27 # Hide login if in single user mode and the url does not
27 28 # explicitly specify /login
28 29 #
29 30 # logger.info "PATH: #{request.path}"
30 31 # if GraderConfiguration['system.single_user_mode'] and
31 32 # request.path!='/main/login'
32 33 # @hidelogin = true
33 34 # end
34 35
35 36 @announcements = Announcement.frontpage
36 37 render :action => 'login', :layout => 'empty'
37 38 end
38 39
39 40 def logout
40 41 reset_session
41 42 redirect_to root_path
42 43 end
43 44
44 45 def list
45 46 prepare_list_information
46 47 end
47 48
48 49 def help
49 50 @user = User.find(session[:user_id])
50 51 end
51 52
52 53 def submit
53 54 user = User.find(session[:user_id])
54 55
55 56 @submission = Submission.new
56 57 @submission.problem_id = params[:submission][:problem_id]
57 58 @submission.user = user
58 59 @submission.language_id = 0
59 60 if (params['file']) and (params['file']!='')
60 61 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
61 62 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
62 63 @submission.source_filename = params['file'].original_filename
63 64 end
64 65
65 66 if (params[:editor_text])
66 67 language = Language.find_by_id(params[:language_id])
67 68 @submission.source = params[:editor_text]
68 69 @submission.source_filename = "live_edit.#{language.ext}"
69 70 @submission.language = language
70 71 end
71 72
72 73 @submission.submitted_at = Time.new.gmtime
73 74 @submission.ip_address = request.remote_ip
74 75
75 76 if GraderConfiguration.time_limit_mode? and user.contest_finished?
76 77 @submission.errors.add(:base,"The contest is over.")
77 78 prepare_list_information
78 79 render :action => 'list' and return
79 80 end
80 81
81 82 if @submission.valid?(@current_user)
82 83 if @submission.save == false
83 84 flash[:notice] = 'Error saving your submission'
84 85 elsif Task.create(:submission_id => @submission.id,
85 86 :status => Task::STATUS_INQUEUE) == false
86 87 flash[:notice] = 'Error adding your submission to task queue'
87 88 end
88 89 else
89 90 prepare_list_information
90 91 render :action => 'list' and return
91 92 end
92 93 redirect_to edit_submission_path(@submission)
93 94 end
94 95
95 96 def source
96 97 submission = Submission.find(params[:id])
97 98 if ((submission.user_id == session[:user_id]) and
98 99 (submission.problem != nil) and
99 100 (submission.problem.available))
100 101 send_data(submission.source,
101 102 {:filename => submission.download_filename,
102 103 :type => 'text/plain'})
103 104 else
104 105 flash[:notice] = 'Error viewing source'
105 106 redirect_to :action => 'list'
106 107 end
107 108 end
108 109
109 110 def compiler_msg
110 111 @submission = Submission.find(params[:id])
111 112 if @submission.user_id == session[:user_id]
112 113 render :action => 'compiler_msg', :layout => 'empty'
113 114 else
114 115 flash[:notice] = 'Error viewing source'
115 116 redirect_to :action => 'list'
116 117 end
117 118 end
118 119
119 120 def result
120 121 if !GraderConfiguration.show_grading_result
121 122 redirect_to :action => 'list' and return
122 123 end
123 124 @user = User.find(session[:user_id])
124 125 @submission = Submission.find(params[:id])
125 126 if @submission.user!=@user
126 127 flash[:notice] = 'You are not allowed to view result of other users.'
127 128 redirect_to :action => 'list' and return
128 129 end
129 130 prepare_grading_result(@submission)
130 131 end
131 132
132 133 def load_output
133 134 if !GraderConfiguration.show_grading_result or params[:num]==nil
134 135 redirect_to :action => 'list' and return
135 136 end
136 137 @user = User.find(session[:user_id])
137 138 @submission = Submission.find(params[:id])
138 139 if @submission.user!=@user
139 140 flash[:notice] = 'You are not allowed to view result of other users.'
140 141 redirect_to :action => 'list' and return
141 142 end
142 143 case_num = params[:num].to_i
143 144 out_filename = output_filename(@user.login,
144 145 @submission.problem.name,
145 146 @submission.id,
146 147 case_num)
147 148 if !FileTest.exists?(out_filename)
148 149 flash[:notice] = 'Output not found.'
149 150 redirect_to :action => 'list' and return
150 151 end
151 152
152 153 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
153 154 response.headers['Content-Type'] = "application/force-download"
154 155 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
155 156 response.headers["X-Sendfile"] = out_filename
156 157 response.headers['Content-length'] = File.size(out_filename)
157 158 render :nothing => true
158 159 else
159 160 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
160 161 end
161 162 end
162 163
163 164 def error
164 165 @user = User.find(session[:user_id])
165 166 end
166 167
167 168 # announcement refreshing and hiding methods
168 169
169 170 def announcements
170 171 if params.has_key? 'recent'
171 172 prepare_announcements(params[:recent])
172 173 else
173 174 prepare_announcements
174 175 end
175 176 render(:partial => 'announcement',
176 177 :collection => @announcements,
177 178 :locals => {:announcement_effect => true})
178 179 end
179 180
180 181 def confirm_contest_start
181 182 user = User.find(session[:user_id])
182 183 if request.method == 'POST'
183 184 user.update_start_time
184 185 redirect_to :action => 'list'
185 186 else
186 187 @contests = user.contests
187 188 @user = user
188 189 end
189 190 end
190 191
191 192 protected
192 193
193 194 def prepare_announcements(recent=nil)
194 195 if GraderConfiguration.show_tasks_to?(@user)
195 196 @announcements = Announcement.published(true)
196 197 else
197 198 @announcements = Announcement.published
198 199 end
199 200 if recent!=nil
200 201 recent_id = recent.to_i
201 202 @announcements = @announcements.find_all { |a| a.id > recent_id }
202 203 end
203 204 end
204 205
205 206 def prepare_list_information
206 207 @user = User.find(session[:user_id])
207 208 if not GraderConfiguration.multicontests?
208 209 @problems = @user.available_problems
209 210 else
210 211 @contest_problems = @user.available_problems_group_by_contests
211 212 @problems = @user.available_problems
212 213 end
213 214 @prob_submissions = {}
214 215 @problems.each do |p|
215 216 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
216 217 if sub!=nil
217 218 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
218 219 else
219 220 @prob_submissions[p.id] = { :count => 0, :submission => nil }
220 221 end
221 222 end
222 223 prepare_announcements
223 224 end
224 225
225 226 def check_viewability
226 227 @user = User.find(session[:user_id])
227 228 if (!GraderConfiguration.show_tasks_to?(@user)) and
228 229 ((action_name=='submission') or (action_name=='submit'))
229 230 redirect_to :action => 'list' and return
230 231 end
231 232 end
232 233
233 234 def prepare_grading_result(submission)
234 235 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
235 236 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
236 237 else
237 238 # guess task info from problem.full_score
238 239 cases = submission.problem.full_score / 10
239 240 grading_info = {
240 241 'testruns' => cases,
241 242 'testcases' => cases
242 243 }
243 244 end
244 245 @test_runs = []
245 246 if grading_info['testruns'].is_a? Integer
246 247 trun_count = grading_info['testruns']
247 248 trun_count.times do |i|
248 249 @test_runs << [ read_grading_result(@user.login,
249 250 submission.problem.name,
250 251 submission.id,
251 252 i+1) ]
252 253 end
253 254 else
254 255 grading_info['testruns'].keys.sort.each do |num|
255 256 run = []
256 257 testrun = grading_info['testruns'][num]
257 258 testrun.each do |c|
258 259 run << read_grading_result(@user.login,
259 260 submission.problem.name,
260 261 submission.id,
261 262 c)
262 263 end
263 264 @test_runs << run
264 265 end
265 266 end
266 267 end
267 268
268 269 def grading_result_dir(user_name, problem_name, submission_id, case_num)
269 270 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
270 271 end
271 272
272 273 def output_filename(user_name, problem_name, submission_id, case_num)
273 274 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
274 275 return "#{dir}/output.txt"
275 276 end
276 277
277 278 def read_grading_result(user_name, problem_name, submission_id, case_num)
278 279 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
279 280 result_file_name = "#{dir}/result"
280 281 if !FileTest.exists?(result_file_name)
281 282 return {:num => case_num, :msg => 'program did not run'}
282 283 else
283 284 results = File.open(result_file_name).readlines
284 285 run_stat = extract_running_stat(results)
285 286 output_filename = "#{dir}/output.txt"
286 287 if FileTest.exists?(output_filename)
287 288 output_file = true
288 289 output_size = File.size(output_filename)
289 290 else
290 291 output_file = false
291 292 output_size = 0
292 293 end
293 294
294 295 return {
295 296 :num => case_num,
296 297 :msg => results[0],
297 298 :run_stat => run_stat,
298 299 :output => output_file,
299 300 :output_size => output_size
300 301 }
301 302 end
302 303 end
303 304
304 305 # copied from grader/script/lib/test_request_helper.rb
305 306 def extract_running_stat(results)
306 307 running_stat_line = results[-1]
307 308
308 309 # extract exit status line
309 310 run_stat = ""
310 311 if !(/[Cc]orrect/.match(results[0]))
311 312 run_stat = results[0].chomp
312 313 else
313 314 run_stat = 'Program exited normally'
314 315 end
315 316
316 317 logger.info "Stat line: #{running_stat_line}"
317 318
318 319 # extract running time
319 320 if res = /r(.*)u(.*)s/.match(running_stat_line)
320 321 seconds = (res[1].to_f + res[2].to_f)
321 322 time_stat = "Time used: #{seconds} sec."
322 323 else
323 324 seconds = nil
324 325 time_stat = "Time used: n/a sec."
325 326 end
326 327
327 328 # extract memory usage
328 329 if res = /s(.*)m/.match(running_stat_line)
329 330 memory_used = res[1].to_i
330 331 else
331 332 memory_used = -1
332 333 end
333 334
334 335 return {
335 336 :msg => "#{run_stat}\n#{time_stat}",
336 337 :running_time => seconds,
337 338 :exit_status => run_stat,
338 339 :memory_usage => memory_used
339 340 }
340 341 end
341 342
342 343 def confirm_and_update_start_time
343 344 user = User.find(session[:user_id])
344 345 if (GraderConfiguration.indv_contest_mode? and
345 346 GraderConfiguration['contest.confirm_indv_contest_start'] and
346 347 !user.contest_started?)
347 348 redirect_to :action => 'confirm_contest_start' and return
348 349 end
349 350 if not GraderConfiguration.analysis_mode?
350 351 user.update_start_time
351 352 end
352 353 end
353 354
354 355 def reject_announcement_refresh_when_logged_out
355 356 if not session[:user_id]
356 357 render :text => 'Access forbidden', :status => 403
357 358 end
358 359
359 360 if GraderConfiguration.multicontests?
360 361 user = User.find(session[:user_id])
361 362 if user.contest_stat.forced_logout
362 363 render :text => 'Access forbidden', :status => 403
363 364 end
364 365 end
365 366 end
366 367
367 368 end
368 369
@@ -84,443 +84,444
84 84 @scorearray = Array.new
85 85 @users.each do |u|
86 86 ustat = Array.new
87 87 ustat[0] = u
88 88 @problems.each do |p|
89 89 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
90 90 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
91 91 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
92 92 else
93 93 ustat << [0,false]
94 94 end
95 95 end
96 96 @scorearray << ustat
97 97 end
98 98 if params[:commit] == 'download csv' then
99 99 csv = gen_csv_from_scorearray(@scorearray,@problems)
100 100 send_data csv, filename: 'last_score.csv'
101 101 else
102 102 render template: 'user_admin/user_stat'
103 103 end
104 104
105 105 end
106 106
107 107 def login_stat
108 108 @logins = Array.new
109 109
110 110 date_and_time = '%Y-%m-%d %H:%M'
111 111 begin
112 112 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
113 113 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
114 114 rescue
115 115 @since_time = DateTime.new(1000,1,1)
116 116 end
117 117 begin
118 118 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
119 119 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
120 120 rescue
121 121 @until_time = DateTime.new(3000,1,1)
122 122 end
123 123
124 124 User.all.each do |user|
125 125 @logins << { id: user.id,
126 126 login: user.login,
127 127 full_name: user.full_name,
128 128 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
129 129 user.id,@since_time,@until_time)
130 130 .count(:id),
131 131 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
132 132 user.id,@since_time,@until_time)
133 133 .minimum(:created_at),
134 134 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
135 135 user.id,@since_time,@until_time)
136 136 .maximum(:created_at),
137 137 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
138 138 user.id,@since_time,@until_time)
139 139 .select(:ip_address).uniq
140 140
141 141 }
142 142 end
143 143 end
144 144
145 145 def submission_stat
146 146
147 147 date_and_time = '%Y-%m-%d %H:%M'
148 148 begin
149 149 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
150 150 rescue
151 151 @since_time = DateTime.new(1000,1,1)
152 152 end
153 153 begin
154 154 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
155 155 rescue
156 156 @until_time = DateTime.new(3000,1,1)
157 157 end
158 158
159 159 @submissions = {}
160 160
161 161 User.find_each do |user|
162 162 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
163 163 end
164 164
165 165 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
166 166 if @submissions[s.user_id]
167 167 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
168 168 a = Problem.find_by_id(s.problem_id)
169 169 @submissions[s.user_id][:sub][s.problem_id] =
170 170 { prob_name: (a ? a.full_name : '(NULL)'),
171 171 sub_ids: [s.id] }
172 172 else
173 173 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
174 174 end
175 175 @submissions[s.user_id][:count] += 1
176 176 end
177 177 end
178 178 end
179 179
180 180 def problem_hof
181 181 # gen problem list
182 182 @user = User.find(session[:user_id])
183 183 @problems = @user.available_problems
184 184
185 185 # get selected problems or the default
186 186 if params[:id]
187 187 begin
188 188 @problem = Problem.available.find(params[:id])
189 189 rescue
190 190 redirect_to action: :problem_hof
191 191 flash[:notice] = 'Error: submissions for that problem are not viewable.'
192 192 return
193 193 end
194 194 end
195 195
196 196 return unless @problem
197 197
198 198 @by_lang = {} #aggregrate by language
199 199
200 200 range =65
201 201 @histogram = { data: Array.new(range,0), summary: {} }
202 202 @summary = {count: 0, solve: 0, attempt: 0}
203 203 user = Hash.new(0)
204 204 Submission.where(problem_id: @problem.id).find_each do |sub|
205 205 #histogram
206 206 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
207 207 @histogram[:data][d.to_i] += 1 if d < range
208 208
209 209 next unless sub.points
210 210 @summary[:count] += 1
211 211 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
212 212
213 213 lang = Language.find_by_id(sub.language_id)
214 214 next unless lang
215 215 next unless sub.points >= @problem.full_score
216 216
217 217 #initialize
218 218 unless @by_lang.has_key?(lang.pretty_name)
219 219 @by_lang[lang.pretty_name] = {
220 220 runtime: { avail: false, value: 2**30-1 },
221 221 memory: { avail: false, value: 2**30-1 },
222 222 length: { avail: false, value: 2**30-1 },
223 223 first: { avail: false, value: DateTime.new(3000,1,1) }
224 224 }
225 225 end
226 226
227 227 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
228 228 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
229 229 end
230 230
231 231 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
232 232 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
233 233 end
234 234
235 235 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and sub.user and
236 236 !sub.user.admin?
237 237 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
238 238 end
239 239
240 240 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
241 241 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
242 242 end
243 243 end
244 244
245 245 #process user_id
246 246 @by_lang.each do |lang,prop|
247 247 prop.each do |k,v|
248 248 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
249 249 end
250 250 end
251 251
252 252 #sum into best
253 253 if @by_lang and @by_lang.first
254 254 @best = @by_lang.first[1].clone
255 255 @by_lang.each do |lang,prop|
256 256 if @best[:runtime][:value] >= prop[:runtime][:value]
257 257 @best[:runtime] = prop[:runtime]
258 258 @best[:runtime][:lang] = lang
259 259 end
260 260 if @best[:memory][:value] >= prop[:memory][:value]
261 261 @best[:memory] = prop[:memory]
262 262 @best[:memory][:lang] = lang
263 263 end
264 264 if @best[:length][:value] >= prop[:length][:value]
265 265 @best[:length] = prop[:length]
266 266 @best[:length][:lang] = lang
267 267 end
268 268 if @best[:first][:value] >= prop[:first][:value]
269 269 @best[:first] = prop[:first]
270 270 @best[:first][:lang] = lang
271 271 end
272 272 end
273 273 end
274 274
275 275 @histogram[:summary][:max] = [@histogram[:data].max,1].max
276 276 @summary[:attempt] = user.count
277 277 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
278 278 end
279 279
280 280 def stuck #report struggling user,problem
281 281 # init
282 282 user,problem = nil
283 283 solve = true
284 284 tries = 0
285 285 @struggle = Array.new
286 286 record = {}
287 287 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
288 288 next unless sub.problem and sub.user
289 289 if user != sub.user_id or problem != sub.problem_id
290 290 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
291 291 record = {user: sub.user, problem: sub.problem}
292 292 user,problem = sub.user_id, sub.problem_id
293 293 solve = false
294 294 tries = 0
295 295 end
296 296 if sub.points >= sub.problem.full_score
297 297 solve = true
298 298 else
299 299 tries += 1
300 300 end
301 301 end
302 302 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
303 303 @struggle = @struggle[0..50]
304 304 end
305 305
306 306
307 307 def multiple_login
308 308 #user with multiple IP
309 309 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
310 310 last,count = 0,0
311 311 first = 0
312 312 @users = []
313 313 raw.each do |r|
314 314 if last != r.user.login
315 315 count = 1
316 316 last = r.user.login
317 317 first = r
318 318 else
319 319 @users << first if count == 1
320 320 @users << r
321 321 count += 1
322 322 end
323 323 end
324 324
325 325 #IP with multiple user
326 326 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
327 327 last,count = 0,0
328 328 first = 0
329 329 @ip = []
330 330 raw.each do |r|
331 331 if last != r.ip_address
332 332 count = 1
333 333 last = r.ip_address
334 334 first = r
335 335 else
336 336 @ip << first if count == 1
337 337 @ip << r
338 338 count += 1
339 339 end
340 340 end
341 341 end
342 342
343 343 def cheat_report
344 344 date_and_time = '%Y-%m-%d %H:%M'
345 345 begin
346 346 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
347 347 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
348 348 rescue
349 349 @since_time = Time.zone.now.ago( 90.minutes)
350 350 end
351 351 begin
352 352 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
353 353 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
354 354 rescue
355 355 @until_time = Time.zone.now
356 356 end
357 357
358 358 #multi login
359 359 @ml = Login.joins(:user).where("logins.created_at >= ? and logins.created_at <= ?",@since_time,@until_time).select('users.login,count(distinct ip_address) as count,users.full_name').group("users.id").having("count > 1")
360 360
361 361 st = <<-SQL
362 362 SELECT l2.*
363 363 FROM logins l2 INNER JOIN
364 364 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
365 365 FROM logins l
366 366 INNER JOIN users u ON l.user_id = u.id
367 367 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
368 368 GROUP BY u.id
369 369 HAVING count > 1
370 370 ) ml ON l2.user_id = ml.id
371 371 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
372 372 UNION
373 373 SELECT l2.*
374 374 FROM logins l2 INNER JOIN
375 375 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
376 376 FROM logins l
377 377 INNER JOIN users u ON l.user_id = u.id
378 378 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
379 379 GROUP BY l.ip_address
380 380 HAVING count > 1
381 381 ) ml on ml.ip_address = l2.ip_address
382 382 INNER JOIN users u ON l2.user_id = u.id
383 383 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
384 384 ORDER BY ip_address,created_at
385 385 SQL
386 386 @mld = Login.find_by_sql(st)
387 387
388 388 st = <<-SQL
389 389 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
390 390 FROM submissions s INNER JOIN
391 391 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
392 392 FROM logins l
393 393 INNER JOIN users u ON l.user_id = u.id
394 394 WHERE l.created_at >= ? and l.created_at <= ?
395 395 GROUP BY u.id
396 396 HAVING count > 1
397 397 ) ml ON s.user_id = ml.id
398 398 WHERE s.submitted_at >= ? and s.submitted_at <= ?
399 399 UNION
400 400 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
401 401 FROM submissions s INNER JOIN
402 402 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
403 403 FROM logins l
404 404 INNER JOIN users u ON l.user_id = u.id
405 405 WHERE l.created_at >= ? and l.created_at <= ?
406 406 GROUP BY l.ip_address
407 407 HAVING count > 1
408 408 ) ml on ml.ip_address = s.ip_address
409 409 WHERE s.submitted_at >= ? and s.submitted_at <= ?
410 410 ORDER BY ip_address,submitted_at
411 411 SQL
412 412 @subs = Submission.joins(:problem).find_by_sql([st,@since_time,@until_time,
413 413 @since_time,@until_time,
414 414 @since_time,@until_time,
415 415 @since_time,@until_time])
416 416
417 417 end
418 418
419 419 def cheat_scruntinize
420 420 #convert date & time
421 421 date_and_time = '%Y-%m-%d %H:%M'
422 422 begin
423 423 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
424 424 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
425 425 rescue
426 426 @since_time = Time.zone.now.ago( 90.minutes)
427 427 end
428 428 begin
429 429 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
430 430 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
431 431 rescue
432 432 @until_time = Time.zone.now
433 433 end
434 434
435 435 #convert sid
436 436 @sid = params[:SID].split(/[,\s]/) if params[:SID]
437 437 unless @sid and @sid.size > 0
438 438 return
439 439 redirect_to actoin: :cheat_scruntinize
440 440 flash[:notice] = 'Please enter at least 1 student id'
441 441 end
442 442 mark = Array.new(@sid.size,'?')
443 443 condition = "(u.login = " + mark.join(' OR u.login = ') + ')'
444 444
445 445 @st = <<-SQL
446 446 SELECT l.created_at as submitted_at ,-1 as id,u.login,u.full_name,l.ip_address,"" as problem_id,"" as points,l.user_id
447 447 FROM logins l INNER JOIN users u on l.user_id = u.id
448 448 WHERE l.created_at >= ? AND l.created_at <= ? AND #{condition}
449 449 UNION
450 450 SELECT s.submitted_at,s.id,u.login,u.full_name,s.ip_address,s.problem_id,s.points,s.user_id
451 451 FROM submissions s INNER JOIN users u ON s.user_id = u.id
452 452 WHERE s.submitted_at >= ? AND s.submitted_at <= ? AND #{condition}
453 453 ORDER BY submitted_at
454 454 SQL
455 455
456 456 p = [@st,@since_time,@until_time] + @sid + [@since_time,@until_time] + @sid
457 457 @logs = Submission.joins(:problem).find_by_sql(p)
458 458
459 459
460 460
461 461
462 462
463 463 end
464 464
465 465 protected
466 466
467 467 def calculate_max_score(problems, users,since_id,until_id, get_last_score = false)
468 + #scorearray[i] = user #i's user stat where i is the index (not id)
468 469 scorearray = Array.new
469 470 users.each do |u|
470 471 ustat = Array.new
471 472 ustat[0] = u
472 473 problems.each do |p|
473 474 unless get_last_score
474 475 #get max score
475 476 max_points = 0
476 477 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
477 478 max_points = sub.points if sub and sub.points and (sub.points > max_points)
478 479 end
479 480 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
480 481 else
481 482 #get latest score
482 483 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
483 484 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
484 485 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
485 486 else
486 487 ustat << [0,false]
487 488 end
488 489 end
489 490 end
490 491 scorearray << ustat
491 492 end
492 493 return scorearray
493 494 end
494 495
495 496 def gen_csv_from_scorearray(scorearray,problem)
496 497 CSV.generate do |csv|
497 498 #add header
498 499 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
499 500 problem.each { |p| header << p.name }
500 501 header += ['Total','Passed']
501 502 csv << header
502 503 #add data
503 504 scorearray.each do |sc|
504 505 total = num_passed = 0
505 506 row = Array.new
506 507 sc.each_index do |i|
507 508 if i == 0
508 509 row << sc[i].login
509 510 row << sc[i].full_name
510 511 row << sc[i].activated
511 512 row << (sc[i].try(:contest_stat).try(:started_at)!=nil ? 'yes' : 'no')
512 513 row << sc[i].contests.collect {|c| c.name}.join(', ')
513 514 else
514 515 row << sc[i][0]
515 516 total += sc[i][0]
516 517 num_passed += 1 if sc[i][1]
517 518 end
518 519 end
519 520 row << total
520 521 row << num_passed
521 522 csv << row
522 523 end
523 524 end
524 525 end
525 526
526 527 end
@@ -1,115 +1,112
1 1 class SubmissionsController < ApplicationController
2 2 before_action :check_valid_login
3 3 before_action :submission_authorization, only: [:show, :download, :edit]
4 4 before_action :admin_authorization, only: [:rejudge]
5 5
6 6 # GET /submissions
7 7 # GET /submissions.json
8 8 # Show problem selection and user's submission of that problem
9 9 def index
10 10 @user = @current_user
11 11 @problems = @user.available_problems
12 12
13 13 if params[:problem_id]==nil
14 14 @problem = nil
15 15 @submissions = nil
16 16 else
17 17 @problem = Problem.find_by_id(params[:problem_id])
18 18 if (@problem == nil) or (not @problem.available)
19 19 redirect_to main_list_path
20 20 flash[:notice] = 'Error: submissions for that problem are not viewable.'
21 21 return
22 22 end
23 23 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc)
24 24 end
25 25 end
26 26
27 27 # GET /submissions/1
28 28 # GET /submissions/1.json
29 29 def show
30 30 @submission = Submission.find(params[:id])
31 31
32 32 #log the viewing
33 33 user = User.find(session[:user_id])
34 34 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
35 35
36 36 @task = @submission.task
37 37 end
38 38
39 39 def download
40 40 @submission = Submission.find(params[:id])
41 41 send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
42 42 end
43 43
44 44 def compiler_msg
45 45 @submission = Submission.find(params[:id])
46 46 respond_to do |format|
47 47 format.js
48 48 end
49 49 end
50 50
51 51 #on-site new submission on specific problem
52 52 def direct_edit_problem
53 53 @problem = Problem.find(params[:problem_id])
54 54 unless @current_user.can_view_problem?(@problem)
55 55 unauthorized_redirect
56 56 return
57 57 end
58 58 @source = ''
59 59 if (params[:view_latest])
60 60 sub = Submission.find_last_by_user_and_problem(@current_user.id,@problem.id)
61 61 @source = @submission.source.to_s if @submission and @submission.source
62 62 end
63 63 render 'edit'
64 64 end
65 65
66 66 # GET /submissions/1/edit
67 67 def edit
68 68 @submission = Submission.find(params[:id])
69 69 @source = @submission.source.to_s
70 70 @problem = @submission.problem
71 71 @lang_id = @submission.language.id
72 72 end
73 73
74 74
75 75 def get_latest_submission_status
76 76 @problem = Problem.find(params[:pid])
77 77 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
78 - puts User.find(params[:uid]).login
79 - puts Problem.find(params[:pid]).name
80 - puts 'nil' unless @submission
81 78 respond_to do |format|
82 79 format.js
83 80 end
84 81 end
85 82
86 83 # GET /submissions/:id/rejudge
87 84 def rejudge
88 85 @submission = Submission.find(params[:id])
89 86 @task = @submission.task
90 87 @task.status_inqueue! if @task
91 88 respond_to do |format|
92 89 format.js
93 90 end
94 91 end
95 92
96 93 protected
97 94
98 95 def submission_authorization
99 96 #admin always has privileged
100 97 if @current_user.admin?
101 98 return true
102 99 end
103 100
104 101 sub = Submission.find(params[:id])
105 102 if @current_user.available_problems.include? sub.problem
106 103 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
107 104 end
108 105
109 106 #default to NO
110 107 unauthorized_redirect
111 108 return false
112 109 end
113 110
114 111
115 112 end
@@ -1,518 +1,522
1 1 require 'csv'
2 2
3 3 class UserAdminController < ApplicationController
4 4
5 5 include MailHelperMethods
6 6
7 7 before_action :admin_authorization
8 8
9 9 def index
10 10 @user_count = User.count
11 11 if params[:page] == 'all'
12 12 @users = User.all
13 13 @paginated = false
14 14 else
15 15 @users = User.paginate :page => params[:page]
16 16 @paginated = true
17 17 end
18 18 @users = User.all
19 19 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
20 20 @contests = Contest.enabled
21 21 end
22 22
23 23 def active
24 24 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
25 25 @users = []
26 26 sessions.each do |session|
27 27 if session.data[:user_id]
28 28 @users << User.find(session.data[:user_id])
29 29 end
30 30 end
31 31 end
32 32
33 33 def show
34 34 @user = User.find(params[:id])
35 35 end
36 36
37 37 def new
38 38 @user = User.new
39 39 end
40 40
41 41 def create
42 42 @user = User.new(user_params)
43 43 @user.activated = true
44 44 if @user.save
45 45 flash[:notice] = 'User was successfully created.'
46 46 redirect_to :action => 'index'
47 47 else
48 48 render :action => 'new'
49 49 end
50 50 end
51 51
52 52 def clear_last_ip
53 53 @user = User.find(params[:id])
54 54 @user.last_ip = nil
55 55 @user.save
56 56 redirect_to action: 'index', page: params[:page]
57 57 end
58 58
59 59 def create_from_list
60 60 lines = params[:user_list]
61 61
62 62 note = []
63 63 error_note = []
64 + error_msg = nil
64 65 ok_user = []
65 66
66 67 lines.split("\n").each do |line|
67 68 items = line.chomp.split(',')
68 69 if items.length>=2
69 70 login = items[0]
70 71 full_name = items[1]
71 72 remark =''
72 73 user_alias = ''
73 74
74 75 added_random_password = false
75 76 if items.length >= 3 and items[2].chomp(" ").length > 0;
76 77 password = items[2].chomp(" ")
77 78 else
78 79 password = random_password
79 - add_random_password=true;
80 + added_random_password=true;
80 81 end
81 82
82 83 if items.length>= 4 and items[3].chomp(" ").length > 0;
83 84 user_alias = items[3].chomp(" ")
84 85 else
85 86 user_alias = login
86 87 end
87 88
88 89 if items.length>=5
89 90 remark = items[4].strip;
90 91 end
91 92
92 93 user = User.find_by_login(login)
93 94 if (user)
94 95 user.full_name = full_name
95 96 user.password = password
96 97 user.remark = remark
97 98 else
98 99 user = User.new({:login => login,
99 100 :full_name => full_name,
100 101 :password => password,
101 102 :password_confirmation => password,
102 103 :alias => user_alias,
103 104 :remark => remark})
104 105 end
105 106 user.activated = true
106 107
107 108 if user.save
108 109 if added_random_password
109 110 note << "'#{login}' (+)"
110 111 else
111 112 note << login
112 113 end
113 114 ok_user << user
114 115 else
115 - error_note << "#{login}"
116 + error_note << "'#{login}'"
117 + error_msg = user.errors.full_messages.to_sentence unless error_msg
116 118 end
117 119
118 120 end
119 121 end
120 122
121 123 #add to group
122 124 if params[:add_to_group]
123 125 group = Group.where(id: params[:group_id]).first
124 126 if group
125 127 group.users << ok_user
126 128 end
127 129 end
128 130
129 131 # show flash
132 + if note.size > 0
130 133 flash[:success] = 'User(s) ' + note.join(', ') +
131 134 ' were successfully created. ' +
132 135 '( (+) - created with random passwords.)'
136 + end
133 137 if error_note.size > 0
134 - flash[:error] = "Following user(s) failed to be created: " + error_note.join(', ')
138 + flash[:error] = "Following user(s) failed to be created: " + error_note.join(', ') + ". The error of the first failed one are: " + error_msg;
135 139 end
136 140 redirect_to :action => 'index'
137 141 end
138 142
139 143 def edit
140 144 @user = User.find(params[:id])
141 145 end
142 146
143 147 def update
144 148 @user = User.find(params[:id])
145 149 if @user.update_attributes(user_params)
146 150 flash[:notice] = 'User was successfully updated.'
147 151 redirect_to :action => 'show', :id => @user
148 152 else
149 153 render :action => 'edit'
150 154 end
151 155 end
152 156
153 157 def destroy
154 158 User.find(params[:id]).destroy
155 159 redirect_to :action => 'index'
156 160 end
157 161
158 162 def user_stat
159 163 if params[:commit] == 'download csv'
160 164 @problems = Problem.all
161 165 else
162 166 @problems = Problem.available_problems
163 167 end
164 168 @users = User.includes(:contests, :contest_stat).where(enabled: true)
165 169 @scorearray = Array.new
166 170 @users.each do |u|
167 171 ustat = Array.new
168 172 ustat[0] = u
169 173 @problems.each do |p|
170 174 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
171 175 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
172 176 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
173 177 else
174 178 ustat << [0,false]
175 179 end
176 180 end
177 181 @scorearray << ustat
178 182 end
179 183 if params[:commit] == 'download csv' then
180 184 csv = gen_csv_from_scorearray(@scorearray,@problems)
181 185 send_data csv, filename: 'last_score.csv'
182 186 else
183 187 render template: 'user_admin/user_stat'
184 188 end
185 189 end
186 190
187 191 def user_stat_max
188 192 if params[:commit] == 'download csv'
189 193 @problems = Problem.all
190 194 else
191 195 @problems = Problem.available_problems
192 196 end
193 197 @users = User.includes(:contests).includes(:contest_stat).all
194 198 @scorearray = Array.new
195 199 #set up range from param
196 200 since_id = params.fetch(:since_id, 0).to_i
197 201 until_id = params.fetch(:until_id, 0).to_i
198 202 @users.each do |u|
199 203 ustat = Array.new
200 204 ustat[0] = u
201 205 @problems.each do |p|
202 206 max_points = 0
203 207 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
204 208 max_points = sub.points if sub and sub.points and (sub.points > max_points)
205 209 end
206 210 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
207 211 end
208 212 @scorearray << ustat
209 213 end
210 214
211 215 if params[:commit] == 'download csv' then
212 216 csv = gen_csv_from_scorearray(@scorearray,@problems)
213 217 send_data csv, filename: 'max_score.csv'
214 218 else
215 219 render template: 'user_admin/user_stat'
216 220 end
217 221 end
218 222
219 223 def import
220 224 if params[:file]==''
221 225 flash[:notice] = 'Error importing no file'
222 226 redirect_to :action => 'index' and return
223 227 end
224 228 import_from_file(params[:file])
225 229 end
226 230
227 231 def random_all_passwords
228 232 users = User.all
229 233 @prefix = params[:prefix] || ''
230 234 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
231 235 @changed = false
232 236 if params[:commit] == 'Go ahead'
233 237 @non_admin_users.each do |user|
234 238 password = random_password
235 239 user.password = password
236 240 user.password_confirmation = password
237 241 user.save
238 242 end
239 243 @changed = true
240 244 end
241 245 end
242 246
243 247 # contest management
244 248
245 249 def contests
246 250 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
247 251 @contests = Contest.enabled
248 252 end
249 253
250 254 def assign_from_list
251 255 contest_id = params[:users_contest_id]
252 256 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
253 257 contest = Contest.find(params[:new_contest][:id])
254 258 if !contest
255 259 flash[:notice] = 'Error: no contest'
256 260 redirect_to :action => 'contests', :id =>contest_id
257 261 end
258 262
259 263 note = []
260 264 users.each do |u|
261 265 u.contests = [contest]
262 266 note << u.login
263 267 end
264 268 flash[:notice] = 'User(s) ' + note.join(', ') +
265 269 " were successfully reassigned to #{contest.title}."
266 270 redirect_to :action => 'contests', :id =>contest.id
267 271 end
268 272
269 273 def add_to_contest
270 274 user = User.find(params[:id])
271 275 contest = Contest.find(params[:contest_id])
272 276 if user and contest
273 277 user.contests << contest
274 278 end
275 279 redirect_to :action => 'index'
276 280 end
277 281
278 282 def remove_from_contest
279 283 user = User.find(params[:id])
280 284 contest = Contest.find(params[:contest_id])
281 285 if user and contest
282 286 user.contests.delete(contest)
283 287 end
284 288 redirect_to :action => 'index'
285 289 end
286 290
287 291 def contest_management
288 292 end
289 293
290 294 def manage_contest
291 295 contest = Contest.find(params[:contest][:id])
292 296 if !contest
293 297 flash[:notice] = 'You did not choose the contest.'
294 298 redirect_to :action => 'contest_management' and return
295 299 end
296 300
297 301 operation = params[:operation]
298 302
299 303 if not ['add','remove','assign'].include? operation
300 304 flash[:notice] = 'You did not choose the operation to perform.'
301 305 redirect_to :action => 'contest_management' and return
302 306 end
303 307
304 308 lines = params[:login_list]
305 309 if !lines or lines.blank?
306 310 flash[:notice] = 'You entered an empty list.'
307 311 redirect_to :action => 'contest_management' and return
308 312 end
309 313
310 314 note = []
311 315 users = []
312 316 lines.split("\n").each do |line|
313 317 user = User.find_by_login(line.chomp)
314 318 if user
315 319 if operation=='add'
316 320 if ! user.contests.include? contest
317 321 user.contests << contest
318 322 end
319 323 elsif operation=='remove'
320 324 user.contests.delete(contest)
321 325 else
322 326 user.contests = [contest]
323 327 end
324 328
325 329 if params[:reset_timer]
326 330 user.contest_stat.forced_logout = true
327 331 user.contest_stat.reset_timer_and_save
328 332 end
329 333
330 334 if params[:notification_emails]
331 335 send_contest_update_notification_email(user, contest)
332 336 end
333 337
334 338 note << user.login
335 339 users << user
336 340 end
337 341 end
338 342
339 343 if params[:reset_timer]
340 344 logout_users(users)
341 345 end
342 346
343 347 flash[:notice] = 'User(s) ' + note.join(', ') +
344 348 ' were successfully modified. '
345 349 redirect_to :action => 'contest_management'
346 350 end
347 351
348 352 # admin management
349 353
350 354 def admin
351 355 @admins = User.all.find_all {|user| user.admin? }
352 356 end
353 357
354 358 def grant_admin
355 359 login = params[:login]
356 360 user = User.find_by_login(login)
357 361 if user!=nil
358 362 admin_role = Role.find_by_name('admin')
359 363 user.roles << admin_role
360 364 else
361 365 flash[:notice] = 'Unknown user'
362 366 end
363 367 flash[:notice] = 'User added as admins'
364 368 redirect_to :action => 'admin'
365 369 end
366 370
367 371 def revoke_admin
368 372 user = User.find(params[:id])
369 373 if user==nil
370 374 flash[:notice] = 'Unknown user'
371 375 redirect_to :action => 'admin' and return
372 376 elsif user.login == 'root'
373 377 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
374 378 redirect_to :action => 'admin' and return
375 379 end
376 380
377 381 admin_role = Role.find_by_name('admin')
378 382 user.roles.delete(admin_role)
379 383 flash[:notice] = 'User permission revoked'
380 384 redirect_to :action => 'admin'
381 385 end
382 386
383 387 # mass mailing
384 388
385 389 def mass_mailing
386 390 end
387 391
388 392 def bulk_mail
389 393 lines = params[:login_list]
390 394 if !lines or lines.blank?
391 395 flash[:notice] = 'You entered an empty list.'
392 396 redirect_to :action => 'mass_mailing' and return
393 397 end
394 398
395 399 mail_subject = params[:subject]
396 400 if !mail_subject or mail_subject.blank?
397 401 flash[:notice] = 'You entered an empty mail subject.'
398 402 redirect_to :action => 'mass_mailing' and return
399 403 end
400 404
401 405 mail_body = params[:email_body]
402 406 if !mail_body or mail_body.blank?
403 407 flash[:notice] = 'You entered an empty mail body.'
404 408 redirect_to :action => 'mass_mailing' and return
405 409 end
406 410
407 411 note = []
408 412 users = []
409 413 lines.split("\n").each do |line|
410 414 user = User.find_by_login(line.chomp)
411 415 if user
412 416 send_mail(user.email, mail_subject, mail_body)
413 417 note << user.login
414 418 end
415 419 end
416 420
417 421 flash[:notice] = 'User(s) ' + note.join(', ') +
418 422 ' were successfully modified. '
419 423 redirect_to :action => 'mass_mailing'
420 424 end
421 425
422 426 #bulk manage
423 427 def bulk_manage
424 428
425 429 begin
426 430 @users = User.where('(login REGEXP ?) OR (remark REGEXP ?)',params[:regex],params[:regex]) if params[:regex]
427 431 @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised
428 432 rescue Exception
429 433 flash[:error] = 'Regular Expression is malformed'
430 434 @users = nil
431 435 end
432 436
433 437 if params[:commit]
434 438 @action = {}
435 439 @action[:set_enable] = params[:enabled]
436 440 @action[:enabled] = params[:enable] == "1"
437 441 @action[:gen_password] = params[:gen_password]
438 442 @action[:add_group] = params[:add_group]
439 443 @action[:group_name] = params[:group_name]
440 444 end
441 445
442 446 if params[:commit] == "Perform"
443 447 if @action[:set_enable]
444 448 @users.update_all(enabled: @action[:enabled])
445 449 end
446 450 if @action[:gen_password]
447 451 @users.each do |u|
448 452 password = random_password
449 453 u.password = password
450 454 u.password_confirmation = password
451 455 u.save
452 456 end
453 457 end
454 458 if @action[:add_group] and @action[:group_name]
455 459 @group = Group.find(@action[:group_name])
456 460 ok = []
457 461 failed = []
458 462 @users.each do |user|
459 463 begin
460 464 @group.users << user
461 465 ok << user.login
462 466 rescue => e
463 467 failed << user.login
464 468 end
465 469 end
466 470 flash[:success] = "The following users are added to the 'group #{@group.name}': " + ok.join(', ') if ok.count > 0
467 471 flash[:alert] = "The following users are already in the 'group #{@group.name}': " + failed.join(', ') if failed.count > 0
468 472 end
469 473 end
470 474 end
471 475
472 476 protected
473 477
474 478 def random_password(length=5)
475 479 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
476 480 newpass = ""
477 481 length.times { newpass << chars[rand(chars.size-1)] }
478 482 return newpass
479 483 end
480 484
481 485 def import_from_file(f)
482 486 data_hash = YAML.load(f)
483 487 @import_log = ""
484 488
485 489 country_data = data_hash[:countries]
486 490 site_data = data_hash[:sites]
487 491 user_data = data_hash[:users]
488 492
489 493 # import country
490 494 countries = {}
491 495 country_data.each_pair do |id,country|
492 496 c = Country.find_by_name(country[:name])
493 497 if c!=nil
494 498 countries[id] = c
495 499 @import_log << "Found #{country[:name]}\n"
496 500 else
497 501 countries[id] = Country.new(:name => country[:name])
498 502 countries[id].save
499 503 @import_log << "Created #{country[:name]}\n"
500 504 end
501 505 end
502 506
503 507 # import sites
504 508 sites = {}
505 509 site_data.each_pair do |id,site|
506 510 s = Site.find_by_name(site[:name])
507 511 if s!=nil
508 512 @import_log << "Found #{site[:name]}\n"
509 513 else
510 514 s = Site.new(:name => site[:name])
511 515 @import_log << "Created #{site[:name]}\n"
512 516 end
513 517 s.password = site[:password]
514 518 s.country = countries[site[:country_id]]
515 519 s.save
516 520 sites[id] = s
517 521 end
518 522
@@ -1,80 +1,82
1 1 .container-fluid
2 2 .row
3 3 .col-md-6
4 4 %h1 Group #{@group.name}
5 5 .row
6 6 .col-md-6
7 7 %b Description:
8 8 = @group.description
9 9 %br
10 10 = link_to 'Edit', edit_group_path(@group), class: 'btn btn-primary'
11 11 .row
12 12 .col-md-12
13 13 %h1 Group details
14 14 .row
15 15 .col-md-6
16 16 .panel.panel-default
17 17 .panel-heading
18 18 .panel-title Users in this group
19 19 .panel-body
20 20 %ul
21 21 %li
22 22 If you want to add several users to a group, it may be easier to just re-import those users in
23 23 = link_to 'New list of users', new_list_user_admin_index_path
24 - page
24 + page. You can also use
25 + = link_to 'Bulk Manage User', bulk_manage_user_admin_index_path
26 + page.
25 27 =form_tag add_user_group_path(@group), class: 'form-inline' do
26 28 .form-group
27 29 =label_tag :user_id, "User"
28 - =select_tag :user_id, options_from_collection_for_select(User.all,'id','full_name'), class: 'select2'
30 + =select_tag :user_id, options_from_collection_for_select(User.all,'id','full_name'), class: 'select2', style: 'width: 10em';
29 31 =submit_tag "Add",class: 'btn btn-primary'
30 32
31 33
32 34 %table.table.table-hover
33 35 %thead
34 36 %tr
35 37 %th Login
36 38 %th Full name
37 39 %th Remark
38 40 %th= link_to 'Remove All', remove_all_user_group_path(@group), method: :delete, :data => { :confirm => "Remove ALL USERS from group?" }, class: 'btn btn-danger btn-sm'
39 41
40 42 %tbody
41 43 - @group.users.each do |user|
42 44 %tr
43 45 %td= user.login
44 46 %td= user.full_name
45 47 %td= user.remark
46 48 %td= link_to 'Remove', remove_user_group_path(@group,user), :method => :delete, :data => { :confirm => "Remove #{user.full_name}?" }, class: 'btn btn-danger btn-sm'
47 49 .col-md-6
48 50 .panel.panel-default
49 51 .panel-heading
50 52 .panel-title Problems
51 53 .panel-body
52 54 %ul
53 55 %li
54 56 If you want to add several problem to a group, it may be easier to bulk manage them in the
55 - = link_to 'Bulk Manage', manage_problems_path
57 + = link_to 'Bulk Manage Problems', manage_problems_path
56 58 page
57 59 =form_tag add_problem_group_path(@group), class: 'form-inline' do
58 60 .form-group
59 61 =label_tag :problem_id, "Problem"
60 - =select_tag :problem_id, options_from_collection_for_select(Problem.all,'id','full_name'), class: 'select2'
62 + =select_tag :problem_id, options_from_collection_for_select(Problem.all,'id','full_name'), class: 'select2', style: 'width: 10em';
61 63 =submit_tag "Add",class: 'btn btn-primary'
62 64
63 65
64 66 %table.table.table-hover
65 67 %thead
66 68 %tr
67 69 %th name
68 70 %th Full name
69 71 %th Full score
70 72 %th= link_to 'Remove All', remove_all_problem_group_path(@group), method: :delete, :data => { :confirm => "Remove ALL PROBLEMS from group?" }, class: 'btn btn-danger btn-sm'
71 73
72 74 %tbody
73 75 - @group.problems.each do |problem|
74 76 %tr
75 77 %td= problem.name
76 78 %td= problem.full_name
77 79 %td= problem.full_score
78 80 %td= link_to 'Remove', remove_problem_group_path(@group,problem), :method => :delete, :data => { :confirm => "Remove #{problem.full_name}?" }, class: 'btn btn-danger btn-sm'
79 81
80 82
@@ -1,11 +1,12
1 1 %h1= GraderConfiguration['ui.front.title']
2 2
3 3 .row
4 4 .col-md-6
5 5 - if @announcements.length!=0
6 6 .announcementbox{:style => 'margin-top: 0px'}
7 7 %span{:class => 'title'}
8 8 Announcements
9 9 = render :partial => 'announcement', :collection => @announcements
10 10 .col-md-4{style: "padding-left: 20px;"}
11 11 = render :partial => 'login_box'
12 + = "current ip is #{@remote_ip}"
@@ -1,69 +1,69
1 1 %table.table.sortable.table-striped.table-bordered.table-condensed
2 2 %thead
3 3 %tr
4 4 %th Login
5 5 %th Name
6 6 / %th Activated?
7 7 / %th Logged_in
8 8 / %th Contest(s)
9 9 %th Remark
10 10 - @problems.each do |p|
11 11 %th.text-right= p.name.gsub('_',' ')
12 12 %th.text-right Total
13 13 %th.text-right Passed
14 14 %tbody
15 - - sum = Array.new(@scorearray[0].count,0)
16 - - nonzero = Array.new(@scorearray[0].count,0)
17 - - full = Array.new(@scorearray[0].count,0)
15 + - sum = Array.new(@problems.count+1,0)
16 + - nonzero = Array.new(@problems.count+1,0)
17 + - full = Array.new(@problems.count+1,0)
18 18 - @scorearray.each do |sc|
19 19 %tr
20 20 - total,num_passed = 0,0
21 21 - sc.each_index do |i|
22 22 - if i == 0
23 23 %td= link_to sc[i].login, stat_user_path(sc[i])
24 24 %td= sc[i].full_name
25 25 / %td= sc[i].activated
26 26 / %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no'
27 27 / %td= sc[i].contests.collect {|c| c.name}.join(', ')
28 28 %td= sc[i].remark
29 29 - else
30 30 %td.text-right= sc[i][0]
31 31 - total += sc[i][0]
32 32 - num_passed += 1 if sc[i][1]
33 33 - sum[i] += sc[i][0]
34 34 - nonzero[i] += 1 if sc[i][0] > 0
35 35 - full[i] += 1 if sc[i][1]
36 36 %td.text-right= total
37 37 %td.text-right= num_passed
38 38 %tfoot
39 39 %tr
40 40 %td Summation
41 41 %td
42 42 %td
43 43 - sum.each.with_index do |s,i|
44 44 - next if i == 0
45 45 %td.text-right= number_with_delimiter(s)
46 46 %td
47 47 %td
48 48 %tr
49 49 %td partial solver
50 50 %td
51 51 %td
52 52 - nonzero.each.with_index do |s,i|
53 53 - next if i == 0
54 54 %td.text-right= number_with_delimiter(s)
55 55 %td
56 56 %td
57 57 %tr
58 58 %td Full solver
59 59 %td
60 60 %td
61 61 - full.each.with_index do |s,i|
62 62 - next if i == 0
63 63 %td.text-right= number_with_delimiter(s)
64 64 %td
65 65 %td
66 66
67 67
68 68 :javascript
69 69 $.bootstrapSortable(true,'reversed')
@@ -1,11 +1,11
1 1 .container-fluid
2 2 %h1 Current Score
3 3 = form_tag current_score_report_path, method: 'get' do
4 4 Show only users from this group
5 - = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
5 + = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_id]), id: 'group_name',class: 'select2', style: 'width: 20em';
6 6 = submit_tag 'Apply',class: 'btn btn-default'
7 7
8 8 %br
9 9
10 10
11 11 = render "score_table"
@@ -1,205 +1,204
1 1 Rails.application.routes.draw do
2 2 resources :tags
3 3 get "sources/direct_edit"
4 4
5 5 root :to => 'main#login'
6 6
7 7 #logins
8 8 match 'login/login', to: 'login#login', via: [:get,:post]
9 9
10 10 resources :contests
11 11 resources :sites
12 12 resources :test
13 13
14 14 resources :messages do
15 15 member do
16 16 get 'hide'
17 17 post 'reply'
18 18 end
19 19 collection do
20 20 get 'console'
21 21 get 'list_all'
22 22 end
23 23 end
24 24
25 25 resources :announcements do
26 26 member do
27 27 get 'toggle','toggle_front'
28 28 end
29 29 end
30 30
31 31 resources :problems do
32 32 member do
33 33 get 'toggle'
34 34 get 'toggle_test'
35 35 get 'toggle_view_testcase'
36 36 get 'stat'
37 37 end
38 38 collection do
39 39 get 'turn_all_off'
40 40 get 'turn_all_on'
41 41 get 'import'
42 42 get 'manage'
43 43 get 'quick_create'
44 44 post 'do_manage'
45 45 post 'do_import'
46 46 end
47 47 end
48 48
49 49 resources :groups do
50 50 member do
51 51 post 'add_user', to: 'groups#add_user', as: 'add_user'
52 52 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
53 53 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
54 54 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
55 55 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
56 56 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
57 57 end
58 58 collection do
59 59
60 60 end
61 61 end
62 62
63 63 resources :testcases, only: [] do
64 64 member do
65 65 get 'download_input'
66 66 get 'download_sol'
67 67 end
68 68 collection do
69 69 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
70 70 end
71 71 end
72 72
73 73 resources :grader_configuration, controller: 'configurations' do
74 74 collection do
75 75 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
76 76 end
77 77 end
78 78
79 79 resources :users do
80 80 member do
81 81 get 'toggle_activate', 'toggle_enable'
82 82 get 'stat'
83 83 end
84 84 collection do
85 85 get 'profile'
86 86 post 'chg_passwd'
87 87 end
88 88 end
89 89
90 90 resources :submissions do
91 91 member do
92 92 get 'download'
93 93 get 'compiler_msg'
94 94 get 'rejudge'
95 95 get 'source'
96 96 end
97 97 collection do
98 98 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
99 99 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
100 100 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
101 101 end
102 102 end
103 103
104 104
105 105 #user admin
106 106 resources :user_admin do
107 107 collection do
108 108 match 'bulk_manage', via: [:get, :post]
109 109 get 'bulk_mail'
110 110 get 'user_stat'
111 111 get 'import'
112 112 get 'new_list'
113 113 get 'admin'
114 114 get 'active'
115 115 get 'mass_mailing'
116 116 get 'revoke_admin'
117 117 post 'grant_admin'
118 118 match 'create_from_list', via: [:get, :post]
119 119 match 'random_all_passwords', via: [:get, :post]
120 120 end
121 121 member do
122 122 get 'clear_last_ip'
123 123 end
124 124 end
125 125
126 126 resources :contest_management, only: [:index] do
127 127 collection do
128 128 get 'user_stat'
129 129 get 'clear_stat'
130 130 get 'clear_all_stat'
131 131 get 'change_contest_mode'
132 132 end
133 133 end
134 134
135 135 #get 'user_admin', to: 'user_admin#index'
136 136 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
137 137 #post 'user_admin', to: 'user_admin#create'
138 138 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
139 139
140 140 #singular resource
141 141 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
142 142 #report
143 143 resource :report, only: [], controller: 'report' do
144 144 get 'login'
145 145 get 'multiple_login'
146 146 get 'problem_hof(/:id)', action: 'problem_hof', as: 'problem_hof'
147 147 get 'current_score(/:group_id)', action: 'current_score', as: 'current_score'
148 148 get 'max_score'
149 149 post 'show_max_score'
150 - get 'problem_hof(/:id)', action: 'problem_hof', as: 'problem_hof'
151 150 get 'stuck'
152 151 get 'cheat_report'
153 152 post 'cheat_report'
154 153 get 'cheat_scruntinize'
155 154 post 'cheat_scruntinize'
156 155 end
157 156 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
158 157 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
159 158 #get "report/login"
160 159 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
161 160 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
162 161
163 162 resource :main, only: [], controller: 'main' do
164 163 get 'login'
165 164 get 'logout'
166 165 get 'list'
167 166 get 'submission(/:id)', action: 'submission', as: 'main_submission'
168 167 get 'announcements'
169 168 get 'help'
170 169 post 'submit'
171 170 end
172 171 #main
173 172 #get "main/list"
174 173 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
175 174 #post 'main/submit', to: 'main#submit'
176 175 #get 'main/announcements', to: 'main#announcements'
177 176
178 177
179 178 #
180 179 get 'tasks/view/:file.:ext' => 'tasks#view'
181 180 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
182 181 get 'heartbeat/:id/edit' => 'heartbeat#edit'
183 182
184 183 #grader
185 184 get 'graders/list', to: 'graders#list', as: 'grader_list'
186 185 namespace :graders do
187 186 get 'task/:id/:type', action: 'task', as: 'task'
188 187 get 'view/:id/:type', action: 'view', as: 'view'
189 188 get 'clear/:id', action: 'clear', as: 'clear'
190 189 get 'stop'
191 190 get 'stop_all'
192 191 get 'clear_all'
193 192 get 'clear_terminated'
194 193 get 'start_grading'
195 194 get 'start_exam'
196 195
197 196 end
198 197
199 198
200 199 # See how all your routes lay out with "rake routes"
201 200
202 201 # This is a legacy wild controller route that's not recommended for RESTful applications.
203 202 # Note: This route will make all actions in every controller accessible via GET requests.
204 203 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
205 204 end
@@ -1,306 +1,307
1 1 # This file is auto-generated from the current state of the database. Instead
2 2 # of editing this file, please use the migrations feature of Active Record to
3 3 # incrementally modify your database, and then regenerate this schema definition.
4 4 #
5 5 # Note that this schema.rb definition is the authoritative source for your
6 6 # database schema. If you need to create the application database on another
7 7 # system, you should be using db:schema:load, not running all the migrations
8 8 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 9 # you'll amass, the slower it'll run and the greater likelihood for issues).
10 10 #
11 11 # It's strongly recommended that you check this file into your version control system.
12 12
13 13 ActiveRecord::Schema.define(version: 2018_06_12_102327) do
14 14
15 - create_table "announcements", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
15 + create_table "announcements", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
16 16 t.string "author"
17 17 t.text "body"
18 18 t.boolean "published"
19 19 t.datetime "created_at"
20 20 t.datetime "updated_at"
21 21 t.boolean "frontpage", default: false
22 22 t.boolean "contest_only", default: false
23 23 t.string "title"
24 24 t.string "notes"
25 25 end
26 26
27 - create_table "contests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
27 + create_table "contests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
28 28 t.string "title"
29 29 t.boolean "enabled"
30 30 t.datetime "created_at"
31 31 t.datetime "updated_at"
32 32 t.string "name"
33 33 end
34 34
35 - create_table "contests_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
35 + create_table "contests_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
36 36 t.integer "contest_id"
37 37 t.integer "problem_id"
38 38 end
39 39
40 - create_table "contests_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
40 + create_table "contests_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
41 41 t.integer "contest_id"
42 42 t.integer "user_id"
43 43 end
44 44
45 - create_table "countries", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
45 + create_table "countries", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
46 46 t.string "name"
47 47 t.datetime "created_at"
48 48 t.datetime "updated_at"
49 49 end
50 50
51 - create_table "descriptions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
51 + create_table "descriptions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
52 52 t.text "body"
53 53 t.boolean "markdowned"
54 54 t.datetime "created_at"
55 55 t.datetime "updated_at"
56 56 end
57 57
58 - create_table "grader_configurations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
58 + create_table "grader_configurations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
59 59 t.string "key"
60 60 t.string "value_type"
61 61 t.string "value"
62 62 t.datetime "created_at"
63 63 t.datetime "updated_at"
64 64 t.text "description"
65 65 end
66 66
67 - create_table "grader_processes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
67 + create_table "grader_processes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
68 68 t.string "host"
69 69 t.integer "pid"
70 70 t.string "mode"
71 71 t.boolean "active"
72 72 t.datetime "created_at"
73 73 t.datetime "updated_at"
74 74 t.integer "task_id"
75 75 t.string "task_type"
76 76 t.boolean "terminated"
77 - t.index ["host", "pid"], name: "index_grader_processes_on_ip_and_pid"
77 + t.index ["host", "pid"], name: "index_grader_processes_on_host_and_pid"
78 78 end
79 79
80 - create_table "groups", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
80 + create_table "groups", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
81 81 t.string "name"
82 82 t.string "description"
83 83 end
84 84
85 - create_table "groups_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
85 + create_table "groups_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
86 86 t.integer "problem_id", null: false
87 87 t.integer "group_id", null: false
88 88 t.index ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id"
89 89 end
90 90
91 - create_table "groups_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
91 + create_table "groups_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
92 92 t.integer "group_id", null: false
93 93 t.integer "user_id", null: false
94 94 t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
95 95 end
96 96
97 97 create_table "heart_beats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
98 98 t.integer "user_id"
99 99 t.string "ip_address"
100 - t.datetime "created_at", null: false
101 - t.datetime "updated_at", null: false
100 + t.datetime "created_at"
101 + t.datetime "updated_at"
102 102 t.string "status"
103 103 t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
104 104 end
105 105
106 - create_table "languages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
106 + create_table "languages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
107 107 t.string "name", limit: 10
108 108 t.string "pretty_name"
109 109 t.string "ext", limit: 10
110 110 t.string "common_ext"
111 111 end
112 112
113 113 create_table "logins", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
114 114 t.integer "user_id"
115 115 t.string "ip_address"
116 - t.datetime "created_at", null: false
117 - t.datetime "updated_at", null: false
116 + t.datetime "created_at"
117 + t.datetime "updated_at"
118 118 end
119 119
120 - create_table "messages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
120 + create_table "messages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
121 121 t.integer "sender_id"
122 122 t.integer "receiver_id"
123 123 t.integer "replying_message_id"
124 124 t.text "body"
125 125 t.boolean "replied"
126 126 t.datetime "created_at"
127 127 t.datetime "updated_at"
128 128 end
129 129
130 - create_table "problems", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
130 + create_table "problems", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
131 131 t.string "name", limit: 30
132 132 t.string "full_name"
133 133 t.integer "full_score"
134 134 t.date "date_added"
135 135 t.boolean "available"
136 136 t.string "url"
137 137 t.integer "description_id"
138 138 t.boolean "test_allowed"
139 139 t.boolean "output_only"
140 140 t.string "description_filename"
141 141 t.boolean "view_testcase"
142 142 end
143 143
144 - create_table "problems_tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
144 + create_table "problems_tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
145 145 t.integer "problem_id"
146 146 t.integer "tag_id"
147 147 t.index ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true
148 148 t.index ["problem_id"], name: "index_problems_tags_on_problem_id"
149 149 t.index ["tag_id"], name: "index_problems_tags_on_tag_id"
150 150 end
151 151
152 - create_table "rights", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
152 + create_table "rights", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
153 153 t.string "name"
154 154 t.string "controller"
155 155 t.string "action"
156 156 end
157 157
158 - create_table "rights_roles", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
158 + create_table "rights_roles", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
159 159 t.integer "right_id"
160 160 t.integer "role_id"
161 161 t.index ["role_id"], name: "index_rights_roles_on_role_id"
162 162 end
163 163
164 - create_table "roles", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
164 + create_table "roles", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
165 165 t.string "name"
166 166 end
167 167
168 - create_table "roles_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
168 + create_table "roles_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
169 169 t.integer "role_id"
170 170 t.integer "user_id"
171 171 t.index ["user_id"], name: "index_roles_users_on_user_id"
172 172 end
173 173
174 - create_table "sessions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
174 + create_table "sessions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
175 175 t.string "session_id"
176 176 t.text "data"
177 177 t.datetime "updated_at"
178 178 t.index ["session_id"], name: "index_sessions_on_session_id"
179 179 t.index ["updated_at"], name: "index_sessions_on_updated_at"
180 180 end
181 181
182 - create_table "sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
182 + create_table "sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
183 183 t.string "name"
184 184 t.boolean "started"
185 185 t.datetime "start_time"
186 186 t.datetime "created_at"
187 187 t.datetime "updated_at"
188 188 t.integer "country_id"
189 189 t.string "password"
190 190 end
191 191
192 192 create_table "submission_view_logs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
193 193 t.integer "user_id"
194 194 t.integer "submission_id"
195 - t.datetime "created_at", null: false
196 - t.datetime "updated_at", null: false
195 + t.datetime "created_at"
196 + t.datetime "updated_at"
197 197 end
198 198
199 - create_table "submissions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
199 + create_table "submissions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
200 200 t.integer "user_id"
201 201 t.integer "problem_id"
202 202 t.integer "language_id"
203 203 t.text "source", limit: 16777215
204 204 t.binary "binary"
205 205 t.datetime "submitted_at"
206 206 t.datetime "compiled_at"
207 207 t.text "compiler_message"
208 208 t.datetime "graded_at"
209 209 t.integer "points"
210 210 t.text "grader_comment"
211 211 t.integer "number"
212 212 t.string "source_filename"
213 213 t.float "max_runtime"
214 214 t.integer "peak_memory"
215 215 t.integer "effective_code_length"
216 216 t.string "ip_address"
217 217 t.index ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true
218 218 t.index ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id"
219 219 end
220 220
221 - create_table "tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
221 + create_table "tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
222 222 t.string "name", null: false
223 223 t.text "description"
224 224 t.boolean "public"
225 225 t.datetime "created_at", null: false
226 226 t.datetime "updated_at", null: false
227 227 end
228 228
229 - create_table "tasks", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
229 + create_table "tasks", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
230 230 t.integer "submission_id"
231 231 t.datetime "created_at"
232 232 t.integer "status"
233 233 t.datetime "updated_at"
234 234 t.index ["submission_id"], name: "index_tasks_on_submission_id"
235 235 end
236 236
237 - create_table "test_pairs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
237 + create_table "test_pairs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
238 238 t.integer "problem_id"
239 239 t.text "input", limit: 16777215
240 240 t.text "solution", limit: 16777215
241 241 t.datetime "created_at"
242 242 t.datetime "updated_at"
243 243 end
244 244
245 - create_table "test_requests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
245 + create_table "test_requests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
246 246 t.integer "user_id"
247 247 t.integer "problem_id"
248 248 t.integer "submission_id"
249 249 t.string "input_file_name"
250 250 t.string "output_file_name"
251 251 t.string "running_stat"
252 252 t.integer "status"
253 253 t.datetime "updated_at"
254 254 t.datetime "submitted_at"
255 255 t.datetime "compiled_at"
256 256 t.text "compiler_message"
257 257 t.datetime "graded_at"
258 258 t.string "grader_comment"
259 259 t.datetime "created_at"
260 260 t.float "running_time"
261 261 t.string "exit_status"
262 262 t.integer "memory_usage"
263 263 t.index ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id"
264 264 end
265 265
266 - create_table "testcases", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
266 + create_table "testcases", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
267 267 t.integer "problem_id"
268 268 t.integer "num"
269 269 t.integer "group"
270 270 t.integer "score"
271 271 t.text "input", limit: 4294967295
272 272 t.text "sol", limit: 4294967295
273 273 t.datetime "created_at"
274 274 t.datetime "updated_at"
275 275 t.index ["problem_id"], name: "index_testcases_on_problem_id"
276 276 end
277 277
278 - create_table "user_contest_stats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
278 + create_table "user_contest_stats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
279 279 t.integer "user_id"
280 280 t.datetime "started_at"
281 281 t.datetime "created_at"
282 282 t.datetime "updated_at"
283 + t.boolean "forced_logout"
283 284 end
284 285
285 - create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
286 + create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
286 287 t.string "login", limit: 50
287 288 t.string "full_name"
288 289 t.string "hashed_password"
289 290 t.string "salt", limit: 5
290 291 t.string "alias"
291 292 t.string "email"
292 293 t.integer "site_id"
293 294 t.integer "country_id"
294 295 t.boolean "activated", default: false
295 296 t.datetime "created_at"
296 297 t.datetime "updated_at"
297 298 t.string "section"
298 299 t.boolean "enabled", default: true
299 300 t.string "remark"
300 301 t.string "last_ip"
301 302 t.index ["login"], name: "index_users_on_login", unique: true
302 303 end
303 304
304 305 add_foreign_key "problems_tags", "problems"
305 306 add_foreign_key "problems_tags", "tags"
306 307 end
@@ -1,288 +1,288
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 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 #---------------------------- right --------------------------------
57 57 {
58 58 :key => 'right.user_hall_of_fame',
59 59 :value_type => 'boolean',
60 60 :default_value => 'false',
61 61 :description => 'If true, any user can access hall of fame page.'
62 62 },
63 63
64 64 {
65 65 :key => 'right.multiple_ip_login',
66 66 :value_type => 'boolean',
67 67 :default_value => 'true',
68 68 :description => 'When change from true to false, a user can login from the first IP they logged into afterward.'
69 69 },
70 70
71 71 {
72 72 :key => 'right.user_view_submission',
73 73 :value_type => 'boolean',
74 74 :default_value => 'false',
75 75 :description => 'If true, any user can view submissions of every one.'
76 76 },
77 77
78 78 {
79 79 :key => 'right.bypass_agreement',
80 80 :value_type => 'boolean',
81 81 :default_value => 'true',
82 82 :description => 'When false, a user must accept usage agreement before login'
83 83 },
84 84
85 85 {
86 86 :key => 'right.heartbeat_response',
87 87 :value_type => 'string',
88 88 :default_value => 'OK',
89 89 :description => 'Heart beat response text'
90 90 },
91 91
92 92 {
93 93 :key => 'right.heartbeat_response_full',
94 94 :value_type => 'string',
95 95 :default_value => 'OK',
96 96 :description => 'Heart beat response text when user got full score (set this value to the empty string to disable this feature)'
97 97 },
98 98
99 99 {
100 100 :key => 'right.view_testcase',
101 101 :value_type => 'boolean',
102 102 :default_value => 'false',
103 - :description => 'When true, any user can view/download test data'
103 + :description => 'If true, any user can view/download test data'
104 104 },
105 105
106 106 {
107 107 :key => 'system.online_registration',
108 108 :value_type => 'boolean',
109 109 :default_value => 'false',
110 110 :description => 'This option enables online registration.'
111 111 },
112 112
113 113 # If Configuration['system.online_registration'] is true, the
114 114 # system allows online registration, and will use these
115 115 # information for sending confirmation emails.
116 116 {
117 117 :key => 'system.online_registration.smtp',
118 118 :value_type => 'string',
119 119 :default_value => 'smtp.somehost.com'
120 120 },
121 121
122 122 {
123 123 :key => 'system.online_registration.from',
124 124 :value_type => 'string',
125 125 :default_value => 'your.email@address'
126 126 },
127 127
128 128 {
129 129 :key => 'system.admin_email',
130 130 :value_type => 'string',
131 131 :default_value => 'admin@admin.email'
132 132 },
133 133
134 134 {
135 135 :key => 'system.user_setting_enabled',
136 136 :value_type => 'boolean',
137 137 :default_value => 'true',
138 138 :description => 'If this option is true, users can change their settings'
139 139 },
140 140
141 141 {
142 142 :key => 'system.user_setting_enabled',
143 143 :value_type => 'boolean',
144 144 :default_value => 'true',
145 145 :description => 'If this option is true, users can change their settings'
146 146 },
147 147
148 148 # If Configuration['contest.test_request.early_timeout'] is true
149 149 # the user will not be able to use test request at 30 minutes
150 150 # before the contest ends.
151 151 {
152 152 :key => 'contest.test_request.early_timeout',
153 153 :value_type => 'boolean',
154 154 :default_value => 'false'
155 155 },
156 156
157 157 {
158 158 :key => 'system.multicontests',
159 159 :value_type => 'boolean',
160 160 :default_value => 'false'
161 161 },
162 162
163 163 {
164 164 :key => 'contest.confirm_indv_contest_start',
165 165 :value_type => 'boolean',
166 166 :default_value => 'false'
167 167 },
168 168
169 169 {
170 170 :key => 'contest.default_contest_name',
171 171 :value_type => 'string',
172 172 :default_value => 'none',
173 173 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
174 174 },
175 175
176 176 {
177 177 :key => 'system.use_problem_group',
178 178 :value_type => 'boolean',
179 179 :default_value => 'false',
180 180 :description => "If true, available problem to the user will be only ones associated with the group of the user."
181 181 },
182 182
183 183
184 184 {
185 - :key => 'right.whitelist_ip_only',
185 + :key => 'right.whitelist_ignore',
186 186 :value_type => 'boolean',
187 - :default_value => 'false',
188 - :description => "If true, non-admin user will be able to use the system only when their ip is in the 'whitelist_ip'."
187 + :default_value => 'true',
188 + :description => "If true, no IP check against whitelist_ip is perform. However, when false, non-admin user must have their ip in 'whitelist_ip' to be able to login."
189 189 },
190 190
191 191 {
192 192 :key => 'right.whitelist_ip',
193 193 :value_type => 'string',
194 194 :default_value => '0.0.0.0/0',
195 - :description => "list of whitelist ip, given in comma separated CIDR notation. For example '161.200.92.0/23, 161.200.80.1/32'"
195 + :description => "list of whitelist ip, given in comma separated CIDR notation. For example '192.168.90.0/23, 192.168.1.23/32'"
196 196 },
197 197
198 198 ]
199 199
200 200
201 201 def create_configuration_key(key,
202 202 value_type,
203 203 default_value,
204 204 description='')
205 205 conf = (GraderConfiguration.find_by_key(key) ||
206 206 GraderConfiguration.new(:key => key,
207 207 :value_type => value_type,
208 208 :value => default_value))
209 209 conf.description = description
210 210 conf.save
211 211 end
212 212
213 213 def seed_config
214 214 CONFIGURATIONS.each do |conf|
215 215 if conf.has_key? :description
216 216 desc = conf[:description]
217 217 else
218 218 desc = ''
219 219 end
220 220 create_configuration_key(conf[:key],
221 221 conf[:value_type],
222 222 conf[:default_value],
223 223 desc)
224 224 end
225 225 end
226 226
227 227 def seed_roles
228 228 return if Role.find_by_name('admin')
229 229
230 230 role = Role.create(:name => 'admin')
231 231 user_admin_right = Right.create(:name => 'user_admin',
232 232 :controller => 'user_admin',
233 233 :action => 'all')
234 234 problem_admin_right = Right.create(:name=> 'problem_admin',
235 235 :controller => 'problems',
236 236 :action => 'all')
237 237
238 238 graders_right = Right.create(:name => 'graders_admin',
239 239 :controller => 'graders',
240 240 :action => 'all')
241 241
242 242 role.rights << user_admin_right;
243 243 role.rights << problem_admin_right;
244 244 role.rights << graders_right;
245 245 role.save
246 246 end
247 247
248 248 def seed_root
249 249 return if User.find_by_login('root')
250 250
251 251 root = User.new(:login => 'root',
252 252 :full_name => 'Administrator',
253 253 :alias => 'root')
254 254 root.password = 'ioionrails';
255 255
256 256 class << root
257 257 public :encrypt_new_password
258 258 def valid?(context=nil)
259 259 true
260 260 end
261 261 end
262 262
263 263 root.encrypt_new_password
264 264
265 265 root.roles << Role.find_by_name('admin')
266 266
267 267 root.activated = true
268 268 root.save
269 269 end
270 270
271 271 def seed_users_and_roles
272 272 seed_roles
273 273 seed_root
274 274 end
275 275
276 276 def seed_more_languages
277 - Language.delete_all
277 + #Language.delete_all
278 278 Language.find_or_create_by( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
279 279 Language.find_or_create_by( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
280 280 Language.find_or_create_by( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
281 281 Language.find_or_create_by( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
282 282 Language.find_or_create_by( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
283 283 Language.find_or_create_by( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
284 284 end
285 285
286 286 seed_config
287 287 seed_users_and_roles
288 288 seed_more_languages
@@ -1,121 +1,121
1 1 require "application_system_test_case"
2 2
3 3 class UsersTest < ApplicationSystemTestCase
4 4 # test "visiting the index" do
5 5 # visit users_url
6 6 #
7 7 # assert_selector "h1", text: "User"
8 8 # end
9 9
10 10 test "add new user and edit" do
11 11 login('admin','admin')
12 12 within 'header' do
13 13 click_on 'Manage'
14 14 click_on 'Users', match: :first
15 15 end
16 16
17 17 assert_text "Users"
18 18 assert_text "New user"
19 19
20 20 click_on "New user", match: :first
21 21 fill_in 'Login', with: 'test1'
22 22 fill_in 'Full name', with: 'test1 McTestface'
23 23 fill_in 'e-mail', with: 'a@a.com'
24 24 fill_in 'Password', with: 'abcdef'
25 25 fill_in 'Password confirmation', with: 'abcdef'
26 26
27 27 click_on 'Create'
28 28
29 29 assert_text 'User was successfully created'
30 30 assert_text 'a@a.com'
31 31 assert_text 'test1 McTestface'
32 32
33 33 within('tr', text: 'McTestface') do
34 34 click_on 'Edit'
35 35 end
36 36
37 37 fill_in 'Alias', with: 'hahaha'
38 38 fill_in 'Remark', with: 'section 2'
39 39 click_on 'Update User'
40 40
41 41 assert_text 'section 2'
42 42 end
43 43
44 44 test "add multiple users" do
45 45 login 'admin', 'admin'
46 46 within 'header' do
47 47 click_on 'Manage'
48 48 click_on 'Users', match: :first
49 49 end
50 50
51 51 click_on 'New list of users', match: :first
52 52 find(:css, 'textarea').fill_in with:"abc1,Boaty McBoatface,abcdef,alias1,remark1,\nabc2,Boaty2 McSecond,acbdef123,aias2,remark2"
53 - click_on 'create users'
53 + click_on 'Create following users'
54 54
55 55 assert_text('remark1')
56 56 assert_text('remark2')
57 57 end
58 58
59 59 test "grant admin right" do
60 60 login 'admin', 'admin'
61 61 within 'header' do
62 62 click_on 'Manage'
63 63 click_on 'Users', match: :first
64 64 end
65 65
66 66 click_on "View administrator"
67 67 fill_in 'login', with: 'john'
68 68 click_on "Grant"
69 69
70 70 visit logout_main_path
71 71 login 'john','hello'
72 72 within 'header' do
73 73 click_on 'Manage'
74 74 click_on 'Problem', match: :first
75 75 end
76 76 assert_text "Turn off all problems"
77 77 end
78 78
79 79 test "try using admin from normal user" do
80 80 login 'admin','admin'
81 81 visit bulk_manage_user_admin_index_path
82 82 assert_current_path bulk_manage_user_admin_index_path
83 83 visit logout_main_path
84 84
85 85 login 'jack','morning'
86 86 visit bulk_manage_user_admin_index_path
87 87 assert_text 'You are not authorized'
88 88 assert_current_path login_main_path
89 89
90 90 login 'james','morning'
91 91 visit new_list_user_admin_index_path
92 92 assert_text 'You are not authorized'
93 93 assert_current_path login_main_path
94 94 end
95 95
96 96 test "login then change password" do
97 97 newpassword = '1234asdf'
98 98 login 'john', 'hello'
99 99 visit profile_users_path
100 100
101 101 fill_in 'password', with: newpassword
102 102 fill_in 'password_confirmation', with: newpassword
103 103
104 104 click_on 'Edit'
105 105
106 106 visit logout_main_path
107 107 login 'john', 'hello'
108 108 assert_text 'Wrong password'
109 109
110 110 login 'john', newpassword
111 111 assert_text "MAIN"
112 112 assert_text "Submission"
113 113 end
114 114
115 115 def login(username,password)
116 116 visit root_path
117 117 fill_in "Login", with: username
118 118 fill_in "Password", with: password
119 119 click_on "Login"
120 120 end
121 121 end
You need to be logged in to leave comments. Login now