Description:
add bulk manage for enablind users
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r643:39e4a808eb76 - - 7 files changed: 132 inserted, 7 deleted

@@ -0,0 +1,77
1 + %h1 Bulk Manage User
2 +
3 + = form_tag bulk_manage_user_admin_path
4 + .row
5 + .col-md-6
6 + .panel.panel-primary
7 + .panel-title.panel-heading
8 + Filter User
9 + .panel-body
10 + Filtering users whose login match the following MySQL regex
11 + .form-group
12 + = label_tag "regex", 'Regex Pattern'
13 + = text_field_tag "regex", params[:regex], class: 'form-control'
14 + %p
15 + Example
16 + %ul
17 + %li
18 + %code root
19 + matches every user whose login contains "root"
20 + %li
21 + %code ^56
22 + matches every user whose login starts with "56"
23 + %li
24 + %code 21$
25 + matches every user whose login ends with "21"
26 + .col-md-6
27 + .panel.panel-primary
28 + .panel-title.panel-heading
29 + Action
30 + .panel-body
31 + .row.form-group
32 + .col-md-6
33 + %label.checkbox-inline
34 + = check_box_tag "enabled", true, params[:enabled]
35 + Change "Enabled" to
36 + .col-md-3
37 + %label.radio-inline
38 + = radio_button_tag "enable", 1, params[:enable] == '1', id: 'enable-yes'
39 + Yes
40 + .col-md-3
41 + %label.radio-inline
42 + = radio_button_tag "enable", 0, params[:enable] == '0', id: 'enable-no'
43 + No
44 + .row.form-group
45 + .col-md-6
46 + %label.checkbox-inline
47 + = check_box_tag "gen_password", true, params[:gen_password]
48 + Generate new random password
49 +
50 + .row
51 + .col-md-12
52 + = submit_tag "Preview Result", class: 'btn btn-default'
53 + - if @users
54 + .row
55 + .col-md-4
56 + - if @action
57 + %h2 Confirmation
58 + - if @action[:set_enable]
59 + .alert.alert-info The following users will be set #{(@action[:enabled] ? 'enable' : 'disable')}.
60 + - if @action[:gen_password]
61 + .alert.alert-info The password of the following users will be randomly generated.
62 + .row
63 + .col-md-4
64 + = submit_tag "Perform", class: 'btn btn-primary'
65 + .row
66 + .col-md-12
67 + The pattern matches #{@users.count} following users.
68 + %br
69 + - @users.each do |user|
70 + = user.login
71 + = ' '
72 + = user.full_name
73 + = ' '
74 + = "(#{user.remark})" if user.remark
75 + %br
76 +
77 +
@@ -218,338 +218,371
218 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
218 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
219 @changed = false
219 @changed = false
220 if request.request_method == 'POST'
220 if request.request_method == 'POST'
221 @non_admin_users.each do |user|
221 @non_admin_users.each do |user|
222 password = random_password
222 password = random_password
223 user.password = password
223 user.password = password
224 user.password_confirmation = password
224 user.password_confirmation = password
225 user.save
225 user.save
226 end
226 end
227 @changed = true
227 @changed = true
228 end
228 end
229 end
229 end
230
230
231 # contest management
231 # contest management
232
232
233 def contests
233 def contests
234 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
234 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
235 @contests = Contest.enabled
235 @contests = Contest.enabled
236 end
236 end
237
237
238 def assign_from_list
238 def assign_from_list
239 contest_id = params[:users_contest_id]
239 contest_id = params[:users_contest_id]
240 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
240 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
241 contest = Contest.find(params[:new_contest][:id])
241 contest = Contest.find(params[:new_contest][:id])
242 if !contest
242 if !contest
243 flash[:notice] = 'Error: no contest'
243 flash[:notice] = 'Error: no contest'
244 redirect_to :action => 'contests', :id =>contest_id
244 redirect_to :action => 'contests', :id =>contest_id
245 end
245 end
246
246
247 note = []
247 note = []
248 users.each do |u|
248 users.each do |u|
249 u.contests = [contest]
249 u.contests = [contest]
250 note << u.login
250 note << u.login
251 end
251 end
252 flash[:notice] = 'User(s) ' + note.join(', ') +
252 flash[:notice] = 'User(s) ' + note.join(', ') +
253 " were successfully reassigned to #{contest.title}."
253 " were successfully reassigned to #{contest.title}."
254 redirect_to :action => 'contests', :id =>contest.id
254 redirect_to :action => 'contests', :id =>contest.id
255 end
255 end
256
256
257 def add_to_contest
257 def add_to_contest
258 user = User.find(params[:id])
258 user = User.find(params[:id])
259 contest = Contest.find(params[:contest_id])
259 contest = Contest.find(params[:contest_id])
260 if user and contest
260 if user and contest
261 user.contests << contest
261 user.contests << contest
262 end
262 end
263 redirect_to :action => 'index'
263 redirect_to :action => 'index'
264 end
264 end
265
265
266 def remove_from_contest
266 def remove_from_contest
267 user = User.find(params[:id])
267 user = User.find(params[:id])
268 contest = Contest.find(params[:contest_id])
268 contest = Contest.find(params[:contest_id])
269 if user and contest
269 if user and contest
270 user.contests.delete(contest)
270 user.contests.delete(contest)
271 end
271 end
272 redirect_to :action => 'index'
272 redirect_to :action => 'index'
273 end
273 end
274
274
275 def contest_management
275 def contest_management
276 end
276 end
277
277
278 def manage_contest
278 def manage_contest
279 contest = Contest.find(params[:contest][:id])
279 contest = Contest.find(params[:contest][:id])
280 if !contest
280 if !contest
281 flash[:notice] = 'You did not choose the contest.'
281 flash[:notice] = 'You did not choose the contest.'
282 redirect_to :action => 'contest_management' and return
282 redirect_to :action => 'contest_management' and return
283 end
283 end
284
284
285 operation = params[:operation]
285 operation = params[:operation]
286
286
287 if not ['add','remove','assign'].include? operation
287 if not ['add','remove','assign'].include? operation
288 flash[:notice] = 'You did not choose the operation to perform.'
288 flash[:notice] = 'You did not choose the operation to perform.'
289 redirect_to :action => 'contest_management' and return
289 redirect_to :action => 'contest_management' and return
290 end
290 end
291
291
292 lines = params[:login_list]
292 lines = params[:login_list]
293 if !lines or lines.blank?
293 if !lines or lines.blank?
294 flash[:notice] = 'You entered an empty list.'
294 flash[:notice] = 'You entered an empty list.'
295 redirect_to :action => 'contest_management' and return
295 redirect_to :action => 'contest_management' and return
296 end
296 end
297
297
298 note = []
298 note = []
299 users = []
299 users = []
300 lines.split("\n").each do |line|
300 lines.split("\n").each do |line|
301 user = User.find_by_login(line.chomp)
301 user = User.find_by_login(line.chomp)
302 if user
302 if user
303 if operation=='add'
303 if operation=='add'
304 if ! user.contests.include? contest
304 if ! user.contests.include? contest
305 user.contests << contest
305 user.contests << contest
306 end
306 end
307 elsif operation=='remove'
307 elsif operation=='remove'
308 user.contests.delete(contest)
308 user.contests.delete(contest)
309 else
309 else
310 user.contests = [contest]
310 user.contests = [contest]
311 end
311 end
312
312
313 if params[:reset_timer]
313 if params[:reset_timer]
314 user.contest_stat.forced_logout = true
314 user.contest_stat.forced_logout = true
315 user.contest_stat.reset_timer_and_save
315 user.contest_stat.reset_timer_and_save
316 end
316 end
317
317
318 if params[:notification_emails]
318 if params[:notification_emails]
319 send_contest_update_notification_email(user, contest)
319 send_contest_update_notification_email(user, contest)
320 end
320 end
321
321
322 note << user.login
322 note << user.login
323 users << user
323 users << user
324 end
324 end
325 end
325 end
326
326
327 if params[:reset_timer]
327 if params[:reset_timer]
328 logout_users(users)
328 logout_users(users)
329 end
329 end
330
330
331 flash[:notice] = 'User(s) ' + note.join(', ') +
331 flash[:notice] = 'User(s) ' + note.join(', ') +
332 ' were successfully modified. '
332 ' were successfully modified. '
333 redirect_to :action => 'contest_management'
333 redirect_to :action => 'contest_management'
334 end
334 end
335
335
336 # admin management
336 # admin management
337
337
338 def admin
338 def admin
339 @admins = User.all.find_all {|user| user.admin? }
339 @admins = User.all.find_all {|user| user.admin? }
340 end
340 end
341
341
342 def grant_admin
342 def grant_admin
343 login = params[:login]
343 login = params[:login]
344 user = User.find_by_login(login)
344 user = User.find_by_login(login)
345 if user!=nil
345 if user!=nil
346 admin_role = Role.find_by_name('admin')
346 admin_role = Role.find_by_name('admin')
347 user.roles << admin_role
347 user.roles << admin_role
348 else
348 else
349 flash[:notice] = 'Unknown user'
349 flash[:notice] = 'Unknown user'
350 end
350 end
351 flash[:notice] = 'User added as admins'
351 flash[:notice] = 'User added as admins'
352 redirect_to :action => 'admin'
352 redirect_to :action => 'admin'
353 end
353 end
354
354
355 def revoke_admin
355 def revoke_admin
356 user = User.find(params[:id])
356 user = User.find(params[:id])
357 if user==nil
357 if user==nil
358 flash[:notice] = 'Unknown user'
358 flash[:notice] = 'Unknown user'
359 redirect_to :action => 'admin' and return
359 redirect_to :action => 'admin' and return
360 elsif user.login == 'root'
360 elsif user.login == 'root'
361 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
361 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
362 redirect_to :action => 'admin' and return
362 redirect_to :action => 'admin' and return
363 end
363 end
364
364
365 admin_role = Role.find_by_name('admin')
365 admin_role = Role.find_by_name('admin')
366 user.roles.delete(admin_role)
366 user.roles.delete(admin_role)
367 flash[:notice] = 'User permission revoked'
367 flash[:notice] = 'User permission revoked'
368 redirect_to :action => 'admin'
368 redirect_to :action => 'admin'
369 end
369 end
370
370
371 # mass mailing
371 # mass mailing
372
372
373 def mass_mailing
373 def mass_mailing
374 end
374 end
375
375
376 def bulk_mail
376 def bulk_mail
377 lines = params[:login_list]
377 lines = params[:login_list]
378 if !lines or lines.blank?
378 if !lines or lines.blank?
379 flash[:notice] = 'You entered an empty list.'
379 flash[:notice] = 'You entered an empty list.'
380 redirect_to :action => 'mass_mailing' and return
380 redirect_to :action => 'mass_mailing' and return
381 end
381 end
382
382
383 mail_subject = params[:subject]
383 mail_subject = params[:subject]
384 if !mail_subject or mail_subject.blank?
384 if !mail_subject or mail_subject.blank?
385 flash[:notice] = 'You entered an empty mail subject.'
385 flash[:notice] = 'You entered an empty mail subject.'
386 redirect_to :action => 'mass_mailing' and return
386 redirect_to :action => 'mass_mailing' and return
387 end
387 end
388
388
389 mail_body = params[:email_body]
389 mail_body = params[:email_body]
390 if !mail_body or mail_body.blank?
390 if !mail_body or mail_body.blank?
391 flash[:notice] = 'You entered an empty mail body.'
391 flash[:notice] = 'You entered an empty mail body.'
392 redirect_to :action => 'mass_mailing' and return
392 redirect_to :action => 'mass_mailing' and return
393 end
393 end
394
394
395 note = []
395 note = []
396 users = []
396 users = []
397 lines.split("\n").each do |line|
397 lines.split("\n").each do |line|
398 user = User.find_by_login(line.chomp)
398 user = User.find_by_login(line.chomp)
399 if user
399 if user
400 send_mail(user.email, mail_subject, mail_body)
400 send_mail(user.email, mail_subject, mail_body)
401 note << user.login
401 note << user.login
402 end
402 end
403 end
403 end
404
404
405 flash[:notice] = 'User(s) ' + note.join(', ') +
405 flash[:notice] = 'User(s) ' + note.join(', ') +
406 ' were successfully modified. '
406 ' were successfully modified. '
407 redirect_to :action => 'mass_mailing'
407 redirect_to :action => 'mass_mailing'
408 end
408 end
409
409
410 + #bulk manage
411 + def bulk_manage
412 +
413 + begin
414 + @users = User.where('login REGEXP ?',params[:regex]) if params[:regex]
415 + @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised
416 + rescue Exception
417 + flash[:error] = 'Regular Expression is malformed'
418 + @users = nil
419 + end
420 +
421 + if params[:commit]
422 + @action = {}
423 + @action[:set_enable] = params[:enabled]
424 + @action[:enabled] = params[:enable] == "1"
425 + @action[:gen_password] = params[:gen_password]
426 + end
427 +
428 + if params[:commit] == "Perform"
429 + if @action[:set_enable]
430 + @users.update_all(enabled: @action[:enabled])
431 + end
432 + if @action[:gen_password]
433 + @users.each do |u|
434 + password = random_password
435 + u.password = password
436 + u.password_confirmation = password
437 + u.save
438 + end
439 + end
440 + end
441 + end
442 +
410 protected
443 protected
411
444
412 def random_password(length=5)
445 def random_password(length=5)
413 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
446 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
414 newpass = ""
447 newpass = ""
415 length.times { newpass << chars[rand(chars.size-1)] }
448 length.times { newpass << chars[rand(chars.size-1)] }
416 return newpass
449 return newpass
417 end
450 end
418
451
419 def import_from_file(f)
452 def import_from_file(f)
420 data_hash = YAML.load(f)
453 data_hash = YAML.load(f)
421 @import_log = ""
454 @import_log = ""
422
455
423 country_data = data_hash[:countries]
456 country_data = data_hash[:countries]
424 site_data = data_hash[:sites]
457 site_data = data_hash[:sites]
425 user_data = data_hash[:users]
458 user_data = data_hash[:users]
426
459
427 # import country
460 # import country
428 countries = {}
461 countries = {}
429 country_data.each_pair do |id,country|
462 country_data.each_pair do |id,country|
430 c = Country.find_by_name(country[:name])
463 c = Country.find_by_name(country[:name])
431 if c!=nil
464 if c!=nil
432 countries[id] = c
465 countries[id] = c
433 @import_log << "Found #{country[:name]}\n"
466 @import_log << "Found #{country[:name]}\n"
434 else
467 else
435 countries[id] = Country.new(:name => country[:name])
468 countries[id] = Country.new(:name => country[:name])
436 countries[id].save
469 countries[id].save
437 @import_log << "Created #{country[:name]}\n"
470 @import_log << "Created #{country[:name]}\n"
438 end
471 end
439 end
472 end
440
473
441 # import sites
474 # import sites
442 sites = {}
475 sites = {}
443 site_data.each_pair do |id,site|
476 site_data.each_pair do |id,site|
444 s = Site.find_by_name(site[:name])
477 s = Site.find_by_name(site[:name])
445 if s!=nil
478 if s!=nil
446 @import_log << "Found #{site[:name]}\n"
479 @import_log << "Found #{site[:name]}\n"
447 else
480 else
448 s = Site.new(:name => site[:name])
481 s = Site.new(:name => site[:name])
449 @import_log << "Created #{site[:name]}\n"
482 @import_log << "Created #{site[:name]}\n"
450 end
483 end
451 s.password = site[:password]
484 s.password = site[:password]
452 s.country = countries[site[:country_id]]
485 s.country = countries[site[:country_id]]
453 s.save
486 s.save
454 sites[id] = s
487 sites[id] = s
455 end
488 end
456
489
457 # import users
490 # import users
458 user_data.each_pair do |id,user|
491 user_data.each_pair do |id,user|
459 u = User.find_by_login(user[:login])
492 u = User.find_by_login(user[:login])
460 if u!=nil
493 if u!=nil
461 @import_log << "Found #{user[:login]}\n"
494 @import_log << "Found #{user[:login]}\n"
462 else
495 else
463 u = User.new(:login => user[:login])
496 u = User.new(:login => user[:login])
464 @import_log << "Created #{user[:login]}\n"
497 @import_log << "Created #{user[:login]}\n"
465 end
498 end
466 u.full_name = user[:name]
499 u.full_name = user[:name]
467 u.password = user[:password]
500 u.password = user[:password]
468 u.country = countries[user[:country_id]]
501 u.country = countries[user[:country_id]]
469 u.site = sites[user[:site_id]]
502 u.site = sites[user[:site_id]]
470 u.activated = true
503 u.activated = true
471 u.email = "empty-#{u.login}@none.com"
504 u.email = "empty-#{u.login}@none.com"
472 if not u.save
505 if not u.save
473 @import_log << "Errors\n"
506 @import_log << "Errors\n"
474 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
507 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
475 end
508 end
476 end
509 end
477
510
478 end
511 end
479
512
480 def logout_users(users)
513 def logout_users(users)
481 users.each do |user|
514 users.each do |user|
482 contest_stat = user.contest_stat(true)
515 contest_stat = user.contest_stat(true)
483 if contest_stat and !contest_stat.forced_logout
516 if contest_stat and !contest_stat.forced_logout
484 contest_stat.forced_logout = true
517 contest_stat.forced_logout = true
485 contest_stat.save
518 contest_stat.save
486 end
519 end
487 end
520 end
488 end
521 end
489
522
490 def send_contest_update_notification_email(user, contest)
523 def send_contest_update_notification_email(user, contest)
491 contest_title_name = GraderConfiguration['contest.name']
524 contest_title_name = GraderConfiguration['contest.name']
492 contest_name = contest.name
525 contest_name = contest.name
493 mail_subject = t('contest.notification.email_subject', {
526 mail_subject = t('contest.notification.email_subject', {
494 :contest_title_name => contest_title_name,
527 :contest_title_name => contest_title_name,
495 :contest_name => contest_name })
528 :contest_name => contest_name })
496 mail_body = t('contest.notification.email_body', {
529 mail_body = t('contest.notification.email_body', {
497 :full_name => user.full_name,
530 :full_name => user.full_name,
498 :contest_title_name => contest_title_name,
531 :contest_title_name => contest_title_name,
499 :contest_name => contest.name,
532 :contest_name => contest.name,
500 })
533 })
501
534
502 logger.info mail_body
535 logger.info mail_body
503 send_mail(user.email, mail_subject, mail_body)
536 send_mail(user.email, mail_subject, mail_body)
504 end
537 end
505
538
506 def find_contest_and_user_from_contest_id(id)
539 def find_contest_and_user_from_contest_id(id)
507 if id!='none'
540 if id!='none'
508 @contest = Contest.find(id)
541 @contest = Contest.find(id)
509 else
542 else
510 @contest = nil
543 @contest = nil
511 end
544 end
512 if @contest
545 if @contest
513 @users = @contest.users
546 @users = @contest.users
514 else
547 else
515 @users = User.find_users_with_no_contest
548 @users = User.find_users_with_no_contest
516 end
549 end
517 return [@contest, @users]
550 return [@contest, @users]
518 end
551 end
519
552
520 def gen_csv_from_scorearray(scorearray,problem)
553 def gen_csv_from_scorearray(scorearray,problem)
521 CSV.generate do |csv|
554 CSV.generate do |csv|
522 #add header
555 #add header
523 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
556 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
524 problem.each { |p| header << p.name }
557 problem.each { |p| header << p.name }
525 header += ['Total','Passed']
558 header += ['Total','Passed']
526 csv << header
559 csv << header
527 #add data
560 #add data
528 scorearray.each do |sc|
561 scorearray.each do |sc|
529 total = num_passed = 0
562 total = num_passed = 0
530 row = Array.new
563 row = Array.new
531 sc.each_index do |i|
564 sc.each_index do |i|
532 if i == 0
565 if i == 0
533 row << sc[i].login
566 row << sc[i].login
534 row << sc[i].full_name
567 row << sc[i].full_name
535 row << sc[i].activated
568 row << sc[i].activated
536 row << (sc[i].try(:contest_stat).try(:started_at).nil? ? 'no' : 'yes')
569 row << (sc[i].try(:contest_stat).try(:started_at).nil? ? 'no' : 'yes')
537 row << sc[i].contests.collect {|c| c.name}.join(', ')
570 row << sc[i].contests.collect {|c| c.name}.join(', ')
538 else
571 else
539 row << sc[i][0]
572 row << sc[i][0]
540 total += sc[i][0]
573 total += sc[i][0]
541 num_passed += 1 if sc[i][1]
574 num_passed += 1 if sc[i][1]
542 end
575 end
543 end
576 end
544 row << total
577 row << total
545 row << num_passed
578 row << num_passed
546 csv << row
579 csv << row
547 end
580 end
548 end
581 end
549 end
582 end
550
583
551 private
584 private
552 def user_params
585 def user_params
553 params.require(:user).permit(:login,:password,:password_confirmation,:email, :alias, :full_name,:remark)
586 params.require(:user).permit(:login,:password,:password_confirmation,:email, :alias, :full_name,:remark)
554 end
587 end
555 end
588 end
@@ -1,134 +1,136
1 class Problem < ActiveRecord::Base
1 class Problem < ActiveRecord::Base
2
2
3 belongs_to :description
3 belongs_to :description
4 has_and_belongs_to_many :contests, :uniq => true
4 has_and_belongs_to_many :contests, :uniq => true
5 has_many :test_pairs, :dependent => :delete_all
5 has_many :test_pairs, :dependent => :delete_all
6 has_many :testcases, :dependent => :destroy
6 has_many :testcases, :dependent => :destroy
7
7
8 validates_presence_of :name
8 validates_presence_of :name
9 validates_format_of :name, :with => /\A\w+\z/
9 validates_format_of :name, :with => /\A\w+\z/
10 validates_presence_of :full_name
10 validates_presence_of :full_name
11
11
12 scope :available, -> { where(available: true) }
12 scope :available, -> { where(available: true) }
13
13
14 DEFAULT_TIME_LIMIT = 1
14 DEFAULT_TIME_LIMIT = 1
15 DEFAULT_MEMORY_LIMIT = 32
15 DEFAULT_MEMORY_LIMIT = 32
16
16
17 def self.available_problems
17 def self.available_problems
18 available.order(date_added: :desc).order(:name)
18 available.order(date_added: :desc).order(:name)
19 #Problem.available.all(:order => "date_added DESC, name ASC")
19 #Problem.available.all(:order => "date_added DESC, name ASC")
20 end
20 end
21
21
22 def self.create_from_import_form_params(params, old_problem=nil)
22 def self.create_from_import_form_params(params, old_problem=nil)
23 org_problem = old_problem || Problem.new
23 org_problem = old_problem || Problem.new
24 import_params, problem = Problem.extract_params_and_check(params,
24 import_params, problem = Problem.extract_params_and_check(params,
25 org_problem)
25 org_problem)
26
26
27 if !problem.errors.empty?
27 if !problem.errors.empty?
28 return problem, 'Error importing'
28 return problem, 'Error importing'
29 end
29 end
30
30
31 problem.full_score = 100
31 problem.full_score = 100
32 problem.date_added = Time.new
32 problem.date_added = Time.new
33 problem.test_allowed = true
33 problem.test_allowed = true
34 problem.output_only = false
34 problem.output_only = false
35 problem.available = false
35 problem.available = false
36
36
37 if not problem.save
37 if not problem.save
38 return problem, 'Error importing'
38 return problem, 'Error importing'
39 end
39 end
40
40
41 import_to_db = params.has_key? :import_to_db
41 import_to_db = params.has_key? :import_to_db
42
42
43 importer = TestdataImporter.new(problem)
43 importer = TestdataImporter.new(problem)
44
44
45 if not importer.import_from_file(import_params[:file],
45 if not importer.import_from_file(import_params[:file],
46 import_params[:time_limit],
46 import_params[:time_limit],
47 import_params[:memory_limit],
47 import_params[:memory_limit],
48 import_params[:checker_name],
48 import_params[:checker_name],
49 import_to_db)
49 import_to_db)
50 problem.errors.add(:base,'Import error.')
50 problem.errors.add(:base,'Import error.')
51 end
51 end
52
52
53 return problem, importer.log_msg
53 return problem, importer.log_msg
54 end
54 end
55
55
56 def self.download_file_basedir
56 def self.download_file_basedir
57 return "#{Rails.root}/data/tasks"
57 return "#{Rails.root}/data/tasks"
58 end
58 end
59
59
60 def get_submission_stat
60 def get_submission_stat
61 result = Hash.new
61 result = Hash.new
62 #total number of submission
62 #total number of submission
63 result[:total_sub] = Submission.where(problem_id: self.id).count
63 result[:total_sub] = Submission.where(problem_id: self.id).count
64 - result[:attempted_user] = Submission.where(problem_id: self.id).group_by(:user_id)
64 + result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
65 + result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
66 + return result
65 end
67 end
66
68
67 def long_name
69 def long_name
68 "[#{name}] #{full_name}"
70 "[#{name}] #{full_name}"
69 end
71 end
70
72
71 protected
73 protected
72
74
73 def self.to_i_or_default(st, default)
75 def self.to_i_or_default(st, default)
74 if st!=''
76 if st!=''
75 result = st.to_i
77 result = st.to_i
76 end
78 end
77 result ||= default
79 result ||= default
78 end
80 end
79
81
80 def self.to_f_or_default(st, default)
82 def self.to_f_or_default(st, default)
81 if st!=''
83 if st!=''
82 result = st.to_f
84 result = st.to_f
83 end
85 end
84 result ||= default
86 result ||= default
85 end
87 end
86
88
87 def self.extract_params_and_check(params, problem)
89 def self.extract_params_and_check(params, problem)
88 time_limit = Problem.to_f_or_default(params[:time_limit],
90 time_limit = Problem.to_f_or_default(params[:time_limit],
89 DEFAULT_TIME_LIMIT)
91 DEFAULT_TIME_LIMIT)
90 memory_limit = Problem.to_i_or_default(params[:memory_limit],
92 memory_limit = Problem.to_i_or_default(params[:memory_limit],
91 DEFAULT_MEMORY_LIMIT)
93 DEFAULT_MEMORY_LIMIT)
92
94
93 if time_limit<=0 or time_limit >60
95 if time_limit<=0 or time_limit >60
94 problem.errors.add(:base,'Time limit out of range.')
96 problem.errors.add(:base,'Time limit out of range.')
95 end
97 end
96
98
97 if memory_limit==0 and params[:memory_limit]!='0'
99 if memory_limit==0 and params[:memory_limit]!='0'
98 problem.errors.add(:base,'Memory limit format errors.')
100 problem.errors.add(:base,'Memory limit format errors.')
99 elsif memory_limit<=0 or memory_limit >512
101 elsif memory_limit<=0 or memory_limit >512
100 problem.errors.add(:base,'Memory limit out of range.')
102 problem.errors.add(:base,'Memory limit out of range.')
101 end
103 end
102
104
103 if params[:file]==nil or params[:file]==''
105 if params[:file]==nil or params[:file]==''
104 problem.errors.add(:base,'No testdata file.')
106 problem.errors.add(:base,'No testdata file.')
105 end
107 end
106
108
107 checker_name = 'text'
109 checker_name = 'text'
108 if ['text','float'].include? params[:checker]
110 if ['text','float'].include? params[:checker]
109 checker_name = params[:checker]
111 checker_name = params[:checker]
110 end
112 end
111
113
112 file = params[:file]
114 file = params[:file]
113
115
114 if !problem.errors.empty?
116 if !problem.errors.empty?
115 return nil, problem
117 return nil, problem
116 end
118 end
117
119
118 problem.name = params[:name]
120 problem.name = params[:name]
119 if params[:full_name]!=''
121 if params[:full_name]!=''
120 problem.full_name = params[:full_name]
122 problem.full_name = params[:full_name]
121 else
123 else
122 problem.full_name = params[:name]
124 problem.full_name = params[:name]
123 end
125 end
124
126
125 return [{
127 return [{
126 :time_limit => time_limit,
128 :time_limit => time_limit,
127 :memory_limit => memory_limit,
129 :memory_limit => memory_limit,
128 :file => file,
130 :file => file,
129 :checker_name => checker_name
131 :checker_name => checker_name
130 },
132 },
131 problem]
133 problem]
132 end
134 end
133
135
134 end
136 end
@@ -1,18 +1,22
1 %tr
1 %tr
2 %td
2 %td
3 + - if @current_user and @current_user.admin?
4 + = link_to problem.name, stat_problem_path(problem)
5 + - else
3 = "#{problem.name}"
6 = "#{problem.name}"
4 %td
7 %td
5 = "#{problem.full_name}"
8 = "#{problem.full_name}"
9 +
6 %br
10 %br
7 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
11 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
8 %td
12 %td
9 = @prob_submissions[problem.id][:count]
13 = @prob_submissions[problem.id][:count]
10 = link_to "[subs]", main_submission_path(problem.id)
14 = link_to "[subs]", main_submission_path(problem.id)
11 %td
15 %td
12 = render :partial => 'submission_short',
16 = render :partial => 'submission_short',
13 :locals => {:submission => @prob_submissions[problem.id][:submission], :problem_name => problem.name, :problem_id => problem.id }
17 :locals => {:submission => @prob_submissions[problem.id][:submission], :problem_name => problem.name, :problem_id => problem.id }
14 %td
18 %td
15 - if @prob_submissions[problem.id][:submission]
19 - if @prob_submissions[problem.id][:submission]
16 = link_to 'Edit', edit_submission_path(@prob_submissions[problem.id][:submission]), class: 'btn btn-success'
20 = link_to 'Edit', edit_submission_path(@prob_submissions[problem.id][:submission]), class: 'btn btn-success'
17 - else
21 - else
18 = link_to 'New', direct_edit_problem_submissions_path(problem.id), class: 'btn btn-success'
22 = link_to 'New', direct_edit_problem_submissions_path(problem.id), class: 'btn btn-success'
@@ -1,100 +1,101
1 %h1 Listing users
1 %h1 Listing users
2
2
3 .panel.panel-primary
3 .panel.panel-primary
4 .panel-title.panel-heading
4 .panel-title.panel-heading
5 Quick Add
5 Quick Add
6 .panel-body
6 .panel-body
7 = form_tag( {method: 'post'}, {class: 'form-inline'}) do
7 = form_tag( {method: 'post'}, {class: 'form-inline'}) do
8 .form-group
8 .form-group
9 = label_tag 'user_login', 'Login'
9 = label_tag 'user_login', 'Login'
10 = text_field 'user', 'login', :size => 10,class: 'form-control'
10 = text_field 'user', 'login', :size => 10,class: 'form-control'
11 .form-group
11 .form-group
12 = label_tag 'user_full_name', 'Full Name'
12 = label_tag 'user_full_name', 'Full Name'
13 = text_field 'user', 'full_name', :size => 10,class: 'form-control'
13 = text_field 'user', 'full_name', :size => 10,class: 'form-control'
14 .form-group
14 .form-group
15 = label_tag 'user_password', 'Password'
15 = label_tag 'user_password', 'Password'
16 = text_field 'user', 'password', :size => 10,class: 'form-control'
16 = text_field 'user', 'password', :size => 10,class: 'form-control'
17 .form-group
17 .form-group
18 = label_tag 'user_password_confirmation', 'Confirm'
18 = label_tag 'user_password_confirmation', 'Confirm'
19 = text_field 'user', 'password_confirmation', :size => 10,class: 'form-control'
19 = text_field 'user', 'password_confirmation', :size => 10,class: 'form-control'
20 .form-group
20 .form-group
21 = label_tag 'user_email', 'email'
21 = label_tag 'user_email', 'email'
22 = text_field 'user', 'email', :size => 10,class: 'form-control'
22 = text_field 'user', 'email', :size => 10,class: 'form-control'
23 =submit_tag "Create", class: 'btn btn-primary'
23 =submit_tag "Create", class: 'btn btn-primary'
24
24
25 .panel.panel-primary
25 .panel.panel-primary
26 .panel-title.panel-heading
26 .panel-title.panel-heading
27 Import from site management
27 Import from site management
28 .panel-body
28 .panel-body
29 = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do
29 = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do
30 .form-group
30 .form-group
31 = label_tag :file, 'File:'
31 = label_tag :file, 'File:'
32 .input-group
32 .input-group
33 %span.input-group-btn
33 %span.input-group-btn
34 %span.btn.btn-default.btn-file
34 %span.btn.btn-default.btn-file
35 Browse
35 Browse
36 = file_field_tag 'file'
36 = file_field_tag 'file'
37 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
37 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
38 = submit_tag 'Submit', class: 'btn btn-default'
38 = submit_tag 'Submit', class: 'btn btn-default'
39
39
40
40
41 %p
41 %p
42 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
42 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
43 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
43 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
44 = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '}
44 = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '}
45 + = link_to 'Bulk Manage', bulk_manage_user_admin_path , { class: 'btn btn-default '}
45 = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '}
46 = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '}
46 = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '}
47 = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '}
47 = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '}
48 = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '}
48
49
49 - if GraderConfiguration.multicontests?
50 - if GraderConfiguration.multicontests?
50 %br/
51 %br/
51 %b Multi-contest:
52 %b Multi-contest:
52 = link_to '[Manage bulk users in contests]', :action => 'contest_management'
53 = link_to '[Manage bulk users in contests]', :action => 'contest_management'
53 View users in:
54 View users in:
54 - @contests.each do |contest|
55 - @contests.each do |contest|
55 = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
56 = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
56 = link_to "[no contest]", :action => 'contests', :id => 'none'
57 = link_to "[no contest]", :action => 'contests', :id => 'none'
57
58
58 Total #{@user_count} users |
59 Total #{@user_count} users |
59 - if !@paginated
60 - if !@paginated
60 Display all users.
61 Display all users.
61 \#{link_to '[show in pages]', :action => 'index', :page => '1'}
62 \#{link_to '[show in pages]', :action => 'index', :page => '1'}
62 - else
63 - else
63 Display in pages.
64 Display in pages.
64 \#{link_to '[display all]', :action => 'index', :page => 'all'} |
65 \#{link_to '[display all]', :action => 'index', :page => 'all'} |
65 \#{will_paginate @users, :container => false}
66 \#{will_paginate @users, :container => false}
66
67
67
68
68 %table.table.table-hover.table-condense
69 %table.table.table-hover.table-condense
69 %thead
70 %thead
70 %th Login
71 %th Login
71 %th Full name
72 %th Full name
72 %th email
73 %th email
73 %th Remark
74 %th Remark
74 %th
75 %th
75 Activated
76 Activated
76 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?]
77 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?]
77 %th
78 %th
78 Enabled
79 Enabled
79 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
80 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
80 %th Last IP
81 %th Last IP
81 %th
82 %th
82 %th
83 %th
83 %th
84 %th
84 %th
85 %th
85 - for user in @users
86 - for user in @users
86 %tr
87 %tr
87 %td= link_to user.login, stat_user_path(user)
88 %td= link_to user.login, stat_user_path(user)
88 %td= user.full_name
89 %td= user.full_name
89 %td= user.email
90 %td= user.email
90 %td= user.remark
91 %td= user.remark
91 %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}")
92 %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}")
92 %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}")
93 %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}")
93 %td= user.last_ip
94 %td= user.last_ip
94 %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block'
95 %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block'
95 %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
96 %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
96 %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
97 %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
97 %td= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
98 %td= link_to 'Destroy', { :action => 'destroy', :id => user }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
98 %br/
99 %br/
99 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
100 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
100 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
101 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
@@ -1,8 +1,9
1 <h1>Adding list of users</h1>
1 <h1>Adding list of users</h1>
2
2
3 <%= form_tag :action => 'create_from_list' do %>
3 <%= form_tag :action => 'create_from_list' do %>
4 - <%= submit_tag 'create users' %><br/>
4 + <%= submit_tag 'create users',class: 'btn btn-success'%><br/>
5 - List of user information in this format: <tt>user_id,name(,passwd(,alias))</tt><br/>
5 + List of user information in this format: <tt>user_id,name(,passwd(,alias(,remark)))</tt><br/>
6 - Note that <tt>passwd</tt> and <tt>alias</tt> is optional.<br/>
6 + Note that <tt>passwd, alias</tt> and <tt> remark </tt>is optional.<br />
7 + When <tt>passwd</tt> or <tt>alias</tt> is empty, the original value will be used instead.<br/>
7 <%= text_area_tag 'user_list', nil, :rows => 50, :cols => 80 %>
8 <%= text_area_tag 'user_list', nil, :rows => 50, :cols => 80 %>
8 <% end %>
9 <% end %>
@@ -1,92 +1,99
1 CafeGrader::Application.routes.draw do
1 CafeGrader::Application.routes.draw do
2 get "sources/direct_edit"
2 get "sources/direct_edit"
3
3
4 root :to => 'main#login'
4 root :to => 'main#login'
5
5
6 #logins
6 #logins
7 get 'login/login', to: 'login#login'
7 get 'login/login', to: 'login#login'
8
8
9 resources :contests
9 resources :contests
10
10
11 resources :sites
11 resources :sites
12
12
13 resources :announcements do
13 resources :announcements do
14 member do
14 member do
15 get 'toggle','toggle_front'
15 get 'toggle','toggle_front'
16 end
16 end
17 end
17 end
18
18
19 resources :problems do
19 resources :problems do
20 member do
20 member do
21 get 'toggle'
21 get 'toggle'
22 get 'toggle_test'
22 get 'toggle_test'
23 get 'toggle_view_testcase'
23 get 'toggle_view_testcase'
24 get 'stat'
24 get 'stat'
25 end
25 end
26 collection do
26 collection do
27 get 'turn_all_off'
27 get 'turn_all_off'
28 get 'turn_all_on'
28 get 'turn_all_on'
29 get 'import'
29 get 'import'
30 get 'manage'
30 get 'manage'
31 end
31 end
32
32
33 end
33 end
34
34
35 resources :testcases, only: [] do
35 resources :testcases, only: [] do
36 member do
36 member do
37 get 'download_input'
37 get 'download_input'
38 get 'download_sol'
38 get 'download_sol'
39 end
39 end
40 collection do
40 collection do
41 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
41 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
42 end
42 end
43 end
43 end
44
44
45 resources :grader_configuration, controller: 'configurations'
45 resources :grader_configuration, controller: 'configurations'
46
46
47 resources :users do
47 resources :users do
48 member do
48 member do
49 get 'toggle_activate', 'toggle_enable'
49 get 'toggle_activate', 'toggle_enable'
50 get 'stat'
50 get 'stat'
51 end
51 end
52 end
52 end
53
53
54 resources :submissions do
54 resources :submissions do
55 member do
55 member do
56 get 'download'
56 get 'download'
57 get 'compiler_msg'
57 get 'compiler_msg'
58 end
58 end
59 collection do
59 collection do
60 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
60 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
61 get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
61 get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
62 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
62 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
63 end
63 end
64 end
64 end
65
65
66 - get 'tasks/view/:file.:ext' => 'tasks#view'
66 +
67 - get 'tasks/download/:id/:file.:ext' => 'tasks#download'
68 - get 'heartbeat/:id/edit' => 'heartbeat#edit'
69
67
70 #main
68 #main
71 get "main/list"
69 get "main/list"
72 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
70 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
73
71
72 + #user admin
73 + get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
74 +
74 #report
75 #report
75 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
76 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
76 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
77 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
77 get "report/login"
78 get "report/login"
78 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
79 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
79 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
80 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
80
81
82 +
83 + #
84 + get 'tasks/view/:file.:ext' => 'tasks#view'
85 + get 'tasks/download/:id/:file.:ext' => 'tasks#download'
86 + get 'heartbeat/:id/edit' => 'heartbeat#edit'
87 +
81 #grader
88 #grader
82 get 'graders/list', to: 'graders#list', as: 'grader_list'
89 get 'graders/list', to: 'graders#list', as: 'grader_list'
83
90
84
91
85 get 'heartbeat/:id/edit' => 'heartbeat#edit'
92 get 'heartbeat/:id/edit' => 'heartbeat#edit'
86
93
87 # See how all your routes lay out with "rake routes"
94 # See how all your routes lay out with "rake routes"
88
95
89 # This is a legacy wild controller route that's not recommended for RESTful applications.
96 # This is a legacy wild controller route that's not recommended for RESTful applications.
90 # Note: This route will make all actions in every controller accessible via GET requests.
97 # Note: This route will make all actions in every controller accessible via GET requests.
91 match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
98 match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
92 end
99 end
You need to be logged in to leave comments. Login now