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

r843:2e56e1493802 - - 5 files changed: 39 inserted, 85 deleted

@@ -0,0 +1,5
1 + class AddIndexToTaskStatus < ActiveRecord::Migration[5.2]
2 + def change
3 + add_index :tasks, :status
4 + end
5 + end
@@ -1,236 +1,167
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 @users = User.all
12 12 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
13 13 @contests = Contest.enabled
14 14 end
15 15
16 16 def active
17 17 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
18 18 @users = []
19 19 sessions.each do |session|
20 20 if session.data[:user_id]
21 21 @users << User.find(session.data[:user_id])
22 22 end
23 23 end
24 24 end
25 25
26 26 def show
27 27 @user = User.find(params[:id])
28 28 end
29 29
30 30 def new
31 31 @user = User.new
32 32 end
33 33
34 34 def create
35 35 @user = User.new(user_params)
36 36 @user.activated = true
37 37 if @user.save
38 38 flash[:notice] = 'User was successfully created.'
39 39 redirect_to :action => 'index'
40 40 else
41 41 render :action => 'new'
42 - end
42 + end
43 43 end
44 44
45 45 def clear_last_ip
46 46 @user = User.find(params[:id])
47 47 @user.last_ip = nil
48 48 @user.save
49 49 redirect_to action: 'index', page: params[:page]
50 50 end
51 51
52 52 def create_from_list
53 53 lines = params[:user_list]
54 54
55 - note = []
56 - error_note = []
57 - error_msg = nil
58 - ok_user = []
59 -
60 - lines.split("\n").each do |line|
61 - #split with large limit, this will cause consecutive ',' to be result in a blank
62 - items = line.chomp.split(',',1000)
63 - if items.length>=2
64 - login = items[0]
65 - full_name = items[1]
66 - remark =''
67 - user_alias = ''
68 -
69 - added_random_password = false
70 - added_password = false
71 - if items.length >= 3
72 - if items[2].chomp(" ").length > 0
73 - password = items[2].chomp(" ")
74 - added_password = true
75 - end
76 - else
77 - password = random_password
78 - added_random_password=true;
79 - end
80 -
81 - if items.length>= 4 and items[3].chomp(" ").length > 0;
82 - user_alias = items[3].chomp(" ")
83 - else
84 - user_alias = login
85 - end
86 -
87 55
88 - has_remark = false
89 - if items.length>=5
90 - remark = items[4].strip;
91 - has_remark = true
92 - end
56 + res = User.create_from_list(lines)
57 + error_logins = res[:error_logins]
58 + error_msg = res[:first_error]
59 + ok_user = res[:created_users]
93 60
94 - user = User.find_by_login(login)
95 - if (user)
96 - user.full_name = full_name
97 - user.remark = remark if has_remark
98 - user.password = password if added_password || added_random_password
99 - else
100 - #create a random password if none are given
101 - password = random_password unless password
102 - user = User.new({:login => login,
103 - :full_name => full_name,
104 - :password => password,
105 - :password_confirmation => password,
106 - :alias => user_alias,
107 - :remark => remark})
108 - end
109 - user.activated = true
110 -
111 - if user.save
112 - if added_random_password
113 - note << "'#{login}' (+)"
114 - else
115 - note << login
116 - end
117 - ok_user << user
118 - else
119 - error_note << "'#{login}'"
120 - error_msg = user.errors.full_messages.to_sentence unless error_msg
121 - end
122 -
123 - end
124 - end
125 61
126 62 #add to group
127 63 if params[:add_to_group]
128 - group = Group.where(id: params[:group_id]).first
129 - if group
130 - group.users << ok_user
131 - end
64 + group = Group.find_by(id: params[:group_id])&.add_users_skip_existing(ok_user)
132 65 end
133 66
134 67 # show flash
135 - if note.size > 0
136 - flash[:success] = 'User(s) ' + note.join(', ') +
137 - ' were successfully created. ' +
138 - '( (+) - created with random passwords.)'
68 + if ok_user.count > 0
69 + flash[:success] = "#{ok_user.count} user(s) was created or updated successfully"
139 70 end
140 - if error_note.size > 0
71 + if error_logins.size > 0
141 72 flash[:error] = "Following user(s) failed to be created: " + error_note.join(', ') + ". The error of the first failed one are: " + error_msg;
142 73 end
143 74 redirect_to :action => 'index'
144 75 end
145 76
146 77 def edit
147 78 @user = User.find(params[:id])
148 79 end
149 80
150 81 def update
151 82 @user = User.find(params[:id])
152 83 if @user.update_attributes(user_params)
153 84 flash[:notice] = 'User was successfully updated.'
154 85 redirect_to :action => 'show', :id => @user
155 86 else
156 87 render :action => 'edit'
157 88 end
158 89 end
159 90
160 91 def destroy
161 92 User.find(params[:id]).destroy
162 93 redirect_to :action => 'index'
163 94 end
164 95
165 96 def user_stat
166 97 if params[:commit] == 'download csv'
167 98 @problems = Problem.all
168 99 else
169 100 @problems = Problem.available_problems
170 101 end
171 102 @users = User.includes(:contests, :contest_stat).where(enabled: true)
172 103 @scorearray = Array.new
173 104 @users.each do |u|
174 105 ustat = Array.new
175 106 ustat[0] = u
176 107 @problems.each do |p|
177 108 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
178 109 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
179 110 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
180 111 else
181 112 ustat << [0,false]
182 113 end
183 114 end
184 115 @scorearray << ustat
185 116 end
186 117 if params[:commit] == 'download csv' then
187 118 csv = gen_csv_from_scorearray(@scorearray,@problems)
188 119 send_data csv, filename: 'last_score.csv'
189 120 else
190 121 render template: 'user_admin/user_stat'
191 122 end
192 123 end
193 124
194 125 def user_stat_max
195 126 if params[:commit] == 'download csv'
196 127 @problems = Problem.all
197 128 else
198 129 @problems = Problem.available_problems
199 130 end
200 131 @users = User.includes(:contests).includes(:contest_stat).all
201 132 @scorearray = Array.new
202 133 #set up range from param
203 134 since_id = params.fetch(:since_id, 0).to_i
204 135 until_id = params.fetch(:until_id, 0).to_i
205 136 @users.each do |u|
206 137 ustat = Array.new
207 138 ustat[0] = u
208 139 @problems.each do |p|
209 140 max_points = 0
210 141 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
211 142 max_points = sub.points if sub and sub.points and (sub.points > max_points)
212 143 end
213 144 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
214 145 end
215 146 @scorearray << ustat
216 147 end
217 148
218 149 if params[:commit] == 'download csv' then
219 150 csv = gen_csv_from_scorearray(@scorearray,@problems)
220 151 send_data csv, filename: 'max_score.csv'
221 152 else
222 153 render template: 'user_admin/user_stat'
223 154 end
224 155 end
225 156
226 157 def import
227 158 if params[:file]==''
228 159 flash[:notice] = 'Error importing no file'
229 160 redirect_to :action => 'index' and return
230 161 end
231 162 import_from_file(params[:file])
232 163 end
233 164
234 165 def random_all_passwords
235 166 users = User.all
236 167 @prefix = params[:prefix] || ''
@@ -308,220 +239,225
308 239 redirect_to :action => 'contest_management' and return
309 240 end
310 241
311 242 lines = params[:login_list]
312 243 if !lines or lines.blank?
313 244 flash[:notice] = 'You entered an empty list.'
314 245 redirect_to :action => 'contest_management' and return
315 246 end
316 247
317 248 note = []
318 249 users = []
319 250 lines.split("\n").each do |line|
320 251 user = User.find_by_login(line.chomp)
321 252 if user
322 253 if operation=='add'
323 254 if ! user.contests.include? contest
324 255 user.contests << contest
325 256 end
326 257 elsif operation=='remove'
327 258 user.contests.delete(contest)
328 259 else
329 260 user.contests = [contest]
330 261 end
331 262
332 263 if params[:reset_timer]
333 264 user.contest_stat.forced_logout = true
334 265 user.contest_stat.reset_timer_and_save
335 266 end
336 267
337 268 if params[:notification_emails]
338 269 send_contest_update_notification_email(user, contest)
339 270 end
340 271
341 272 note << user.login
342 273 users << user
343 274 end
344 275 end
345 276
346 277 if params[:reset_timer]
347 278 logout_users(users)
348 279 end
349 280
350 281 flash[:notice] = 'User(s) ' + note.join(', ') +
351 282 ' were successfully modified. '
352 283 redirect_to :action => 'contest_management'
353 284 end
354 285
355 286 # admin management
356 287
357 288 def admin
358 289 @admins = Role.where(name: 'admin').take.users
359 290 @tas = Role.where(name: 'ta').take.users
360 291 end
361 292
362 293 def modify_role
363 294 user = User.find_by_login(params[:login])
364 295 role = Role.find_by_name(params[:role])
365 296 unless user && role
366 297 flash[:error] = 'Unknown user or role'
367 298 redirect_to admin_user_admin_index_path
368 299 return
369 300 end
370 301 if params[:commit] == 'Grant'
371 302 #grant role
372 303 user.roles << role
373 304 flash[:notice] = "User '#{user.login}' has been granted the role '#{role.name}'"
374 305 else
375 306 #revoke role
376 307 if user.login == 'root' && role.name == 'admin'
377 308 flash[:error] = 'You cannot revoke admisnistrator permission from root.'
378 309 redirect_to admin_user_admin_index_path
379 310 return
380 311 end
381 312 user.roles.delete(role)
382 313 flash[:notice] = "The role '#{role.name}' has been revoked from User '#{user.login}'"
383 314 end
384 315 redirect_to admin_user_admin_index_path
385 316 end
386 317
387 318 # mass mailing
388 319
389 320 def mass_mailing
390 321 end
391 322
392 323 def bulk_mail
393 324 lines = params[:login_list]
394 325 if !lines or lines.blank?
395 326 flash[:notice] = 'You entered an empty list.'
396 327 redirect_to :action => 'mass_mailing' and return
397 328 end
398 329
399 330 mail_subject = params[:subject]
400 331 if !mail_subject or mail_subject.blank?
401 332 flash[:notice] = 'You entered an empty mail subject.'
402 333 redirect_to :action => 'mass_mailing' and return
403 334 end
404 -
335 +
405 336 mail_body = params[:email_body]
406 337 if !mail_body or mail_body.blank?
407 338 flash[:notice] = 'You entered an empty mail body.'
408 339 redirect_to :action => 'mass_mailing' and return
409 340 end
410 341
411 342 note = []
412 343 users = []
413 344 lines.split("\n").each do |line|
414 345 user = User.find_by_login(line.chomp)
415 346 if user
416 347 send_mail(user.email, mail_subject, mail_body)
417 348 note << user.login
418 349 end
419 350 end
420 -
351 +
421 352 flash[:notice] = 'User(s) ' + note.join(', ') +
422 353 ' were successfully modified. '
423 354 redirect_to :action => 'mass_mailing'
424 355 end
425 356
426 357 #bulk manage
427 358 def bulk_manage
428 359
429 - begin
430 - @users = User.where('(login REGEXP ?) OR (remark REGEXP ?)',params[:regex],params[:regex]) if params[:regex]
431 - @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised
360 + begin
361 + if params[:filter_group]
362 + @users = Group.find_by(id: params[:filter_group_id]).users
363 + else
364 + @users = User.all
365 + end
366 + @users = @users.where('(login REGEXP ?) OR (remark REGEXP ?)',params[:regex],params[:regex]) unless params[:regex].blank?
367 + @users.count if @users #test the sql
432 368 rescue Exception
433 369 flash[:error] = 'Regular Expression is malformed'
434 370 @users = nil
435 371 end
436 372
437 373 if params[:commit]
438 374 @action = {}
439 375 @action[:set_enable] = params[:enabled]
440 376 @action[:enabled] = params[:enable] == "1"
441 377 @action[:gen_password] = params[:gen_password]
442 378 @action[:add_group] = params[:add_group]
443 379 @action[:group_name] = params[:group_name]
444 380 end
445 381
446 382 if params[:commit] == "Perform"
447 383 if @action[:set_enable]
448 384 @users.update_all(enabled: @action[:enabled])
449 385 end
450 386 if @action[:gen_password]
451 387 @users.each do |u|
452 388 password = random_password
453 389 u.password = password
454 390 u.password_confirmation = password
455 391 u.save
456 392 end
457 393 end
458 394 if @action[:add_group] and @action[:group_name]
459 395 @group = Group.find(@action[:group_name])
460 396 ok = []
461 397 failed = []
462 398 @users.each do |user|
463 399 begin
464 400 @group.users << user
465 401 ok << user.login
466 402 rescue => e
467 403 failed << user.login
468 404 end
469 405 end
470 406 flash[:success] = "The following users are added to the 'group #{@group.name}': " + ok.join(', ') if ok.count > 0
471 407 flash[:alert] = "The following users are already in the 'group #{@group.name}': " + failed.join(', ') if failed.count > 0
472 408 end
473 409 end
474 410 end
475 411
476 412 protected
477 413
478 414 def random_password(length=5)
479 415 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
480 416 newpass = ""
481 417 length.times { newpass << chars[rand(chars.size-1)] }
482 418 return newpass
483 419 end
484 420
485 421 def import_from_file(f)
486 422 data_hash = YAML.load(f)
487 423 @import_log = ""
488 424
489 425 country_data = data_hash[:countries]
490 426 site_data = data_hash[:sites]
491 427 user_data = data_hash[:users]
492 428
493 429 # import country
494 430 countries = {}
495 431 country_data.each_pair do |id,country|
496 432 c = Country.find_by_name(country[:name])
497 433 if c!=nil
498 434 countries[id] = c
499 435 @import_log << "Found #{country[:name]}\n"
500 436 else
501 437 countries[id] = Country.new(:name => country[:name])
502 438 countries[id].save
503 439 @import_log << "Created #{country[:name]}\n"
504 440 end
505 441 end
506 442
507 443 # import sites
508 444 sites = {}
509 445 site_data.each_pair do |id,site|
510 446 s = Site.find_by_name(site[:name])
511 447 if s!=nil
512 448 @import_log << "Found #{site[:name]}\n"
513 449 else
514 450 s = Site.new(:name => site[:name])
515 451 @import_log << "Created #{site[:name]}\n"
516 452 end
517 453 s.password = site[:password]
518 454 s.country = countries[site[:country_id]]
519 455 s.save
520 456 sites[id] = s
521 457 end
522 458
523 459 # import users
524 460 user_data.each_pair do |id,user|
525 461 u = User.find_by_login(user[:login])
526 462 if u!=nil
527 463 @import_log << "Found #{user[:login]}\n"
@@ -1,13 +1,20
1 1 class Group < ActiveRecord::Base
2 2 has_many :groups_problems, class_name: 'GroupProblem'
3 3 has_many :problems, :through => :groups_problems
4 4
5 5 has_many :groups_users, class_name: 'GroupUser'
6 6 has_many :users, :through => :groups_users
7 7
8 8 #has_and_belongs_to_many :problems
9 9 #has_and_belongs_to_many :users
10 10
11 + def add_users_skip_existing(users_list)
12 + new_list = []
13 + users_list.uniq.each do |u|
14 + new_list << u unless users.include? u
15 + end
16 + users << new_list
17 + end
11 18
12 19 end
13 20
@@ -1,82 +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 24 page. You can also use
25 25 = link_to 'Bulk Manage User', bulk_manage_user_admin_index_path
26 26 page.
27 27 =form_tag add_user_group_path(@group), class: 'form-inline' do
28 28 .form-group
29 29 =label_tag :user_id, "User"
30 - =select_tag :user_id, options_from_collection_for_select(User.all,'id','full_name'), class: 'select2', style: 'width: 10em';
30 + =select_tag :user_id, options_from_collection_for_select(User.all,'id','login_with_name'), class: 'select2', style: 'width: 25em';
31 31 =submit_tag "Add",class: 'btn btn-primary'
32 32
33 33
34 34 %table.table.table-hover
35 35 %thead
36 36 %tr
37 37 %th Login
38 38 %th Full name
39 39 %th Remark
40 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'
41 41
42 42 %tbody
43 43 - @group.users.each do |user|
44 44 %tr
45 45 %td= user.login
46 46 %td= user.full_name
47 47 %td= user.remark
48 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'
49 49 .col-md-6
50 50 .panel.panel-default
51 51 .panel-heading
52 52 .panel-title Problems
53 53 .panel-body
54 54 %ul
55 55 %li
56 56 If you want to add several problem to a group, it may be easier to bulk manage them in the
57 57 = link_to 'Bulk Manage Problems', manage_problems_path
58 58 page
59 59 =form_tag add_problem_group_path(@group), class: 'form-inline' do
60 60 .form-group
61 61 =label_tag :problem_id, "Problem"
62 - =select_tag :problem_id, options_from_collection_for_select(Problem.all,'id','full_name'), class: 'select2', style: 'width: 10em';
62 + =select_tag :problem_id, options_from_collection_for_select(Problem.all,'id','long_name'), class: 'select2', style: 'width: 25em';
63 63 =submit_tag "Add",class: 'btn btn-primary'
64 64
65 65
66 66 %table.table.table-hover
67 67 %thead
68 68 %tr
69 69 %th name
70 70 %th Full name
71 71 %th Full score
72 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'
73 73
74 74 %tbody
75 75 - @group.problems.each do |problem|
76 76 %tr
77 77 %td= problem.name
78 78 %td= problem.full_name
79 79 %td= problem.full_score
80 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'
81 81
82 82
@@ -1,86 +1,92
1 1 %h1 Bulk Manage User
2 2
3 3 = form_tag bulk_manage_user_admin_index_path
4 4 .row
5 5 .col-md-6
6 6 .panel.panel-primary
7 7 .panel-title.panel-heading
8 8 Filter User
9 9 .panel-body
10 10 Filtering users whose login match the following MySQL regex
11 11 .form-group
12 12 = label_tag "regex", 'Regex Pattern'
13 13 = text_field_tag "regex", params[:regex], class: 'form-control'
14 14 %p
15 15 Example
16 16 %ul
17 17 %li
18 18 %code root
19 19 matches every user whose login contains "root"
20 20 %li
21 21 %code ^56
22 22 matches every user whose login starts with "56"
23 23 %li
24 24 %code 21$
25 25 matches every user whose login ends with "21"
26 + .form-group
27 + .div.checkbox
28 + %label
29 + = check_box_tag :filter_group, 1, params[:filter_group] == '1'
30 + Apply to this group only
31 + = select_tag "filter_group_id", options_from_collection_for_select( Group.all, 'id','name',params[:filter_group_id]), id: 'group_name',class: 'select2'
26 32 .col-md-6
27 33 .panel.panel-primary
28 34 .panel-title.panel-heading
29 35 Action
30 36 .panel-body
31 37 .row.form-group
32 38 .col-md-6
33 39 %label.checkbox-inline
34 40 = check_box_tag "enabled", true, params[:enabled]
35 41 Change "Enabled" to
36 42 .col-md-3
37 43 %label.radio-inline
38 44 = radio_button_tag "enable", 1, params[:enable] == '1', id: 'enable-yes'
39 45 Yes
40 46 .col-md-3
41 47 %label.radio-inline
42 48 = radio_button_tag "enable", 0, params[:enable] == '0', id: 'enable-no'
43 49 No
44 50 .row.form-group
45 51 .col-md-6
46 52 %label.checkbox-inline
47 53 = check_box_tag "gen_password", true, params[:gen_password]
48 54 Generate new random password
49 55 .row.form-group
50 56 .col-md-4
51 57 %label.checkbox-inline
52 58 = check_box_tag "add_group", true, params[:add_group]
53 59 Add users to group
54 60 %label.col-md-3.control-label.text-right Group name
55 61 .col-md-5
56 62 = select_tag "group_name", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'form-control select2'
57 63
58 64
59 65 .row
60 66 .col-md-12
61 67 = submit_tag "Preview Result", class: 'btn btn-default'
62 68 - if @users
63 69 .row
64 70 .col-md-4
65 71 - if @action
66 72 %h2 Confirmation
67 73 - if @action[:set_enable]
68 74 .alert.alert-info The following users will be set #{(@action[:enabled] ? 'enable' : 'disable')}.
69 75 - if @action[:gen_password]
70 76 .alert.alert-info The password of the following users will be randomly generated.
71 77 .row
72 78 .col-md-4
73 79 = submit_tag "Perform", class: 'btn btn-primary'
74 80 .row
75 81 .col-md-12
76 82 The pattern matches #{@users.count} following users.
77 83 %br
78 84 - @users.each do |user|
79 85 = user.login
80 86 = ' '
81 87 = user.full_name
82 88 = ' '
83 89 = "(#{user.remark})" if user.remark
84 90 %br
85 91
86 92
You need to be logged in to leave comments. Login now