Description:
add current score by group
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r762:c6677423b846 - - 10 files changed: 105 inserted, 24 deleted

@@ -0,0 +1,45
1 + .container-fluid
2 + .row
3 + .col-md-6
4 + %h1 Adding list of users
5 + .row
6 + .col-md-6
7 + .panel.panel-default
8 + .panel-heading
9 + .panel-title Info
10 + .panel-body
11 + %ul
12 + %li
13 + List of user information in this format:
14 + %tt user_id,name(,passwd(,alias(,remark)))
15 + %li
16 + Note that
17 + %tt passwd, alias
18 + and
19 + %tt remark
20 + is optional.
21 + %li
22 + When
23 + %tt passwd
24 + or
25 + %tt alias
26 + is empty, the original value will be used instead.
27 + %li
28 + If the users with the same user_id already exists, existing information will be overwritten.
29 +
30 + .row
31 + .col-md-6
32 + = form_tag :action => 'create_from_list' do
33 + .form-group
34 + = submit_tag 'Create following users',class: 'btn btn-success'
35 + .form-group
36 + .div.checkbox
37 + %label
38 + = check_box_tag :add_to_group
39 + Also add these users to the following group
40 + = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
41 + .form-group
42 + = text_area_tag 'user_list', nil, :rows => 50, :cols => 80
43 + .col-md-6
44 +
45 +
@@ -1,404 +1,409
1 1 require 'csv'
2 2
3 3 class ReportController < ApplicationController
4 4
5 5 before_action :check_valid_login
6 6
7 7 before_action :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score, :current_score]
8 8
9 9 before_action(only: [:problem_hof]) { |c|
10 10 return false unless check_valid_login
11 11
12 12 admin_authorization unless GraderConfiguration["right.user_view_submission"]
13 13 }
14 14
15 15 def max_score
16 16 end
17 17
18 18 def current_score
19 19 @problems = Problem.available_problems
20 + if params[:group_id]
21 + @group = Group.find(params[:group_id])
22 + @users = @group.users.where(enabled: true)
23 + else
20 24 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
25 + end
21 26 @scorearray = calculate_max_score(@problems, @users,0,0,true)
22 27
23 28 #rencer accordingly
24 29 if params[:button] == 'download' then
25 30 csv = gen_csv_from_scorearray(@scorearray,@problems)
26 31 send_data csv, filename: 'max_score.csv'
27 32 else
28 33 #render template: 'user_admin/user_stat'
29 34 render 'current_score'
30 35 end
31 36 end
32 37
33 38 def show_max_score
34 39 #process parameters
35 40 #problems
36 41 @problems = []
37 42 if params[:problem_id]
38 43 params[:problem_id].each do |id|
39 44 next unless id.strip != ""
40 45 pid = Problem.find_by_id(id.to_i)
41 46 @problems << pid if pid
42 47 end
43 48 end
44 49
45 50 #users
46 51 @users = if params[:users] == "all" then
47 52 User.includes(:contests).includes(:contest_stat)
48 53 else
49 54 User.includes(:contests).includes(:contest_stat).where(enabled: true)
50 55 end
51 56
52 57 #set up range from param
53 58 @since_id = params.fetch(:from_id, 0).to_i
54 59 @until_id = params.fetch(:to_id, 0).to_i
55 60 @since_id = nil if @since_id == 0
56 61 @until_id = nil if @until_id == 0
57 62
58 63 #calculate the routine
59 64 @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
60 65
61 66 #rencer accordingly
62 67 if params[:button] == 'download' then
63 68 csv = gen_csv_from_scorearray(@scorearray,@problems)
64 69 send_data csv, filename: 'max_score.csv'
65 70 else
66 71 #render template: 'user_admin/user_stat'
67 72 render 'max_score'
68 73 end
69 74
70 75 end
71 76
72 77 def score
73 78 if params[:commit] == 'download csv'
74 79 @problems = Problem.all
75 80 else
76 81 @problems = Problem.available_problems
77 82 end
78 83 @users = User.includes(:contests, :contest_stat).where(enabled: true)
79 84 @scorearray = Array.new
80 85 @users.each do |u|
81 86 ustat = Array.new
82 87 ustat[0] = u
83 88 @problems.each do |p|
84 89 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
85 90 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
86 91 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
87 92 else
88 93 ustat << [0,false]
89 94 end
90 95 end
91 96 @scorearray << ustat
92 97 end
93 98 if params[:commit] == 'download csv' then
94 99 csv = gen_csv_from_scorearray(@scorearray,@problems)
95 100 send_data csv, filename: 'last_score.csv'
96 101 else
97 102 render template: 'user_admin/user_stat'
98 103 end
99 104
100 105 end
101 106
102 107 def login_stat
103 108 @logins = Array.new
104 109
105 110 date_and_time = '%Y-%m-%d %H:%M'
106 111 begin
107 112 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
108 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)
109 114 rescue
110 115 @since_time = DateTime.new(1000,1,1)
111 116 end
112 117 begin
113 118 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
114 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)
115 120 rescue
116 121 @until_time = DateTime.new(3000,1,1)
117 122 end
118 123
119 124 User.all.each do |user|
120 125 @logins << { id: user.id,
121 126 login: user.login,
122 127 full_name: user.full_name,
123 128 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
124 129 user.id,@since_time,@until_time)
125 130 .count(:id),
126 131 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
127 132 user.id,@since_time,@until_time)
128 133 .minimum(:created_at),
129 134 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
130 135 user.id,@since_time,@until_time)
131 136 .maximum(:created_at),
132 137 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
133 138 user.id,@since_time,@until_time)
134 139 .select(:ip_address).uniq
135 140
136 141 }
137 142 end
138 143 end
139 144
140 145 def submission_stat
141 146
142 147 date_and_time = '%Y-%m-%d %H:%M'
143 148 begin
144 149 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
145 150 rescue
146 151 @since_time = DateTime.new(1000,1,1)
147 152 end
148 153 begin
149 154 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
150 155 rescue
151 156 @until_time = DateTime.new(3000,1,1)
152 157 end
153 158
154 159 @submissions = {}
155 160
156 161 User.find_each do |user|
157 162 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
158 163 end
159 164
160 165 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
161 166 if @submissions[s.user_id]
162 167 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
163 168 a = Problem.find_by_id(s.problem_id)
164 169 @submissions[s.user_id][:sub][s.problem_id] =
165 170 { prob_name: (a ? a.full_name : '(NULL)'),
166 171 sub_ids: [s.id] }
167 172 else
168 173 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
169 174 end
170 175 @submissions[s.user_id][:count] += 1
171 176 end
172 177 end
173 178 end
174 179
175 180 def problem_hof
176 181 # gen problem list
177 182 @user = User.find(session[:user_id])
178 183 @problems = @user.available_problems
179 184
180 185 # get selected problems or the default
181 186 if params[:id]
182 187 begin
183 188 @problem = Problem.available.find(params[:id])
184 189 rescue
185 190 redirect_to action: :problem_hof
186 191 flash[:notice] = 'Error: submissions for that problem are not viewable.'
187 192 return
188 193 end
189 194 end
190 195
191 196 return unless @problem
192 197
193 198 @by_lang = {} #aggregrate by language
194 199
195 200 range =65
196 201 @histogram = { data: Array.new(range,0), summary: {} }
197 202 @summary = {count: 0, solve: 0, attempt: 0}
198 203 user = Hash.new(0)
199 204 Submission.where(problem_id: @problem.id).find_each do |sub|
200 205 #histogram
201 206 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
202 207 @histogram[:data][d.to_i] += 1 if d < range
203 208
204 209 next unless sub.points
205 210 @summary[:count] += 1
206 211 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
207 212
208 213 lang = Language.find_by_id(sub.language_id)
209 214 next unless lang
210 215 next unless sub.points >= @problem.full_score
211 216
212 217 #initialize
213 218 unless @by_lang.has_key?(lang.pretty_name)
214 219 @by_lang[lang.pretty_name] = {
215 220 runtime: { avail: false, value: 2**30-1 },
216 221 memory: { avail: false, value: 2**30-1 },
217 222 length: { avail: false, value: 2**30-1 },
218 223 first: { avail: false, value: DateTime.new(3000,1,1) }
219 224 }
220 225 end
221 226
222 227 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
223 228 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
224 229 end
225 230
226 231 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
227 232 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
228 233 end
229 234
230 235 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and sub.user and
231 236 !sub.user.admin?
232 237 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
233 238 end
234 239
235 240 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
236 241 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
237 242 end
238 243 end
239 244
240 245 #process user_id
241 246 @by_lang.each do |lang,prop|
242 247 prop.each do |k,v|
243 248 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
244 249 end
245 250 end
246 251
247 252 #sum into best
248 253 if @by_lang and @by_lang.first
249 254 @best = @by_lang.first[1].clone
250 255 @by_lang.each do |lang,prop|
251 256 if @best[:runtime][:value] >= prop[:runtime][:value]
252 257 @best[:runtime] = prop[:runtime]
253 258 @best[:runtime][:lang] = lang
254 259 end
255 260 if @best[:memory][:value] >= prop[:memory][:value]
256 261 @best[:memory] = prop[:memory]
257 262 @best[:memory][:lang] = lang
258 263 end
259 264 if @best[:length][:value] >= prop[:length][:value]
260 265 @best[:length] = prop[:length]
261 266 @best[:length][:lang] = lang
262 267 end
263 268 if @best[:first][:value] >= prop[:first][:value]
264 269 @best[:first] = prop[:first]
265 270 @best[:first][:lang] = lang
266 271 end
267 272 end
268 273 end
269 274
270 275 @histogram[:summary][:max] = [@histogram[:data].max,1].max
271 276 @summary[:attempt] = user.count
272 277 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
273 278 end
274 279
275 280 def stuck #report struggling user,problem
276 281 # init
277 282 user,problem = nil
278 283 solve = true
279 284 tries = 0
280 285 @struggle = Array.new
281 286 record = {}
282 287 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
283 288 next unless sub.problem and sub.user
284 289 if user != sub.user_id or problem != sub.problem_id
285 290 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
286 291 record = {user: sub.user, problem: sub.problem}
287 292 user,problem = sub.user_id, sub.problem_id
288 293 solve = false
289 294 tries = 0
290 295 end
291 296 if sub.points >= sub.problem.full_score
292 297 solve = true
293 298 else
294 299 tries += 1
295 300 end
296 301 end
297 302 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
298 303 @struggle = @struggle[0..50]
299 304 end
300 305
301 306
302 307 def multiple_login
303 308 #user with multiple IP
304 309 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
305 310 last,count = 0,0
306 311 first = 0
307 312 @users = []
308 313 raw.each do |r|
309 314 if last != r.user.login
310 315 count = 1
311 316 last = r.user.login
312 317 first = r
313 318 else
314 319 @users << first if count == 1
315 320 @users << r
316 321 count += 1
317 322 end
318 323 end
319 324
320 325 #IP with multiple user
321 326 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
322 327 last,count = 0,0
323 328 first = 0
324 329 @ip = []
325 330 raw.each do |r|
326 331 if last != r.ip_address
327 332 count = 1
328 333 last = r.ip_address
329 334 first = r
330 335 else
331 336 @ip << first if count == 1
332 337 @ip << r
333 338 count += 1
334 339 end
335 340 end
336 341 end
337 342
338 343 def cheat_report
339 344 date_and_time = '%Y-%m-%d %H:%M'
340 345 begin
341 346 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
342 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)
343 348 rescue
344 349 @since_time = Time.zone.now.ago( 90.minutes)
345 350 end
346 351 begin
347 352 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
348 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)
349 354 rescue
350 355 @until_time = Time.zone.now
351 356 end
352 357
353 358 #multi login
354 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")
355 360
356 361 st = <<-SQL
357 362 SELECT l2.*
358 363 FROM logins l2 INNER JOIN
359 364 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
360 365 FROM logins l
361 366 INNER JOIN users u ON l.user_id = u.id
362 367 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
363 368 GROUP BY u.id
364 369 HAVING count > 1
365 370 ) ml ON l2.user_id = ml.id
366 371 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
367 372 UNION
368 373 SELECT l2.*
369 374 FROM logins l2 INNER JOIN
370 375 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
371 376 FROM logins l
372 377 INNER JOIN users u ON l.user_id = u.id
373 378 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
374 379 GROUP BY l.ip_address
375 380 HAVING count > 1
376 381 ) ml on ml.ip_address = l2.ip_address
377 382 INNER JOIN users u ON l2.user_id = u.id
378 383 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
379 384 ORDER BY ip_address,created_at
380 385 SQL
381 386 @mld = Login.find_by_sql(st)
382 387
383 388 st = <<-SQL
384 389 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
385 390 FROM submissions s INNER JOIN
386 391 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
387 392 FROM logins l
388 393 INNER JOIN users u ON l.user_id = u.id
389 394 WHERE l.created_at >= ? and l.created_at <= ?
390 395 GROUP BY u.id
391 396 HAVING count > 1
392 397 ) ml ON s.user_id = ml.id
393 398 WHERE s.submitted_at >= ? and s.submitted_at <= ?
394 399 UNION
395 400 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
396 401 FROM submissions s INNER JOIN
397 402 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
398 403 FROM logins l
399 404 INNER JOIN users u ON l.user_id = u.id
400 405 WHERE l.created_at >= ? and l.created_at <= ?
401 406 GROUP BY l.ip_address
402 407 HAVING count > 1
403 408 ) ml on ml.ip_address = s.ip_address
404 409 WHERE s.submitted_at >= ? and s.submitted_at <= ?
@@ -1,75 +1,74
1 1 class TasksController < ApplicationController
2 2
3 3 before_action :check_valid_login, :check_viewability
4 4
5 5 def index
6 6 redirect_to :action => 'list'
7 7 end
8 8
9 9 def list
10 10 @problems = @user.available_problems
11 11 end
12 12
13 13 # this has contest-wide access control
14 14 def view
15 15 base_name = params[:file]
16 16 base_filename = File.basename("#{base_name}.#{params[:ext]}")
17 17 filename = "#{Problem.download_file_basedir}/#{base_filename}"
18 18
19 19 if !FileTest.exists?(filename)
20 20 redirect_to :action => 'index' and return
21 21 end
22 22
23 23 send_file_to_user(filename, base_filename)
24 24 end
25 25
26 26 # this has problem-level access control
27 27 def download
28 28 problem = Problem.find(params[:id])
29 29 unless @current_user.can_view_problem? problem
30 + flash[:notice] = 'You are not authorized to access this file'
30 31 redirect_to :action => 'index' and return
31 32 end
32 33
33 34 base_name = params[:file]
34 35 base_filename = File.basename("#{base_name}.#{params[:ext]}")
35 36 filename = "#{Problem.download_file_basedir}/#{params[:id]}/#{base_filename}"
36 - puts "SENDING: #{filename}"
37 37
38 38 if !FileTest.exists?(filename)
39 + flash[:notice] = 'File does not exists'
39 40 redirect_to :action => 'index' and return
40 41 end
41 42
42 - puts "SENDING: #{filename}"
43 -
44 43 send_file_to_user(filename, base_filename)
45 44 end
46 45
47 46 protected
48 47
49 48 def send_file_to_user(filename, base_filename)
50 49 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
51 50 response.headers['Content-Type'] = "application/force-download"
52 51 response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filename)}\""
53 52 response.headers["X-Sendfile"] = filename
54 53 response.headers['Content-length'] = File.size(filename)
55 54 render :nothing => true
56 55 else
57 56 if params[:ext]=='pdf'
58 57 content_type = 'application/pdf'
59 58 else
60 59 content_type = 'application/octet-stream'
61 60 end
62 61
63 62 send_file filename, :stream => false, :disposition => 'inline', :filename => base_filename, :type => content_type
64 63 end
65 64 end
66 65
67 66 def check_viewability
68 67 @user = User.find(session[:user_id])
69 68 if @user==nil or !GraderConfiguration.show_tasks_to?(@user)
70 69 redirect_to :controller => 'main', :action => 'list'
71 70 return false
72 71 end
73 72 end
74 73
75 74 end
@@ -1,499 +1,519
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 + error_note = []
64 + ok_user = []
63 65
64 66 lines.split("\n").each do |line|
65 67 items = line.chomp.split(',')
66 68 if items.length>=2
67 69 login = items[0]
68 70 full_name = items[1]
69 71 remark =''
70 72 user_alias = ''
71 73
72 74 added_random_password = false
73 75 if items.length >= 3 and items[2].chomp(" ").length > 0;
74 76 password = items[2].chomp(" ")
75 77 else
76 78 password = random_password
77 79 add_random_password=true;
78 80 end
79 81
80 82 if items.length>= 4 and items[3].chomp(" ").length > 0;
81 83 user_alias = items[3].chomp(" ")
82 84 else
83 85 user_alias = login
84 86 end
85 87
86 88 if items.length>=5
87 89 remark = items[4].strip;
88 90 end
89 91
90 92 user = User.find_by_login(login)
91 93 if (user)
92 94 user.full_name = full_name
93 95 user.password = password
94 96 user.remark = remark
95 97 else
96 98 user = User.new({:login => login,
97 99 :full_name => full_name,
98 100 :password => password,
99 101 :password_confirmation => password,
100 102 :alias => user_alias,
101 103 :remark => remark})
102 104 end
103 105 user.activated = true
104 - user.save
105 106
107 + if user.save
106 108 if added_random_password
107 109 note << "'#{login}' (+)"
108 110 else
109 111 note << login
110 112 end
113 + ok_user << user
114 + else
115 + error_note << "#{login}"
116 + end
117 +
111 118 end
112 119 end
120 +
121 + #add to group
122 + if params[:add_to_group]
123 + group = Group.where(id: params[:group_id]).first
124 + if group
125 + group.users << ok_user
126 + end
127 + end
128 +
129 + # show flash
113 130 flash[:success] = 'User(s) ' + note.join(', ') +
114 131 ' were successfully created. ' +
115 132 '( (+) - created with random passwords.)'
133 + if error_note.size > 0
134 + flash[:error] = "Following user(s) failed to be created: " + error_note.join(', ')
135 + end
116 136 redirect_to :action => 'index'
117 137 end
118 138
119 139 def edit
120 140 @user = User.find(params[:id])
121 141 end
122 142
123 143 def update
124 144 @user = User.find(params[:id])
125 145 if @user.update_attributes(user_params)
126 146 flash[:notice] = 'User was successfully updated.'
127 147 redirect_to :action => 'show', :id => @user
128 148 else
129 149 render :action => 'edit'
130 150 end
131 151 end
132 152
133 153 def destroy
134 154 User.find(params[:id]).destroy
135 155 redirect_to :action => 'index'
136 156 end
137 157
138 158 def user_stat
139 159 if params[:commit] == 'download csv'
140 160 @problems = Problem.all
141 161 else
142 162 @problems = Problem.available_problems
143 163 end
144 164 @users = User.includes(:contests, :contest_stat).where(enabled: true)
145 165 @scorearray = Array.new
146 166 @users.each do |u|
147 167 ustat = Array.new
148 168 ustat[0] = u
149 169 @problems.each do |p|
150 170 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
151 171 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
152 172 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
153 173 else
154 174 ustat << [0,false]
155 175 end
156 176 end
157 177 @scorearray << ustat
158 178 end
159 179 if params[:commit] == 'download csv' then
160 180 csv = gen_csv_from_scorearray(@scorearray,@problems)
161 181 send_data csv, filename: 'last_score.csv'
162 182 else
163 183 render template: 'user_admin/user_stat'
164 184 end
165 185 end
166 186
167 187 def user_stat_max
168 188 if params[:commit] == 'download csv'
169 189 @problems = Problem.all
170 190 else
171 191 @problems = Problem.available_problems
172 192 end
173 193 @users = User.includes(:contests).includes(:contest_stat).all
174 194 @scorearray = Array.new
175 195 #set up range from param
176 196 since_id = params.fetch(:since_id, 0).to_i
177 197 until_id = params.fetch(:until_id, 0).to_i
178 198 @users.each do |u|
179 199 ustat = Array.new
180 200 ustat[0] = u
181 201 @problems.each do |p|
182 202 max_points = 0
183 203 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
184 204 max_points = sub.points if sub and sub.points and (sub.points > max_points)
185 205 end
186 206 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
187 207 end
188 208 @scorearray << ustat
189 209 end
190 210
191 211 if params[:commit] == 'download csv' then
192 212 csv = gen_csv_from_scorearray(@scorearray,@problems)
193 213 send_data csv, filename: 'max_score.csv'
194 214 else
195 215 render template: 'user_admin/user_stat'
196 216 end
197 217 end
198 218
199 219 def import
200 220 if params[:file]==''
201 221 flash[:notice] = 'Error importing no file'
202 222 redirect_to :action => 'index' and return
203 223 end
204 224 import_from_file(params[:file])
205 225 end
206 226
207 227 def random_all_passwords
208 228 users = User.all
209 229 @prefix = params[:prefix] || ''
210 230 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
211 231 @changed = false
212 232 if params[:commit] == 'Go ahead'
213 233 @non_admin_users.each do |user|
214 234 password = random_password
215 235 user.password = password
216 236 user.password_confirmation = password
217 237 user.save
218 238 end
219 239 @changed = true
220 240 end
221 241 end
222 242
223 243 # contest management
224 244
225 245 def contests
226 246 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
227 247 @contests = Contest.enabled
228 248 end
229 249
230 250 def assign_from_list
231 251 contest_id = params[:users_contest_id]
232 252 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
233 253 contest = Contest.find(params[:new_contest][:id])
234 254 if !contest
235 255 flash[:notice] = 'Error: no contest'
236 256 redirect_to :action => 'contests', :id =>contest_id
237 257 end
238 258
239 259 note = []
240 260 users.each do |u|
241 261 u.contests = [contest]
242 262 note << u.login
243 263 end
244 264 flash[:notice] = 'User(s) ' + note.join(', ') +
245 265 " were successfully reassigned to #{contest.title}."
246 266 redirect_to :action => 'contests', :id =>contest.id
247 267 end
248 268
249 269 def add_to_contest
250 270 user = User.find(params[:id])
251 271 contest = Contest.find(params[:contest_id])
252 272 if user and contest
253 273 user.contests << contest
254 274 end
255 275 redirect_to :action => 'index'
256 276 end
257 277
258 278 def remove_from_contest
259 279 user = User.find(params[:id])
260 280 contest = Contest.find(params[:contest_id])
261 281 if user and contest
262 282 user.contests.delete(contest)
263 283 end
264 284 redirect_to :action => 'index'
265 285 end
266 286
267 287 def contest_management
268 288 end
269 289
270 290 def manage_contest
271 291 contest = Contest.find(params[:contest][:id])
272 292 if !contest
273 293 flash[:notice] = 'You did not choose the contest.'
274 294 redirect_to :action => 'contest_management' and return
275 295 end
276 296
277 297 operation = params[:operation]
278 298
279 299 if not ['add','remove','assign'].include? operation
280 300 flash[:notice] = 'You did not choose the operation to perform.'
281 301 redirect_to :action => 'contest_management' and return
282 302 end
283 303
284 304 lines = params[:login_list]
285 305 if !lines or lines.blank?
286 306 flash[:notice] = 'You entered an empty list.'
287 307 redirect_to :action => 'contest_management' and return
288 308 end
289 309
290 310 note = []
291 311 users = []
292 312 lines.split("\n").each do |line|
293 313 user = User.find_by_login(line.chomp)
294 314 if user
295 315 if operation=='add'
296 316 if ! user.contests.include? contest
297 317 user.contests << contest
298 318 end
299 319 elsif operation=='remove'
300 320 user.contests.delete(contest)
301 321 else
302 322 user.contests = [contest]
303 323 end
304 324
305 325 if params[:reset_timer]
306 326 user.contest_stat.forced_logout = true
307 327 user.contest_stat.reset_timer_and_save
308 328 end
309 329
310 330 if params[:notification_emails]
311 331 send_contest_update_notification_email(user, contest)
312 332 end
313 333
314 334 note << user.login
315 335 users << user
316 336 end
317 337 end
318 338
319 339 if params[:reset_timer]
320 340 logout_users(users)
321 341 end
322 342
323 343 flash[:notice] = 'User(s) ' + note.join(', ') +
324 344 ' were successfully modified. '
325 345 redirect_to :action => 'contest_management'
326 346 end
327 347
328 348 # admin management
329 349
330 350 def admin
331 351 @admins = User.all.find_all {|user| user.admin? }
332 352 end
333 353
334 354 def grant_admin
335 355 login = params[:login]
336 356 user = User.find_by_login(login)
337 357 if user!=nil
338 358 admin_role = Role.find_by_name('admin')
339 359 user.roles << admin_role
340 360 else
341 361 flash[:notice] = 'Unknown user'
342 362 end
343 363 flash[:notice] = 'User added as admins'
344 364 redirect_to :action => 'admin'
345 365 end
346 366
347 367 def revoke_admin
348 368 user = User.find(params[:id])
349 369 if user==nil
350 370 flash[:notice] = 'Unknown user'
351 371 redirect_to :action => 'admin' and return
352 372 elsif user.login == 'root'
353 373 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
354 374 redirect_to :action => 'admin' and return
355 375 end
356 376
357 377 admin_role = Role.find_by_name('admin')
358 378 user.roles.delete(admin_role)
359 379 flash[:notice] = 'User permission revoked'
360 380 redirect_to :action => 'admin'
361 381 end
362 382
363 383 # mass mailing
364 384
365 385 def mass_mailing
366 386 end
367 387
368 388 def bulk_mail
369 389 lines = params[:login_list]
370 390 if !lines or lines.blank?
371 391 flash[:notice] = 'You entered an empty list.'
372 392 redirect_to :action => 'mass_mailing' and return
373 393 end
374 394
375 395 mail_subject = params[:subject]
376 396 if !mail_subject or mail_subject.blank?
377 397 flash[:notice] = 'You entered an empty mail subject.'
378 398 redirect_to :action => 'mass_mailing' and return
379 399 end
380 400
381 401 mail_body = params[:email_body]
382 402 if !mail_body or mail_body.blank?
383 403 flash[:notice] = 'You entered an empty mail body.'
384 404 redirect_to :action => 'mass_mailing' and return
385 405 end
386 406
387 407 note = []
388 408 users = []
389 409 lines.split("\n").each do |line|
390 410 user = User.find_by_login(line.chomp)
391 411 if user
392 412 send_mail(user.email, mail_subject, mail_body)
393 413 note << user.login
394 414 end
395 415 end
396 416
397 417 flash[:notice] = 'User(s) ' + note.join(', ') +
398 418 ' were successfully modified. '
399 419 redirect_to :action => 'mass_mailing'
400 420 end
401 421
402 422 #bulk manage
403 423 def bulk_manage
404 424
405 425 begin
406 426 @users = User.where('(login REGEXP ?) OR (remark REGEXP ?)',params[:regex],params[:regex]) if params[:regex]
407 427 @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised
408 428 rescue Exception
409 429 flash[:error] = 'Regular Expression is malformed'
410 430 @users = nil
411 431 end
412 432
413 433 if params[:commit]
414 434 @action = {}
415 435 @action[:set_enable] = params[:enabled]
416 436 @action[:enabled] = params[:enable] == "1"
417 437 @action[:gen_password] = params[:gen_password]
418 438 @action[:add_group] = params[:add_group]
419 439 @action[:group_name] = params[:group_name]
420 440 end
421 441
422 442 if params[:commit] == "Perform"
423 443 if @action[:set_enable]
424 444 @users.update_all(enabled: @action[:enabled])
425 445 end
426 446 if @action[:gen_password]
427 447 @users.each do |u|
428 448 password = random_password
429 449 u.password = password
430 450 u.password_confirmation = password
431 451 u.save
432 452 end
433 453 end
434 454 if @action[:add_group] and @action[:group_name]
435 455 @group = Group.find(@action[:group_name])
436 456 ok = []
437 457 failed = []
438 458 @users.each do |user|
439 459 begin
440 460 @group.users << user
441 461 ok << user.login
442 462 rescue => e
443 463 failed << user.login
444 464 end
445 465 end
446 466 flash[:success] = "The following users are added to the 'group #{@group.name}': " + ok.join(', ') if ok.count > 0
447 467 flash[:alert] = "The following users are already in the 'group #{@group.name}': " + failed.join(', ') if failed.count > 0
448 468 end
449 469 end
450 470 end
451 471
452 472 protected
453 473
454 474 def random_password(length=5)
455 475 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
456 476 newpass = ""
457 477 length.times { newpass << chars[rand(chars.size-1)] }
458 478 return newpass
459 479 end
460 480
461 481 def import_from_file(f)
462 482 data_hash = YAML.load(f)
463 483 @import_log = ""
464 484
465 485 country_data = data_hash[:countries]
466 486 site_data = data_hash[:sites]
467 487 user_data = data_hash[:users]
468 488
469 489 # import country
470 490 countries = {}
471 491 country_data.each_pair do |id,country|
472 492 c = Country.find_by_name(country[:name])
473 493 if c!=nil
474 494 countries[id] = c
475 495 @import_log << "Found #{country[:name]}\n"
476 496 else
477 497 countries[id] = Country.new(:name => country[:name])
478 498 countries[id].save
479 499 @import_log << "Created #{country[:name]}\n"
480 500 end
481 501 end
482 502
483 503 # import sites
484 504 sites = {}
485 505 site_data.each_pair do |id,site|
486 506 s = Site.find_by_name(site[:name])
487 507 if s!=nil
488 508 @import_log << "Found #{site[:name]}\n"
489 509 else
490 510 s = Site.new(:name => site[:name])
491 511 @import_log << "Created #{site[:name]}\n"
492 512 end
493 513 s.password = site[:password]
494 514 s.country = countries[site[:country_id]]
495 515 s.save
496 516 sites[id] = s
497 517 end
498 518
499 519 # import users
@@ -1,19 +1,20
1 1 module MainHelper
2 2
3 3 def link_to_description_if_any(name, problem, options={})
4 4 if !problem.url.blank?
5 5 return link_to name, problem.url, options
6 6 elsif !problem.description_filename.blank?
7 + #build a link to a problem (via task controller)
7 8 basename, ext = problem.description_filename.split('.')
8 9 options[:controller] = 'tasks'
9 10 options[:action] = 'download'
10 11 options[:id] = problem.id
11 12 options[:file] = basename
12 13 options[:ext] = ext
13 14 return link_to name, options
14 15 else
15 16 return ''
16 17 end
17 18 end
18 19
19 20 end
@@ -1,369 +1,374
1 1 require 'digest/sha1'
2 2 require 'net/pop'
3 3 require 'net/https'
4 4 require 'net/http'
5 5 require 'json'
6 6
7 7 class User < ActiveRecord::Base
8 8
9 9 has_and_belongs_to_many :roles
10 10
11 11 #has_and_belongs_to_many :groups
12 12 has_many :groups_users, class_name: 'GroupUser'
13 13 has_many :groups, :through => :groups_users
14 14
15 15 has_many :test_requests, -> {order(submitted_at: :desc)}
16 16
17 17 has_many :messages, -> { order(created_at: :desc) },
18 18 :class_name => "Message",
19 19 :foreign_key => "sender_id"
20 20
21 21 has_many :replied_messages, -> { order(created_at: :desc) },
22 22 :class_name => "Message",
23 23 :foreign_key => "receiver_id"
24 24
25 25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
26 26
27 27 belongs_to :site
28 28 belongs_to :country
29 29
30 30 has_and_belongs_to_many :contests, -> { order(:name)}
31 31
32 32 scope :activated_users, -> {where activated: true}
33 33
34 34 validates_presence_of :login
35 35 validates_uniqueness_of :login
36 36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
37 37 validates_length_of :login, :within => 3..30
38 38
39 39 validates_presence_of :full_name
40 40 validates_length_of :full_name, :minimum => 1
41 41
42 42 validates_presence_of :password, :if => :password_required?
43 43 validates_length_of :password, :within => 4..20, :if => :password_required?
44 44 validates_confirmation_of :password, :if => :password_required?
45 45
46 46 validates_format_of :email,
47 47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
48 48 :if => :email_validation?
49 49 validate :uniqueness_of_email_from_activated_users,
50 50 :if => :email_validation?
51 51 validate :enough_time_interval_between_same_email_registrations,
52 52 :if => :email_validation?
53 53
54 54 # these are for ytopc
55 55 # disable for now
56 56 #validates_presence_of :province
57 57
58 58 attr_accessor :password
59 59
60 60 before_save :encrypt_new_password
61 61 before_save :assign_default_site
62 62 before_save :assign_default_contest
63 63
64 64 # this is for will_paginate
65 65 cattr_reader :per_page
66 66 @@per_page = 50
67 67
68 68 def self.authenticate(login, password)
69 69 user = find_by_login(login)
70 70 if user
71 71 return user if user.authenticated?(password)
72 72 end
73 73 end
74 74
75 75 def authenticated?(password)
76 76 if self.activated
77 77 hashed_password == User.encrypt(password,self.salt)
78 78 else
79 79 false
80 80 end
81 81 end
82 82
83 83 def admin?
84 84 self.roles.where(name: 'admin').count > 0
85 85 end
86 86
87 87 def email_for_editing
88 88 if self.email==nil
89 89 "(unknown)"
90 90 elsif self.email==''
91 91 "(blank)"
92 92 else
93 93 self.email
94 94 end
95 95 end
96 96
97 97 def email_for_editing=(e)
98 98 self.email=e
99 99 end
100 100
101 101 def alias_for_editing
102 102 if self.alias==nil
103 103 "(unknown)"
104 104 elsif self.alias==''
105 105 "(blank)"
106 106 else
107 107 self.alias
108 108 end
109 109 end
110 110
111 111 def alias_for_editing=(e)
112 112 self.alias=e
113 113 end
114 114
115 115 def activation_key
116 116 if self.hashed_password==nil
117 117 encrypt_new_password
118 118 end
119 119 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
120 120 end
121 121
122 122 def verify_activation_key(key)
123 123 key == activation_key
124 124 end
125 125
126 126 def self.random_password(length=5)
127 127 chars = 'abcdefghjkmnopqrstuvwxyz'
128 128 password = ''
129 129 length.times { password << chars[rand(chars.length - 1)] }
130 130 password
131 131 end
132 132
133 133 def self.find_non_admin_with_prefix(prefix='')
134 134 users = User.all
135 135 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
136 136 end
137 137
138 138 # Contest information
139 139
140 140 def self.find_users_with_no_contest()
141 141 users = User.all
142 142 return users.find_all { |u| u.contests.length == 0 }
143 143 end
144 144
145 145
146 146 def contest_time_left
147 147 if GraderConfiguration.contest_mode?
148 148 return nil if site==nil
149 149 return site.time_left
150 150 elsif GraderConfiguration.indv_contest_mode?
151 151 time_limit = GraderConfiguration.contest_time_limit
152 152 if time_limit == nil
153 153 return nil
154 154 end
155 155 if contest_stat==nil or contest_stat.started_at==nil
156 156 return (Time.now.gmtime + time_limit) - Time.now.gmtime
157 157 else
158 158 finish_time = contest_stat.started_at + time_limit
159 159 current_time = Time.now.gmtime
160 160 if current_time > finish_time
161 161 return 0
162 162 else
163 163 return finish_time - current_time
164 164 end
165 165 end
166 166 else
167 167 return nil
168 168 end
169 169 end
170 170
171 171 def contest_finished?
172 172 if GraderConfiguration.contest_mode?
173 173 return false if site==nil
174 174 return site.finished?
175 175 elsif GraderConfiguration.indv_contest_mode?
176 176 return false if self.contest_stat==nil
177 177 return contest_time_left == 0
178 178 else
179 179 return false
180 180 end
181 181 end
182 182
183 183 def contest_started?
184 184 if GraderConfiguration.indv_contest_mode?
185 185 stat = self.contest_stat
186 186 return ((stat != nil) and (stat.started_at != nil))
187 187 elsif GraderConfiguration.contest_mode?
188 188 return true if site==nil
189 189 return site.started
190 190 else
191 191 return true
192 192 end
193 193 end
194 194
195 195 def update_start_time
196 196 stat = self.contest_stat
197 197 if stat.nil? or stat.started_at.nil?
198 198 stat ||= UserContestStat.new(:user => self)
199 199 stat.started_at = Time.now.gmtime
200 200 stat.save
201 201 end
202 202 end
203 203
204 204 def problem_in_user_contests?(problem)
205 205 problem_contests = problem.contests.all
206 206
207 207 if problem_contests.length == 0 # this is public contest
208 208 return true
209 209 end
210 210
211 211 contests.each do |contest|
212 212 if problem_contests.find {|c| c.id == contest.id }
213 213 return true
214 214 end
215 215 end
216 216 return false
217 217 end
218 218
219 219 def available_problems_group_by_contests
220 220 contest_problems = []
221 221 pin = {}
222 222 contests.enabled.each do |contest|
223 223 available_problems = contest.problems.available
224 224 contest_problems << {
225 225 :contest => contest,
226 226 :problems => available_problems
227 227 }
228 228 available_problems.each {|p| pin[p.id] = true}
229 229 end
230 230 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
231 231 contest_problems << {
232 232 :contest => nil,
233 233 :problems => other_avaiable_problems
234 234 }
235 235 return contest_problems
236 236 end
237 237
238 238 def solve_all_available_problems?
239 239 available_problems.each do |p|
240 240 u = self
241 241 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
242 242 return false if !p or !sub or sub.points < p.full_score
243 243 end
244 244 return true
245 245 end
246 246
247 247 #get a list of available problem
248 248 def available_problems
249 + # first, we check if this is normal mode
249 250 if not GraderConfiguration.multicontests?
251 +
252 + #if this is a normal mode
253 + #we show problem based on problem_group, if the config said so
250 254 if GraderConfiguration.use_problem_group?
251 255 return available_problems_in_group
252 256 else
253 257 return Problem.available_problems
254 258 end
255 259 else
260 + #this is multi contest mode
256 261 contest_problems = []
257 262 pin = {}
258 263 contests.enabled.each do |contest|
259 264 contest.problems.available.each do |problem|
260 265 if not pin.has_key? problem.id
261 266 contest_problems << problem
262 267 end
263 268 pin[problem.id] = true
264 269 end
265 270 end
266 271 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
267 272 return contest_problems + other_avaiable_problems
268 273 end
269 274 end
270 275
271 276 def available_problems_in_group
272 277 problem = []
273 278 self.groups.each do |group|
274 279 group.problems.where(available: true).each { |p| problem << p }
275 280 end
276 281 problem.uniq!
277 282 if problem
278 283 problem.sort! do |a,b|
279 284 case
280 285 when a.date_added < b.date_added
281 286 1
282 287 when a.date_added > b.date_added
283 288 -1
284 289 else
285 290 a.name <=> b.name
286 291 end
287 292 end
288 293 return problem
289 294 else
290 295 return []
291 296 end
292 297 end
293 298
294 299 def can_view_problem?(problem)
295 300 return true if admin?
296 301 return available_problems.include? problem
297 302 end
298 303
299 304 def self.clear_last_login
300 305 User.update_all(:last_ip => nil)
301 306 end
302 307
303 308 protected
304 309 def encrypt_new_password
305 310 return if password.blank?
306 311 self.salt = (10+rand(90)).to_s
307 312 self.hashed_password = User.encrypt(self.password,self.salt)
308 313 end
309 314
310 315 def assign_default_site
311 316 # have to catch error when migrating (because self.site is not available).
312 317 begin
313 318 if self.site==nil
314 319 self.site = Site.find_by_name('default')
315 320 if self.site==nil
316 321 self.site = Site.find(1) # when 'default has be renamed'
317 322 end
318 323 end
319 324 rescue
320 325 end
321 326 end
322 327
323 328 def assign_default_contest
324 329 # have to catch error when migrating (because self.site is not available).
325 330 begin
326 331 if self.contests.length == 0
327 332 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
328 333 if default_contest
329 334 self.contests = [default_contest]
330 335 end
331 336 end
332 337 rescue
333 338 end
334 339 end
335 340
336 341 def password_required?
337 342 self.hashed_password.blank? || !self.password.blank?
338 343 end
339 344
340 345 def self.encrypt(string,salt)
341 346 Digest::SHA1.hexdigest(salt + string)
342 347 end
343 348
344 349 def uniqueness_of_email_from_activated_users
345 350 user = User.activated_users.find_by_email(self.email)
346 351 if user and (user.login != self.login)
347 352 self.errors.add(:base,"Email has already been taken")
348 353 end
349 354 end
350 355
351 356 def enough_time_interval_between_same_email_registrations
352 357 return if !self.new_record?
353 358 return if self.activated
354 359 open_user = User.find_by_email(self.email,
355 360 :order => 'created_at DESC')
356 361 if open_user and open_user.created_at and
357 362 (open_user.created_at > Time.now.gmtime - 5.minutes)
358 363 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
359 364 end
360 365 end
361 366
362 367 def email_validation?
363 368 begin
364 369 return VALIDATE_USER_EMAILS
365 370 rescue
366 371 return false
367 372 end
368 373 end
369 374 end
@@ -1,73 +1,80
1 - %p
2 - %b Name:
3 - = @group.name
4 - %p
1 + .container-fluid
2 + .row
3 + .col-md-6
4 + %h1 Group #{@group.name}
5 + .row
6 + .col-md-6
5 7 %b Description:
6 8 = @group.description
7 -
8 9 %br
9 - = link_to 'Edit', edit_group_path(@group)
10 - \|
11 - = link_to 'Back', groups_path
12 -
10 + = link_to 'Edit', edit_group_path(@group), class: 'btn btn-primary'
13 11 .row
14 12 .col-md-12
15 13 %h1 Group details
16 14 .row
17 15 .col-md-6
18 16 .panel.panel-default
19 17 .panel-heading
20 18 .panel-title Users in this group
21 19 .panel-body
20 + %ul
21 + %li
22 + If you want to add several users to a group, it may be easier to just re-import those users in
23 + = link_to 'New list of users', new_list_user_admin_index_path
24 + page
22 25 =form_tag add_user_group_path(@group), class: 'form-inline' do
23 26 .form-group
24 27 =label_tag :user_id, "User"
25 28 =select_tag :user_id, options_from_collection_for_select(User.all,'id','full_name'), class: 'select2'
26 29 =submit_tag "Add",class: 'btn btn-primary'
27 30
28 31
29 32 %table.table.table-hover
30 33 %thead
31 34 %tr
32 35 %th Login
33 36 %th Full name
34 37 %th Remark
35 38 %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'
36 39
37 40 %tbody
38 41 - @group.users.each do |user|
39 42 %tr
40 43 %td= user.login
41 44 %td= user.full_name
42 45 %td= user.remark
43 46 %td= link_to 'Remove', remove_user_group_path(@group,user), :method => :delete, :data => { :confirm => "Remove #{user.full_name}?" }, class: 'btn btn-danger btn-sm'
44 47 .col-md-6
45 48 .panel.panel-default
46 49 .panel-heading
47 50 .panel-title Problems
48 51 .panel-body
49 -
52 + %ul
53 + %li
54 + 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
56 + page
50 57 =form_tag add_problem_group_path(@group), class: 'form-inline' do
51 58 .form-group
52 59 =label_tag :problem_id, "Problem"
53 60 =select_tag :problem_id, options_from_collection_for_select(Problem.all,'id','full_name'), class: 'select2'
54 61 =submit_tag "Add",class: 'btn btn-primary'
55 62
56 63
57 64 %table.table.table-hover
58 65 %thead
59 66 %tr
60 67 %th name
61 68 %th Full name
62 69 %th Full score
63 70 %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'
64 71
65 72 %tbody
66 73 - @group.problems.each do |problem|
67 74 %tr
68 75 %td= problem.name
69 76 %td= problem.full_name
70 77 %td= problem.full_score
71 78 %td= link_to 'Remove', remove_problem_group_path(@group,problem), :method => :delete, :data => { :confirm => "Remove #{problem.full_name}?" }, class: 'btn btn-danger btn-sm'
72 79
73 80
@@ -1,3 +1,11
1 + .container-fluid
1 2 %h1 Current Score
3 + = form_tag current_score_report_path, method: 'get' do
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'
6 + = submit_tag 'Apply',class: 'btn btn-default'
7 +
8 + %br
9 +
2 10
3 11 = render "score_table"
@@ -1,190 +1,190
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 collection do
16 16 get 'console'
17 17 end
18 18 end
19 19
20 20 resources :announcements do
21 21 member do
22 22 get 'toggle','toggle_front'
23 23 end
24 24 end
25 25
26 26 resources :problems do
27 27 member do
28 28 get 'toggle'
29 29 get 'toggle_test'
30 30 get 'toggle_view_testcase'
31 31 get 'stat'
32 32 end
33 33 collection do
34 34 get 'turn_all_off'
35 35 get 'turn_all_on'
36 36 get 'import'
37 37 get 'manage'
38 38 get 'quick_create'
39 39 post 'do_manage'
40 40 post 'do_import'
41 41 end
42 42 end
43 43
44 44 resources :groups do
45 45 member do
46 46 post 'add_user', to: 'groups#add_user', as: 'add_user'
47 47 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
48 48 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
49 49 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
50 50 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
51 51 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
52 52 end
53 53 collection do
54 54
55 55 end
56 56 end
57 57
58 58 resources :testcases, only: [] do
59 59 member do
60 60 get 'download_input'
61 61 get 'download_sol'
62 62 end
63 63 collection do
64 64 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
65 65 end
66 66 end
67 67
68 68 resources :grader_configuration, controller: 'configurations'
69 69
70 70 resources :users do
71 71 member do
72 72 get 'toggle_activate', 'toggle_enable'
73 73 get 'stat'
74 74 end
75 75 collection do
76 76 get 'profile'
77 77 post 'chg_passwd'
78 78 end
79 79 end
80 80
81 81 resources :submissions do
82 82 member do
83 83 get 'download'
84 84 get 'compiler_msg'
85 85 get 'rejudge'
86 86 get 'source'
87 87 end
88 88 collection do
89 89 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
90 90 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
91 91 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
92 92 end
93 93 end
94 94
95 95
96 96 #user admin
97 97 resources :user_admin do
98 98 collection do
99 99 match 'bulk_manage', via: [:get, :post]
100 100 get 'bulk_mail'
101 101 get 'user_stat'
102 102 get 'import'
103 103 get 'new_list'
104 104 get 'admin'
105 105 get 'active'
106 106 get 'mass_mailing'
107 107 get 'revoke_admin'
108 108 post 'grant_admin'
109 109 match 'create_from_list', via: [:get, :post]
110 110 match 'random_all_passwords', via: [:get, :post]
111 111 end
112 112 member do
113 113 get 'clear_last_ip'
114 114 end
115 115 end
116 116
117 117 resources :contest_management, only: [:index] do
118 118 collection do
119 119 get 'user_stat'
120 120 get 'clear_stat'
121 121 get 'clear_all_stat'
122 122 get 'change_contest_mode'
123 123 end
124 124 end
125 125
126 126 #get 'user_admin', to: 'user_admin#index'
127 127 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
128 128 #post 'user_admin', to: 'user_admin#create'
129 129 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
130 130
131 131 #singular resource
132 132 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
133 133 #report
134 134 resource :report, only: [], controller: 'report' do
135 135 get 'login'
136 136 get 'multiple_login'
137 137 get 'problem_hof/:id', action: 'problem_hof'
138 - get 'current_score'
138 + get 'current_score(/:group_id)', action: 'current_score', as: 'current_score'
139 139 get 'max_score'
140 140 post 'show_max_score'
141 141 end
142 142 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
143 143 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
144 144 #get "report/login"
145 145 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
146 146 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
147 147
148 148 resource :main, only: [], controller: 'main' do
149 149 get 'login'
150 150 get 'logout'
151 151 get 'list'
152 152 get 'submission(/:id)', action: 'submission', as: 'main_submission'
153 153 get 'announcements'
154 154 get 'help'
155 155 post 'submit'
156 156 end
157 157 #main
158 158 #get "main/list"
159 159 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
160 160 #post 'main/submit', to: 'main#submit'
161 161 #get 'main/announcements', to: 'main#announcements'
162 162
163 163
164 164 #
165 165 get 'tasks/view/:file.:ext' => 'tasks#view'
166 166 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
167 167 get 'heartbeat/:id/edit' => 'heartbeat#edit'
168 168
169 169 #grader
170 170 get 'graders/list', to: 'graders#list', as: 'grader_list'
171 171 namespace :graders do
172 172 get 'task/:id/:type', action: 'task', as: 'task'
173 173 get 'view/:id/:type', action: 'view', as: 'view'
174 174 get 'clear/:id', action: 'clear', as: 'clear'
175 175 get 'stop'
176 176 get 'stop_all'
177 177 get 'clear_all'
178 178 get 'clear_terminated'
179 179 get 'start_grading'
180 180 get 'start_exam'
181 181
182 182 end
183 183
184 184
185 185 # See how all your routes lay out with "rake routes"
186 186
187 187 # This is a legacy wild controller route that's not recommended for RESTful applications.
188 188 # Note: This route will make all actions in every controller accessible via GET requests.
189 189 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
190 190 end
deleted file
You need to be logged in to leave comments. Login now