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

r672:d41325ea3a15 - - 20 files changed: 364 inserted, 9 deleted

@@ -0,0 +1,86
1 + class GroupsController < ApplicationController
2 + before_action :set_group, only: [:show, :edit, :update, :destroy,
3 + :add_user, :remove_user,
4 + :add_problem, :remove_problem,
5 + ]
6 + before_action :authenticate, :admin_authorization
7 +
8 + # GET /groups
9 + def index
10 + @groups = Group.all
11 + end
12 +
13 + # GET /groups/1
14 + def show
15 + end
16 +
17 + # GET /groups/new
18 + def new
19 + @group = Group.new
20 + end
21 +
22 + # GET /groups/1/edit
23 + def edit
24 + end
25 +
26 + # POST /groups
27 + def create
28 + @group = Group.new(group_params)
29 +
30 + if @group.save
31 + redirect_to @group, notice: 'Group was successfully created.'
32 + else
33 + render :new
34 + end
35 + end
36 +
37 + # PATCH/PUT /groups/1
38 + def update
39 + if @group.update(group_params)
40 + redirect_to @group, notice: 'Group was successfully updated.'
41 + else
42 + render :edit
43 + end
44 + end
45 +
46 + # DELETE /groups/1
47 + def destroy
48 + @group.destroy
49 + redirect_to groups_url, notice: 'Group was successfully destroyed.'
50 + end
51 +
52 + def remove_user
53 + user = User.find(params[:user_id])
54 + @group.users.delete(user)
55 + redirect_to group_path(@group), notice: "User #{user.login} was removed from the group #{@group.name}"
56 + end
57 +
58 + def add_user
59 + user = User.find(params[:user_id])
60 + @group.users << user
61 + redirect_to group_path(@group), notice: "User #{user.login} was add to the group #{@group.name}"
62 + end
63 +
64 + def remove_problem
65 + problem = Problem.find(params[:problem_id])
66 + @group.problems.delete(problem)
67 + redirect_to group_path(@group), notice: "Problem #{problem.name} was removed from the group #{@group.name}"
68 + end
69 +
70 + def add_problem
71 + problem = Problem.find(params[:problem_id])
72 + @group.problems << problem
73 + redirect_to group_path(@group), notice: "Problem #{problem.name} was add to the group #{@group.name}"
74 + end
75 +
76 + private
77 + # Use callbacks to share common setup or constraints between actions.
78 + def set_group
79 + @group = Group.find(params[:id])
80 + end
81 +
82 + # Only allow a trusted parameter "white list" through.
83 + def group_params
84 + params.require(:group).permit(:name, :description)
85 + end
86 + end
@@ -0,0 +1,2
1 + module GroupsHelper
2 + end
@@ -0,0 +1,5
1 + class Group < ActiveRecord::Base
2 + has_and_belongs_to_many :problems
3 + has_and_belongs_to_many :users
4 + end
5 +
@@ -0,0 +1,16
1 + = form_for @group do |f|
2 + - if @group.errors.any?
3 + #error_explanation
4 + %h2= "#{pluralize(@group.errors.count, "error")} prohibited this group from being saved:"
5 + %ul
6 + - @group.errors.full_messages.each do |msg|
7 + %li= msg
8 +
9 + .form-group.field
10 + = f.label :name
11 + = f.text_field :name, class: 'form-control'
12 + .form-group.field
13 + = f.label :description
14 + = f.text_field :description, class: 'form-control'
15 + .form-group.actions
16 + = f.submit 'Save', class: 'btn btn-primary'
@@ -0,0 +1,7
1 + %h1 Editing group
2 +
3 + = render 'form'
4 +
5 + = link_to 'Show', @group
6 + \|
7 + = link_to 'Back', groups_path
@@ -0,0 +1,24
1 + %h1 Groups
2 +
3 + %p
4 + = link_to 'New Group', new_group_path, class: 'btn btn-primary'
5 + %table.table.table-hover
6 + %thead
7 + %tr
8 + %th Name
9 + %th Description
10 + %th
11 + %th
12 + %th
13 +
14 + %tbody
15 + - @groups.each do |group|
16 + %tr
17 + %td= group.name
18 + %td= group.description
19 + %td= link_to 'Show', group, class: 'btn btn-default'
20 + %td= link_to 'Edit', edit_group_path(group), class: 'btn btn-default'
21 + %td= link_to 'Destroy', group, :method => :delete, :data => { :confirm => 'Are you sure?' }, class: 'btn btn-danger'
22 +
23 + %br
24 +
@@ -0,0 +1,5
1 + %h1 New group
2 +
3 + = render 'form'
4 +
5 + = link_to 'Back', groups_path
@@ -0,0 +1,67
1 + %p#notice= notice
2 +
3 + %p
4 + %b Name:
5 + = @group.name
6 + %p
7 + %b Description:
8 + = @group.description
9 +
10 + %br
11 + = link_to 'Edit', edit_group_path(@group)
12 + \|
13 + = link_to 'Back', groups_path
14 +
15 + .row
16 + .col-md-6
17 + %h1 Users in this group
18 +
19 + =form_tag add_user_group_path(@group), class: 'form-inline' do
20 + .form-group
21 + =label_tag :user_id, "User"
22 + =select_tag :user_id, options_from_collection_for_select(User.all,'id','full_name'), class: 'select2'
23 + =submit_tag "Add",class: 'btn btn-primary'
24 +
25 +
26 + %table.table.table-hover
27 + %thead
28 + %tr
29 + %th Login
30 + %th Full name
31 + %th Remark
32 + %th
33 +
34 + %tbody
35 + - @group.users.each do |user|
36 + %tr
37 + %td= user.login
38 + %td= user.full_name
39 + %td= user.remark
40 + %td= link_to 'Remove', remove_user_group_path(@group,user), :method => :delete, :data => { :confirm => "Remove #{user.full_name}?" }, class: 'btn btn-danger'
41 + .col-md-6
42 + %h1 Problems in this group
43 +
44 + =form_tag add_problem_group_path(@group), class: 'form-inline' do
45 + .form-group
46 + =label_tag :problem_id, "Problem"
47 + =select_tag :problem_id, options_from_collection_for_select(Problem.all,'id','full_name'), class: 'select2'
48 + =submit_tag "Add",class: 'btn btn-primary'
49 +
50 +
51 + %table.table.table-hover
52 + %thead
53 + %tr
54 + %th name
55 + %th Full name
56 + %th Full score
57 + %th
58 +
59 + %tbody
60 + - @group.problems.each do |problem|
61 + %tr
62 + %td= problem.name
63 + %td= problem.full_name
64 + %td= problem.full_score
65 + %td= link_to 'Remove', remove_problem_group_path(@group,problem), :method => :delete, :data => { :confirm => "Remove #{problem.full_name}?" }, class: 'btn btn-danger'
66 +
67 +
@@ -0,0 +1,30
1 + class CreateGroups < ActiveRecord::Migration
2 +
3 + def change
4 + create_table :groups do |t|
5 + t.string :name
6 + t.string :description
7 + end
8 +
9 + create_join_table :groups, :users do |t|
10 + # t.index [:group_id, :user_id]
11 + t.index [:user_id, :group_id]
12 + end
13 +
14 + create_join_table :problems, :groups do |t|
15 + # t.index [:problem_id, :group_id]
16 + t.index [:group_id, :problem_id]
17 + end
18 +
19 + reversible do |change|
20 + change.up do
21 + GraderConfiguration.where(key: 'system.use_problem_group').first_or_create(value_type: 'boolean', value: 'false',
22 + description: 'If true, available problem to the user will be only ones associated with the group of the user');
23 + end
24 +
25 + change.down do
26 + GraderConfiguration.where(key: 'system.use_problem_group').destroy_all
27 + end
28 + end
29 + end
30 + end
@@ -0,0 +1,49
1 + require 'test_helper'
2 +
3 + class GroupsControllerTest < ActionController::TestCase
4 + setup do
5 + @group = groups(:one)
6 + end
7 +
8 + test "should get index" do
9 + get :index
10 + assert_response :success
11 + assert_not_nil assigns(:groups)
12 + end
13 +
14 + test "should get new" do
15 + get :new
16 + assert_response :success
17 + end
18 +
19 + test "should create group" do
20 + assert_difference('Group.count') do
21 + post :create, group: { description: @group.description, name: @group.name }
22 + end
23 +
24 + assert_redirected_to group_path(assigns(:group))
25 + end
26 +
27 + test "should show group" do
28 + get :show, id: @group
29 + assert_response :success
30 + end
31 +
32 + test "should get edit" do
33 + get :edit, id: @group
34 + assert_response :success
35 + end
36 +
37 + test "should update group" do
38 + patch :update, id: @group, group: { description: @group.description, name: @group.name }
39 + assert_redirected_to group_path(assigns(:group))
40 + end
41 +
42 + test "should destroy group" do
43 + assert_difference('Group.count', -1) do
44 + delete :destroy, id: @group
45 + end
46 +
47 + assert_redirected_to groups_path
48 + end
49 + end
@@ -102,192 +102,197
102 102 File.delete out_filename
103 103 end
104 104
105 105 File.open(out_filename,"wb") do |file|
106 106 file.write(params[:file].read)
107 107 end
108 108 @problem.description_filename = "#{@problem.name}.pdf"
109 109 @problem.save
110 110 end
111 111 redirect_to :action => 'show', :id => @problem
112 112 else
113 113 render :action => 'edit'
114 114 end
115 115 end
116 116
117 117 def destroy
118 118 p = Problem.find(params[:id]).destroy
119 119 redirect_to action: :index
120 120 end
121 121
122 122 def toggle
123 123 @problem = Problem.find(params[:id])
124 124 @problem.update_attributes(available: !(@problem.available) )
125 125 respond_to do |format|
126 126 format.js { }
127 127 end
128 128 end
129 129
130 130 def toggle_test
131 131 @problem = Problem.find(params[:id])
132 132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
133 133 respond_to do |format|
134 134 format.js { }
135 135 end
136 136 end
137 137
138 138 def toggle_view_testcase
139 139 @problem = Problem.find(params[:id])
140 140 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
141 141 respond_to do |format|
142 142 format.js { }
143 143 end
144 144 end
145 145
146 146 def turn_all_off
147 147 Problem.available.all.each do |problem|
148 148 problem.available = false
149 149 problem.save
150 150 end
151 151 redirect_to action: :index
152 152 end
153 153
154 154 def turn_all_on
155 155 Problem.where.not(available: true).each do |problem|
156 156 problem.available = true
157 157 problem.save
158 158 end
159 159 redirect_to action: :index
160 160 end
161 161
162 162 def stat
163 163 @problem = Problem.find(params[:id])
164 164 unless @problem.available or session[:admin]
165 165 redirect_to :controller => 'main', :action => 'list'
166 166 return
167 167 end
168 168 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
169 169
170 170 #stat summary
171 171 range =65
172 172 @histogram = { data: Array.new(range,0), summary: {} }
173 173 user = Hash.new(0)
174 174 @submissions.find_each do |sub|
175 175 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
176 176 @histogram[:data][d.to_i] += 1 if d < range
177 177 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
178 178 end
179 179 @histogram[:summary][:max] = [@histogram[:data].max,1].max
180 180
181 181 @summary = { attempt: user.count, solve: 0 }
182 182 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
183 183 end
184 184
185 185 def manage
186 186 @problems = Problem.order(date_added: :desc)
187 187 end
188 188
189 189 def do_manage
190 190 if params.has_key? 'change_date_added'
191 191 change_date_added
192 192 elsif params.has_key? 'add_to_contest'
193 193 add_to_contest
194 194 elsif params.has_key? 'enable_problem'
195 195 set_available(true)
196 196 elsif params.has_key? 'disable_problem'
197 197 set_available(false)
198 + elsif params.has_key? 'add_group'
199 + group = Group.find(params[:group_id])
200 + get_problems_from_params.each do |p|
201 + group.problems << p
202 + end
198 203 end
199 204 redirect_to :action => 'manage'
200 205 end
201 206
202 207 def import
203 208 @allow_test_pair_import = allow_test_pair_import?
204 209 end
205 210
206 211 def do_import
207 212 old_problem = Problem.find_by_name(params[:name])
208 213 if !allow_test_pair_import? and params.has_key? :import_to_db
209 214 params.delete :import_to_db
210 215 end
211 216 @problem, import_log = Problem.create_from_import_form_params(params,
212 217 old_problem)
213 218
214 219 if !@problem.errors.empty?
215 220 render :action => 'import' and return
216 221 end
217 222
218 223 if old_problem!=nil
219 224 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
220 225 end
221 226 @log = import_log
222 227 end
223 228
224 229 def remove_contest
225 230 problem = Problem.find(params[:id])
226 231 contest = Contest.find(params[:contest_id])
227 232 if problem!=nil and contest!=nil
228 233 problem.contests.delete(contest)
229 234 end
230 235 redirect_to :action => 'manage'
231 236 end
232 237
233 238 ##################################
234 239 protected
235 240
236 241 def allow_test_pair_import?
237 242 if defined? ALLOW_TEST_PAIR_IMPORT
238 243 return ALLOW_TEST_PAIR_IMPORT
239 244 else
240 245 return false
241 246 end
242 247 end
243 248
244 249 def change_date_added
245 250 problems = get_problems_from_params
246 251 year = params[:date_added][:year].to_i
247 252 month = params[:date_added][:month].to_i
248 253 day = params[:date_added][:day].to_i
249 254 date = Date.new(year,month,day)
250 255 problems.each do |p|
251 256 p.date_added = date
252 257 p.save
253 258 end
254 259 end
255 260
256 261 def add_to_contest
257 262 problems = get_problems_from_params
258 263 contest = Contest.find(params[:contest][:id])
259 264 if contest!=nil and contest.enabled
260 265 problems.each do |p|
261 266 p.contests << contest
262 267 end
263 268 end
264 269 end
265 270
266 271 def set_available(avail)
267 272 problems = get_problems_from_params
268 273 problems.each do |p|
269 274 p.available = avail
270 275 p.save
271 276 end
272 277 end
273 278
274 279 def get_problems_from_params
275 280 problems = []
276 281 params.keys.each do |k|
277 282 if k.index('prob-')==0
278 283 name, id, order = k.split('-')
279 284 problems << Problem.find(id)
280 285 end
281 286 end
282 287 problems
283 288 end
284 289
285 290 def get_problems_stat
286 291 end
287 292
288 293 private
289 294
290 295 def problem_params
291 296 params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description)
292 297 end
293 298
@@ -347,196 +347,195
347 347 admin_role = Role.find_by_name('admin')
348 348 user.roles << admin_role
349 349 else
350 350 flash[:notice] = 'Unknown user'
351 351 end
352 352 flash[:notice] = 'User added as admins'
353 353 redirect_to :action => 'admin'
354 354 end
355 355
356 356 def revoke_admin
357 357 user = User.find(params[:id])
358 358 if user==nil
359 359 flash[:notice] = 'Unknown user'
360 360 redirect_to :action => 'admin' and return
361 361 elsif user.login == 'root'
362 362 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
363 363 redirect_to :action => 'admin' and return
364 364 end
365 365
366 366 admin_role = Role.find_by_name('admin')
367 367 user.roles.delete(admin_role)
368 368 flash[:notice] = 'User permission revoked'
369 369 redirect_to :action => 'admin'
370 370 end
371 371
372 372 # mass mailing
373 373
374 374 def mass_mailing
375 375 end
376 376
377 377 def bulk_mail
378 378 lines = params[:login_list]
379 379 if !lines or lines.blank?
380 380 flash[:notice] = 'You entered an empty list.'
381 381 redirect_to :action => 'mass_mailing' and return
382 382 end
383 383
384 384 mail_subject = params[:subject]
385 385 if !mail_subject or mail_subject.blank?
386 386 flash[:notice] = 'You entered an empty mail subject.'
387 387 redirect_to :action => 'mass_mailing' and return
388 388 end
389 389
390 390 mail_body = params[:email_body]
391 391 if !mail_body or mail_body.blank?
392 392 flash[:notice] = 'You entered an empty mail body.'
393 393 redirect_to :action => 'mass_mailing' and return
394 394 end
395 395
396 396 note = []
397 397 users = []
398 398 lines.split("\n").each do |line|
399 399 user = User.find_by_login(line.chomp)
400 400 if user
401 401 send_mail(user.email, mail_subject, mail_body)
402 402 note << user.login
403 403 end
404 404 end
405 405
406 406 flash[:notice] = 'User(s) ' + note.join(', ') +
407 407 ' were successfully modified. '
408 408 redirect_to :action => 'mass_mailing'
409 409 end
410 410
411 411 #bulk manage
412 412 def bulk_manage
413 413
414 414 begin
415 415 @users = User.where('login REGEXP ?',params[:regex]) if params[:regex]
416 416 @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised
417 417 rescue Exception
418 418 flash[:error] = 'Regular Expression is malformed'
419 419 @users = nil
420 420 end
421 421
422 422 if params[:commit]
423 423 @action = {}
424 424 @action[:set_enable] = params[:enabled]
425 425 @action[:enabled] = params[:enable] == "1"
426 426 @action[:gen_password] = params[:gen_password]
427 427 @action[:add_group] = params[:add_group]
428 428 @action[:group_name] = params[:group_name]
429 429 end
430 430
431 431 if params[:commit] == "Perform"
432 432 if @action[:set_enable]
433 433 @users.update_all(enabled: @action[:enabled])
434 434 end
435 435 if @action[:gen_password]
436 436 @users.each do |u|
437 437 password = random_password
438 438 u.password = password
439 439 u.password_confirmation = password
440 440 u.save
441 441 end
442 442 end
443 - if @action[:add_group]
444 - @uses.each do |u|
445 -
446 - end
443 + if @action[:add_group] and @action[:group_name]
444 + @group = Group.find(@action[:group_name])
445 + @users.each { |user| @group.users << user }
447 446 end
448 447 end
449 448 end
450 449
451 450 protected
452 451
453 452 def random_password(length=5)
454 453 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
455 454 newpass = ""
456 455 length.times { newpass << chars[rand(chars.size-1)] }
457 456 return newpass
458 457 end
459 458
460 459 def import_from_file(f)
461 460 data_hash = YAML.load(f)
462 461 @import_log = ""
463 462
464 463 country_data = data_hash[:countries]
465 464 site_data = data_hash[:sites]
466 465 user_data = data_hash[:users]
467 466
468 467 # import country
469 468 countries = {}
470 469 country_data.each_pair do |id,country|
471 470 c = Country.find_by_name(country[:name])
472 471 if c!=nil
473 472 countries[id] = c
474 473 @import_log << "Found #{country[:name]}\n"
475 474 else
476 475 countries[id] = Country.new(:name => country[:name])
477 476 countries[id].save
478 477 @import_log << "Created #{country[:name]}\n"
479 478 end
480 479 end
481 480
482 481 # import sites
483 482 sites = {}
484 483 site_data.each_pair do |id,site|
485 484 s = Site.find_by_name(site[:name])
486 485 if s!=nil
487 486 @import_log << "Found #{site[:name]}\n"
488 487 else
489 488 s = Site.new(:name => site[:name])
490 489 @import_log << "Created #{site[:name]}\n"
491 490 end
492 491 s.password = site[:password]
493 492 s.country = countries[site[:country_id]]
494 493 s.save
495 494 sites[id] = s
496 495 end
497 496
498 497 # import users
499 498 user_data.each_pair do |id,user|
500 499 u = User.find_by_login(user[:login])
501 500 if u!=nil
502 501 @import_log << "Found #{user[:login]}\n"
503 502 else
504 503 u = User.new(:login => user[:login])
505 504 @import_log << "Created #{user[:login]}\n"
506 505 end
507 506 u.full_name = user[:name]
508 507 u.password = user[:password]
509 508 u.country = countries[user[:country_id]]
510 509 u.site = sites[user[:site_id]]
511 510 u.activated = true
512 511 u.email = "empty-#{u.login}@none.com"
513 512 if not u.save
514 513 @import_log << "Errors\n"
515 514 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
516 515 end
517 516 end
518 517
519 518 end
520 519
521 520 def logout_users(users)
522 521 users.each do |user|
523 522 contest_stat = user.contest_stat(true)
524 523 if contest_stat and !contest_stat.forced_logout
525 524 contest_stat.forced_logout = true
526 525 contest_stat.save
527 526 end
528 527 end
529 528 end
530 529
531 530 def send_contest_update_notification_email(user, contest)
532 531 contest_title_name = GraderConfiguration['contest.name']
533 532 contest_name = contest.name
534 533 mail_subject = t('contest.notification.email_subject', {
535 534 :contest_title_name => contest_title_name,
536 535 :contest_name => contest_name })
537 536 mail_body = t('contest.notification.email_body', {
538 537 :full_name => user.full_name,
539 538 :contest_title_name => contest_title_name,
540 539 :contest_name => contest.name,
541 540 })
542 541
@@ -1,183 +1,188
1 1 require 'yaml'
2 2
3 3 #
4 4 # This class also contains various login of the system.
5 5 #
6 6 class GraderConfiguration < ActiveRecord::Base
7 7
8 8 SYSTEM_MODE_CONF_KEY = 'system.mode'
9 9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
10 10 MULTICONTESTS_KEY = 'system.multicontests'
11 11 CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
12 12 MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login'
13 13 VIEW_TESTCASE = 'right.view_testcase'
14 14 SINGLE_USER_KEY = 'system.single_user_mode'
15 + SYSTEM_USE_PROBLEM_GROUP = 'system.use_problem_group'
15 16
16 17 cattr_accessor :config_cache
17 18 cattr_accessor :task_grading_info_cache
18 19 cattr_accessor :contest_time_str
19 20 cattr_accessor :contest_time
20 21
21 22 GraderConfiguration.config_cache = nil
22 23 GraderConfiguration.task_grading_info_cache = nil
23 24
24 25 def self.config_cached?
25 26 (defined? CONFIGURATION_CACHE_ENABLED) and (CONFIGURATION_CACHE_ENABLED)
26 27 end
27 28
28 29 def self.get(key)
29 30 if GraderConfiguration.config_cached?
30 31 if GraderConfiguration.config_cache == nil
31 32 self.read_config
32 33 end
33 34 return GraderConfiguration.config_cache[key]
34 35 else
35 36 return GraderConfiguration.read_one_key(key)
36 37 end
37 38 end
38 39
39 40 def self.[](key)
40 41 self.get(key)
41 42 end
42 43
43 44 def self.reload
44 45 self.read_config
45 46 end
46 47
47 48 def self.clear
48 49 GraderConfiguration.config_cache = nil
49 50 end
50 51
51 52 #
52 53 # View decision
53 54 #
54 55 def self.show_submitbox_to?(user)
55 56 mode = get(SYSTEM_MODE_CONF_KEY)
56 57 return false if mode=='analysis'
57 58 if (mode=='contest')
58 59 return false if (user.site!=nil) and
59 60 ((user.site.started!=true) or (user.site.finished?))
60 61 end
61 62 return true
62 63 end
63 64
64 65 def self.show_tasks_to?(user)
65 66 if time_limit_mode?
66 67 return false if not user.contest_started?
67 68 end
68 69 return true
69 70 end
70 71
71 72 def self.show_grading_result
72 73 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
73 74 end
74 75
75 76 def self.show_testcase
76 77 return get(VIEW_TESTCASE)
77 78 end
78 79
79 80 def self.allow_test_request(user)
80 81 mode = get(SYSTEM_MODE_CONF_KEY)
81 82 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
82 83 if (mode=='contest')
83 84 return false if ((user.site!=nil) and
84 85 ((user.site.started!=true) or
85 86 (early_timeout and (user.site.time_left < 30.minutes))))
86 87 end
87 88 return false if mode=='analysis'
88 89 return true
89 90 end
90 91
91 92 def self.task_grading_info
92 93 if GraderConfiguration.task_grading_info_cache==nil
93 94 read_grading_info
94 95 end
95 96 return GraderConfiguration.task_grading_info_cache
96 97 end
97 98
98 99 def self.standard_mode?
99 100 return get(SYSTEM_MODE_CONF_KEY) == 'standard'
100 101 end
101 102
102 103 def self.contest_mode?
103 104 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
104 105 end
105 106
106 107 def self.indv_contest_mode?
107 108 return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
108 109 end
109 110
110 111 def self.multicontests?
111 112 return get(MULTICONTESTS_KEY) == true
112 113 end
113 114
114 115 def self.time_limit_mode?
115 116 mode = get(SYSTEM_MODE_CONF_KEY)
116 117 return ((mode == 'contest') or (mode == 'indv-contest'))
117 118 end
118 119
119 120 def self.analysis_mode?
120 121 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
121 122 end
122 123
124 + def self.use_problem_group?
125 + return get(SYSTEM_USE_PROBLEM_GROUP)
126 + end
127 +
123 128 def self.contest_time_limit
124 129 contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY]
125 130
126 131 if not defined? GraderConfiguration.contest_time_str
127 132 GraderConfiguration.contest_time_str = nil
128 133 end
129 134
130 135 if GraderConfiguration.contest_time_str != contest_time_str
131 136 GraderConfiguration.contest_time_str = contest_time_str
132 137 if tmatch = /(\d+):(\d+)/.match(contest_time_str)
133 138 h = tmatch[1].to_i
134 139 m = tmatch[2].to_i
135 140
136 141 GraderConfiguration.contest_time = h.hour + m.minute
137 142 else
138 143 GraderConfiguration.contest_time = nil
139 144 end
140 145 end
141 146 return GraderConfiguration.contest_time
142 147 end
143 148
144 149 protected
145 150
146 151 def self.convert_type(val,type)
147 152 case type
148 153 when 'string'
149 154 return val
150 155
151 156 when 'integer'
152 157 return val.to_i
153 158
154 159 when 'boolean'
155 160 return (val=='true')
156 161 end
157 162 end
158 163
159 164 def self.read_config
160 165 GraderConfiguration.config_cache = {}
161 166 GraderConfiguration.all.each do |conf|
162 167 key = conf.key
163 168 val = conf.value
164 169 GraderConfiguration.config_cache[key] = GraderConfiguration.convert_type(val,conf.value_type)
165 170 end
166 171 end
167 172
168 173 def self.read_one_key(key)
169 174 conf = GraderConfiguration.find_by_key(key)
170 175 if conf
171 176 return GraderConfiguration.convert_type(conf.value,conf.value_type)
172 177 else
173 178 return nil
174 179 end
175 180 end
176 181
177 182 def self.read_grading_info
178 183 f = File.open(TASK_GRADING_INFO_FILENAME)
179 184 GraderConfiguration.task_grading_info_cache = YAML.load(f)
180 185 f.close
181 186 end
182 187
183 188 end
@@ -1,100 +1,101
1 1 class Problem < ActiveRecord::Base
2 2
3 3 belongs_to :description
4 4 has_and_belongs_to_many :contests, :uniq => true
5 + has_and_belongs_to_many :groups
5 6 has_many :test_pairs, :dependent => :delete_all
6 7 has_many :testcases, :dependent => :destroy
7 8
8 9 validates_presence_of :name
9 10 validates_format_of :name, :with => /\A\w+\z/
10 11 validates_presence_of :full_name
11 12
12 13 scope :available, -> { where(available: true) }
13 14
14 15 DEFAULT_TIME_LIMIT = 1
15 16 DEFAULT_MEMORY_LIMIT = 32
16 17
17 18 def self.available_problems
18 19 available.order(date_added: :desc).order(:name)
19 20 #Problem.available.all(:order => "date_added DESC, name ASC")
20 21 end
21 22
22 23 def self.create_from_import_form_params(params, old_problem=nil)
23 24 org_problem = old_problem || Problem.new
24 25 import_params, problem = Problem.extract_params_and_check(params,
25 26 org_problem)
26 27
27 28 if !problem.errors.empty?
28 29 return problem, 'Error importing'
29 30 end
30 31
31 32 problem.full_score = 100
32 33 problem.date_added = Time.new
33 34 problem.test_allowed = true
34 35 problem.output_only = false
35 36 problem.available = false
36 37
37 38 if not problem.save
38 39 return problem, 'Error importing'
39 40 end
40 41
41 42 import_to_db = params.has_key? :import_to_db
42 43
43 44 importer = TestdataImporter.new(problem)
44 45
45 46 if not importer.import_from_file(import_params[:file],
46 47 import_params[:time_limit],
47 48 import_params[:memory_limit],
48 49 import_params[:checker_name],
49 50 import_to_db)
50 51 problem.errors.add(:base,'Import error.')
51 52 end
52 53
53 54 return problem, importer.log_msg
54 55 end
55 56
56 57 def self.download_file_basedir
57 58 return "#{Rails.root}/data/tasks"
58 59 end
59 60
60 61 def get_submission_stat
61 62 result = Hash.new
62 63 #total number of submission
63 64 result[:total_sub] = Submission.where(problem_id: self.id).count
64 65 result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
65 66 result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
66 67 return result
67 68 end
68 69
69 70 def long_name
70 71 "[#{name}] #{full_name}"
71 72 end
72 73
73 74 protected
74 75
75 76 def self.to_i_or_default(st, default)
76 77 if st!=''
77 78 result = st.to_i
78 79 end
79 80 result ||= default
80 81 end
81 82
82 83 def self.to_f_or_default(st, default)
83 84 if st!=''
84 85 result = st.to_f
85 86 end
86 87 result ||= default
87 88 end
88 89
89 90 def self.extract_params_and_check(params, problem)
90 91 time_limit = Problem.to_f_or_default(params[:time_limit],
91 92 DEFAULT_TIME_LIMIT)
92 93 memory_limit = Problem.to_i_or_default(params[:memory_limit],
93 94 DEFAULT_MEMORY_LIMIT)
94 95
95 96 if time_limit<=0 or time_limit >60
96 97 problem.errors.add(:base,'Time limit out of range.')
97 98 end
98 99
99 100 if memory_limit==0 and params[:memory_limit]!='0'
100 101 problem.errors.add(:base,'Memory limit format errors.')
@@ -1,105 +1,106
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 + has_and_belongs_to_many :groups
10 11
11 12 has_many :test_requests, -> {order(submitted_at: DESC)}
12 13
13 14 has_many :messages, -> { order(created_at: DESC) },
14 15 :class_name => "Message",
15 16 :foreign_key => "sender_id"
16 17
17 18 has_many :replied_messages, -> { order(created_at: DESC) },
18 19 :class_name => "Message",
19 20 :foreign_key => "receiver_id"
20 21
21 22 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
22 23
23 24 belongs_to :site
24 25 belongs_to :country
25 26
26 27 has_and_belongs_to_many :contests, -> { order(:name); uniq}
27 28
28 29 scope :activated_users, -> {where activated: true}
29 30
30 31 validates_presence_of :login
31 32 validates_uniqueness_of :login
32 33 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
33 34 validates_length_of :login, :within => 3..30
34 35
35 36 validates_presence_of :full_name
36 37 validates_length_of :full_name, :minimum => 1
37 38
38 39 validates_presence_of :password, :if => :password_required?
39 40 validates_length_of :password, :within => 4..20, :if => :password_required?
40 41 validates_confirmation_of :password, :if => :password_required?
41 42
42 43 validates_format_of :email,
43 44 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
44 45 :if => :email_validation?
45 46 validate :uniqueness_of_email_from_activated_users,
46 47 :if => :email_validation?
47 48 validate :enough_time_interval_between_same_email_registrations,
48 49 :if => :email_validation?
49 50
50 51 # these are for ytopc
51 52 # disable for now
52 53 #validates_presence_of :province
53 54
54 55 attr_accessor :password
55 56
56 57 before_save :encrypt_new_password
57 58 before_save :assign_default_site
58 59 before_save :assign_default_contest
59 60
60 61 # this is for will_paginate
61 62 cattr_reader :per_page
62 63 @@per_page = 50
63 64
64 65 def self.authenticate(login, password)
65 66 user = find_by_login(login)
66 67 if user
67 68 return user if user.authenticated?(password)
68 69 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
69 70 user.password = password
70 71 user.save
71 72 return user
72 73 end
73 74 end
74 75 end
75 76
76 77 def authenticated?(password)
77 78 if self.activated
78 79 hashed_password == User.encrypt(password,self.salt)
79 80 else
80 81 false
81 82 end
82 83 end
83 84
84 85 def authenticated_by_pop3?(password)
85 86 Net::POP3.enable_ssl
86 87 pop = Net::POP3.new('pops.it.chula.ac.th')
87 88 authen = true
88 89 begin
89 90 pop.start(login, password)
90 91 pop.finish
91 92 return true
92 93 rescue
93 94 return false
94 95 end
95 96 end
96 97
97 98 def authenticated_by_cucas?(password)
98 99 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
99 100 appid = '41508763e340d5858c00f8c1a0f5a2bb'
100 101 appsecret ='d9cbb5863091dbe186fded85722a1e31'
101 102 post_args = {
102 103 'appid' => appid,
103 104 'appsecret' => appsecret,
104 105 'username' => login,
105 106 'password' => password
@@ -197,192 +198,204
197 198 return nil
198 199 end
199 200 if contest_stat==nil or contest_stat.started_at==nil
200 201 return (Time.now.gmtime + time_limit) - Time.now.gmtime
201 202 else
202 203 finish_time = contest_stat.started_at + time_limit
203 204 current_time = Time.now.gmtime
204 205 if current_time > finish_time
205 206 return 0
206 207 else
207 208 return finish_time - current_time
208 209 end
209 210 end
210 211 else
211 212 return nil
212 213 end
213 214 end
214 215
215 216 def contest_finished?
216 217 if GraderConfiguration.contest_mode?
217 218 return false if site==nil
218 219 return site.finished?
219 220 elsif GraderConfiguration.indv_contest_mode?
220 221 return false if self.contest_stat(true)==nil
221 222 return contest_time_left == 0
222 223 else
223 224 return false
224 225 end
225 226 end
226 227
227 228 def contest_started?
228 229 if GraderConfiguration.indv_contest_mode?
229 230 stat = self.contest_stat
230 231 return ((stat != nil) and (stat.started_at != nil))
231 232 elsif GraderConfiguration.contest_mode?
232 233 return true if site==nil
233 234 return site.started
234 235 else
235 236 return true
236 237 end
237 238 end
238 239
239 240 def update_start_time
240 241 stat = self.contest_stat
241 242 if stat.nil? or stat.started_at.nil?
242 243 stat ||= UserContestStat.new(:user => self)
243 244 stat.started_at = Time.now.gmtime
244 245 stat.save
245 246 end
246 247 end
247 248
248 249 def problem_in_user_contests?(problem)
249 250 problem_contests = problem.contests.all
250 251
251 252 if problem_contests.length == 0 # this is public contest
252 253 return true
253 254 end
254 255
255 256 contests.each do |contest|
256 257 if problem_contests.find {|c| c.id == contest.id }
257 258 return true
258 259 end
259 260 end
260 261 return false
261 262 end
262 263
263 264 def available_problems_group_by_contests
264 265 contest_problems = []
265 266 pin = {}
266 267 contests.enabled.each do |contest|
267 268 available_problems = contest.problems.available
268 269 contest_problems << {
269 270 :contest => contest,
270 271 :problems => available_problems
271 272 }
272 273 available_problems.each {|p| pin[p.id] = true}
273 274 end
274 275 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
275 276 contest_problems << {
276 277 :contest => nil,
277 278 :problems => other_avaiable_problems
278 279 }
279 280 return contest_problems
280 281 end
281 282
282 283 def solve_all_available_problems?
283 284 available_problems.each do |p|
284 285 u = self
285 286 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
286 287 return false if !p or !sub or sub.points < p.full_score
287 288 end
288 289 return true
289 290 end
290 291
291 292 def available_problems
292 293 if not GraderConfiguration.multicontests?
294 + if GraderConfiguration.use_problem_group?
295 + return available_problems_in_group
296 + else
293 297 return Problem.available_problems
298 + end
294 299 else
295 300 contest_problems = []
296 301 pin = {}
297 302 contests.enabled.each do |contest|
298 303 contest.problems.available.each do |problem|
299 304 if not pin.has_key? problem.id
300 305 contest_problems << problem
301 306 end
302 307 pin[problem.id] = true
303 308 end
304 309 end
305 310 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
306 311 return contest_problems + other_avaiable_problems
307 312 end
308 313 end
309 314
315 + def available_problems_in_group
316 + problem = []
317 + self.groups.each do |group|
318 + group.problems.where(available: true).each { |p| problem << p }
319 + end
320 + return problem.uniq
321 + end
322 +
310 323 def can_view_problem?(problem)
311 324 if not GraderConfiguration.multicontests?
312 325 return problem.available
313 326 else
314 327 return problem_in_user_contests? problem
315 328 end
316 329 end
317 330
318 331 def self.clear_last_login
319 332 User.update_all(:last_ip => nil)
320 333 end
321 334
322 335 protected
323 336 def encrypt_new_password
324 337 return if password.blank?
325 338 self.salt = (10+rand(90)).to_s
326 339 self.hashed_password = User.encrypt(self.password,self.salt)
327 340 end
328 341
329 342 def assign_default_site
330 343 # have to catch error when migrating (because self.site is not available).
331 344 begin
332 345 if self.site==nil
333 346 self.site = Site.find_by_name('default')
334 347 if self.site==nil
335 348 self.site = Site.find(1) # when 'default has be renamed'
336 349 end
337 350 end
338 351 rescue
339 352 end
340 353 end
341 354
342 355 def assign_default_contest
343 356 # have to catch error when migrating (because self.site is not available).
344 357 begin
345 358 if self.contests.length == 0
346 359 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
347 360 if default_contest
348 361 self.contests = [default_contest]
349 362 end
350 363 end
351 364 rescue
352 365 end
353 366 end
354 367
355 368 def password_required?
356 369 self.hashed_password.blank? || !self.password.blank?
357 370 end
358 371
359 372 def self.encrypt(string,salt)
360 373 Digest::SHA1.hexdigest(salt + string)
361 374 end
362 375
363 376 def uniqueness_of_email_from_activated_users
364 377 user = User.activated_users.find_by_email(self.email)
365 378 if user and (user.login != self.login)
366 379 self.errors.add(:base,"Email has already been taken")
367 380 end
368 381 end
369 382
370 383 def enough_time_interval_between_same_email_registrations
371 384 return if !self.new_record?
372 385 return if self.activated
373 386 open_user = User.find_by_email(self.email,
374 387 :order => 'created_at DESC')
375 388 if open_user and open_user.created_at and
376 389 (open_user.created_at > Time.now.gmtime - 5.minutes)
377 390 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
378 391 end
379 392 end
380 393
381 394 def email_validation?
382 395 begin
383 396 return VALIDATE_USER_EMAILS
384 397 rescue
385 398 return false
386 399 end
387 400 end
388 401 end
@@ -1,85 +1,90
1 1 - content_for :head do
2 2 = stylesheet_link_tag 'problems'
3 3 = javascript_include_tag 'local_jquery'
4 4
5 5 :javascript
6 6 $(document).ready( function() {
7 7 function shiftclick(start,stop,value) {
8 8 $('tr input').each( function(id,input) {
9 9 var $input=$(input);
10 10 var iid=parseInt($input.attr('id').split('-')[2]);
11 11 if(iid>=start&&iid<=stop){
12 12 $input.prop('checked',value)
13 13 }
14 14 });
15 15 }
16 16
17 17 $('tr input').click( function(e) {
18 18 if (e.shiftKey) {
19 19 stop = parseInt($(this).attr('id').split('-')[2]);
20 20 var orig_stop = stop
21 21 if (typeof start !== 'undefined') {
22 22 if (start > stop) {
23 23 var tmp = start;
24 24 start = stop;
25 25 stop = tmp;
26 26 }
27 27 shiftclick(start,stop,$(this).is(':checked') )
28 28 }
29 29 start = orig_stop
30 30 } else {
31 31 start = parseInt($(this).attr('id').split('-')[2]);
32 32 }
33 33 });
34 34 });
35 35
36 36
37 37 %h1 Manage problems
38 38
39 39 %p= link_to '[Back to problem list]', :action => 'list'
40 40
41 41 = form_tag :action=>'do_manage' do
42 - .submitbox
42 + .submitbox.panel
43 43 What do you want to do to the selected problem?
44 44 %br/
45 45 (You can shift-click to select a range of problems)
46 46 %ul
47 47 %li
48 48 Change date added to
49 49 = select_date Date.current, :prefix => 'date_added'
50 50 &nbsp;&nbsp;&nbsp;
51 51 = submit_tag 'Change', :name => 'change_date_added'
52 52 %li
53 53 Set available to
54 54 = submit_tag 'True', :name => 'enable_problem'
55 55 = submit_tag 'False', :name => 'disable_problem'
56 56
57 57 - if GraderConfiguration.multicontests?
58 58 %li
59 59 Add to
60 60 = select("contest","id",Contest.all.collect {|c| [c.title, c.id]})
61 61 = submit_tag 'Add', :name => 'add_to_contest'
62 + %li
63 + Add problems to group
64 + = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
65 + = submit_tag 'Add', name: 'add_group'
62 66
63 - %table
67 +
68 + %table.table.table-hover
64 69 %tr{style: "text-align: left;"}
65 70 %th= check_box_tag 'select_all'
66 71 %th Name
67 72 %th Full name
68 73 %th Available
69 74 %th Date added
70 75 - if GraderConfiguration.multicontests?
71 76 %th Contests
72 77
73 78 - num = 0
74 79 - for problem in @problems
75 80 - num += 1
76 81 %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"}
77 82 %td= check_box_tag "prob-#{problem.id}-#{num}"
78 83 %td= problem.name
79 84 %td= problem.full_name
80 85 %td= problem.available
81 86 %td= problem.date_added
82 87 - if GraderConfiguration.multicontests?
83 88 %td
84 89 - problem.contests.each do |contest|
85 90 = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])"
@@ -1,86 +1,86
1 1 %h1 Bulk Manage User
2 2
3 3 = form_tag bulk_manage_user_admin_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 26 .col-md-6
27 27 .panel.panel-primary
28 28 .panel-title.panel-heading
29 29 Action
30 30 .panel-body
31 31 .row.form-group
32 32 .col-md-6
33 33 %label.checkbox-inline
34 34 = check_box_tag "enabled", true, params[:enabled]
35 35 Change "Enabled" to
36 36 .col-md-3
37 37 %label.radio-inline
38 38 = radio_button_tag "enable", 1, params[:enable] == '1', id: 'enable-yes'
39 39 Yes
40 40 .col-md-3
41 41 %label.radio-inline
42 42 = radio_button_tag "enable", 0, params[:enable] == '0', id: 'enable-no'
43 43 No
44 44 .row.form-group
45 45 .col-md-6
46 46 %label.checkbox-inline
47 47 = check_box_tag "gen_password", true, params[:gen_password]
48 48 Generate new random password
49 49 .row.form-group
50 50 .col-md-4
51 51 %label.checkbox-inline
52 52 = check_box_tag "add_group", true, params[:add_group]
53 53 Add users to group
54 54 %label.col-md-3.control-label.text-right Group name
55 55 .col-md-5
56 - = text_field_tag "group_name", params[:group_name], id: 'group_name',class: 'form-control select2'
56 + = select_tag "group_name", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'form-control select2'
57 57
58 58
59 59 .row
60 60 .col-md-12
61 61 = submit_tag "Preview Result", class: 'btn btn-default'
62 62 - if @users
63 63 .row
64 64 .col-md-4
65 65 - if @action
66 66 %h2 Confirmation
67 67 - if @action[:set_enable]
68 68 .alert.alert-info The following users will be set #{(@action[:enabled] ? 'enable' : 'disable')}.
69 69 - if @action[:gen_password]
70 70 .alert.alert-info The password of the following users will be randomly generated.
71 71 .row
72 72 .col-md-4
73 73 = submit_tag "Perform", class: 'btn btn-primary'
74 74 .row
75 75 .col-md-12
76 76 The pattern matches #{@users.count} following users.
77 77 %br
78 78 - @users.each do |user|
79 79 = user.login
80 80 = ' '
81 81 = user.full_name
82 82 = ' '
83 83 = "(#{user.remark})" if user.remark
84 84 %br
85 85
86 86
@@ -1,98 +1,106
1 1 CafeGrader::Application.routes.draw do
2 2 get "sources/direct_edit"
3 3
4 4 root :to => 'main#login'
5 5
6 6 #logins
7 7 get 'login/login', to: 'login#login'
8 8
9 9 resources :contests
10 10
11 11 resources :sites
12 12
13 13 resources :announcements do
14 14 member do
15 15 get 'toggle','toggle_front'
16 16 end
17 17 end
18 18
19 19 resources :problems do
20 20 member do
21 21 get 'toggle'
22 22 get 'toggle_test'
23 23 get 'toggle_view_testcase'
24 24 get 'stat'
25 25 end
26 26 collection do
27 27 get 'turn_all_off'
28 28 get 'turn_all_on'
29 29 get 'import'
30 30 get 'manage'
31 31 end
32 + end
32 33
34 + resources :groups do
35 + member do
36 + post 'add_user', to: 'groups#add_user', as: 'add_user'
37 + delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
38 + post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
39 + delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
40 + end
33 41 end
34 42
35 43 resources :testcases, only: [] do
36 44 member do
37 45 get 'download_input'
38 46 get 'download_sol'
39 47 end
40 48 collection do
41 49 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
42 50 end
43 51 end
44 52
45 53 resources :grader_configuration, controller: 'configurations'
46 54
47 55 resources :users do
48 56 member do
49 57 get 'toggle_activate', 'toggle_enable'
50 58 get 'stat'
51 59 end
52 60 end
53 61
54 62 resources :submissions do
55 63 member do
56 64 get 'download'
57 65 get 'compiler_msg'
58 66 get 'rejudge'
59 67 end
60 68 collection do
61 69 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
62 70 get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
63 71 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
64 72 end
65 73 end
66 74
67 75
68 76
69 77 #main
70 78 get "main/list"
71 79 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
72 80
73 81 #user admin
74 82 get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
75 83
76 84 #report
77 85 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
78 86 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
79 87 get "report/login"
80 88 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
81 89 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
82 90
83 91
84 92 #
85 93 get 'tasks/view/:file.:ext' => 'tasks#view'
86 94 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
87 95 get 'heartbeat/:id/edit' => 'heartbeat#edit'
88 96
89 97 #grader
90 98 get 'graders/list', to: 'graders#list', as: 'grader_list'
91 99
92 100
93 101 # See how all your routes lay out with "rake routes"
94 102
95 103 # This is a legacy wild controller route that's not recommended for RESTful applications.
96 104 # Note: This route will make all actions in every controller accessible via GET requests.
97 105 match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
98 106 end
@@ -1,177 +1,196
1 1 # encoding: UTF-8
2 2 # This file is auto-generated from the current state of the database. Instead
3 3 # of editing this file, please use the migrations feature of Active Record to
4 4 # incrementally modify your database, and then regenerate this schema definition.
5 5 #
6 6 # Note that this schema.rb definition is the authoritative source for your
7 7 # database schema. If you need to create the application database on another
8 8 # system, you should be using db:schema:load, not running all the migrations
9 9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 10 # you'll amass, the slower it'll run and the greater likelihood for issues).
11 11 #
12 12 # It's strongly recommended that you check this file into your version control system.
13 13
14 - ActiveRecord::Schema.define(version: 20170427070345) do
14 + ActiveRecord::Schema.define(version: 20170911091143) do
15 15
16 16 create_table "announcements", force: :cascade do |t|
17 17 t.string "author", limit: 255
18 18 t.text "body", limit: 65535
19 19 t.boolean "published"
20 20 t.datetime "created_at", null: false
21 21 t.datetime "updated_at", null: false
22 22 t.boolean "frontpage", default: false
23 23 t.boolean "contest_only", default: false
24 24 t.string "title", limit: 255
25 25 t.string "notes", limit: 255
26 26 end
27 27
28 28 create_table "contests", force: :cascade do |t|
29 29 t.string "title", limit: 255
30 30 t.boolean "enabled"
31 31 t.datetime "created_at", null: false
32 32 t.datetime "updated_at", null: false
33 33 t.string "name", limit: 255
34 34 end
35 35
36 36 create_table "contests_problems", id: false, force: :cascade do |t|
37 37 t.integer "contest_id", limit: 4
38 38 t.integer "problem_id", limit: 4
39 39 end
40 40
41 41 create_table "contests_users", id: false, force: :cascade do |t|
42 42 t.integer "contest_id", limit: 4
43 43 t.integer "user_id", limit: 4
44 44 end
45 45
46 46 create_table "countries", force: :cascade do |t|
47 47 t.string "name", limit: 255
48 48 t.datetime "created_at", null: false
49 49 t.datetime "updated_at", null: false
50 50 end
51 51
52 52 create_table "descriptions", force: :cascade do |t|
53 53 t.text "body", limit: 65535
54 54 t.boolean "markdowned"
55 55 t.datetime "created_at", null: false
56 56 t.datetime "updated_at", null: false
57 57 end
58 58
59 59 create_table "grader_configurations", force: :cascade do |t|
60 60 t.string "key", limit: 255
61 61 t.string "value_type", limit: 255
62 62 t.string "value", limit: 255
63 63 t.datetime "created_at", null: false
64 64 t.datetime "updated_at", null: false
65 65 t.text "description", limit: 65535
66 66 end
67 67
68 68 create_table "grader_processes", force: :cascade do |t|
69 69 t.string "host", limit: 255
70 70 t.integer "pid", limit: 4
71 71 t.string "mode", limit: 255
72 72 t.boolean "active"
73 73 t.datetime "created_at", null: false
74 74 t.datetime "updated_at", null: false
75 75 t.integer "task_id", limit: 4
76 76 t.string "task_type", limit: 255
77 77 t.boolean "terminated"
78 78 end
79 79
80 80 add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_ip_and_pid", using: :btree
81 81
82 + create_table "groups", force: :cascade do |t|
83 + t.string "name", limit: 255
84 + t.string "description", limit: 255
85 + end
86 +
87 + create_table "groups_problems", id: false, force: :cascade do |t|
88 + t.integer "problem_id", limit: 4, null: false
89 + t.integer "group_id", limit: 4, null: false
90 + end
91 +
92 + add_index "groups_problems", ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id", using: :btree
93 +
94 + create_table "groups_users", id: false, force: :cascade do |t|
95 + t.integer "group_id", limit: 4, null: false
96 + t.integer "user_id", limit: 4, null: false
97 + end
98 +
99 + add_index "groups_users", ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id", using: :btree
100 +
82 101 create_table "heart_beats", force: :cascade do |t|
83 102 t.integer "user_id", limit: 4
84 103 t.string "ip_address", limit: 255
85 104 t.datetime "created_at", null: false
86 105 t.datetime "updated_at", null: false
87 106 t.string "status", limit: 255
88 107 end
89 108
90 109 add_index "heart_beats", ["updated_at"], name: "index_heart_beats_on_updated_at", using: :btree
91 110
92 111 create_table "languages", force: :cascade do |t|
93 112 t.string "name", limit: 10
94 113 t.string "pretty_name", limit: 255
95 114 t.string "ext", limit: 10
96 115 t.string "common_ext", limit: 255
97 116 end
98 117
99 118 create_table "logins", force: :cascade do |t|
100 119 t.integer "user_id", limit: 4
101 120 t.string "ip_address", limit: 255
102 121 t.datetime "created_at", null: false
103 122 t.datetime "updated_at", null: false
104 123 end
105 124
106 125 create_table "messages", force: :cascade do |t|
107 126 t.integer "sender_id", limit: 4
108 127 t.integer "receiver_id", limit: 4
109 128 t.integer "replying_message_id", limit: 4
110 129 t.text "body", limit: 65535
111 130 t.boolean "replied"
112 131 t.datetime "created_at", null: false
113 132 t.datetime "updated_at", null: false
114 133 end
115 134
116 135 create_table "problems", force: :cascade do |t|
117 136 t.string "name", limit: 30
118 137 t.string "full_name", limit: 255
119 138 t.integer "full_score", limit: 4
120 139 t.date "date_added"
121 140 t.boolean "available"
122 141 t.string "url", limit: 255
123 142 t.integer "description_id", limit: 4
124 143 t.boolean "test_allowed"
125 144 t.boolean "output_only"
126 145 t.string "description_filename", limit: 255
127 146 t.boolean "view_testcase"
128 147 end
129 148
130 149 create_table "rights", force: :cascade do |t|
131 150 t.string "name", limit: 255
132 151 t.string "controller", limit: 255
133 152 t.string "action", limit: 255
134 153 end
135 154
136 155 create_table "rights_roles", id: false, force: :cascade do |t|
137 156 t.integer "right_id", limit: 4
138 157 t.integer "role_id", limit: 4
139 158 end
140 159
141 160 add_index "rights_roles", ["role_id"], name: "index_rights_roles_on_role_id", using: :btree
142 161
143 162 create_table "roles", force: :cascade do |t|
144 163 t.string "name", limit: 255
145 164 end
146 165
147 166 create_table "roles_users", id: false, force: :cascade do |t|
148 167 t.integer "role_id", limit: 4
149 168 t.integer "user_id", limit: 4
150 169 end
151 170
152 171 add_index "roles_users", ["user_id"], name: "index_roles_users_on_user_id", using: :btree
153 172
154 173 create_table "sessions", force: :cascade do |t|
155 174 t.string "session_id", limit: 255
156 175 t.text "data", limit: 65535
157 176 t.datetime "updated_at"
158 177 end
159 178
160 179 add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", using: :btree
161 180 add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at", using: :btree
162 181
163 182 create_table "sites", force: :cascade do |t|
164 183 t.string "name", limit: 255
165 184 t.boolean "started"
166 185 t.datetime "start_time"
167 186 t.datetime "created_at", null: false
168 187 t.datetime "updated_at", null: false
169 188 t.integer "country_id", limit: 4
170 189 t.string "password", limit: 255
171 190 end
172 191
173 192 create_table "submission_view_logs", force: :cascade do |t|
174 193 t.integer "user_id", limit: 4
175 194 t.integer "submission_id", limit: 4
176 195 t.datetime "created_at", null: false
177 196 t.datetime "updated_at", null: false
@@ -70,189 +70,198
70 70
71 71 {
72 72 :key => 'right.user_view_submission',
73 73 :value_type => 'boolean',
74 74 :default_value => 'false',
75 75 :description => 'If true, any user can view submissions of every one.'
76 76 },
77 77
78 78 {
79 79 :key => 'right.bypass_agreement',
80 80 :value_type => 'boolean',
81 81 :default_value => 'true',
82 82 :description => 'When false, a user must accept usage agreement before login'
83 83 },
84 84
85 85 {
86 86 :key => 'right.heartbeat_response',
87 87 :value_type => 'string',
88 88 :default_value => 'OK',
89 89 :description => 'Heart beat response text'
90 90 },
91 91
92 92 {
93 93 :key => 'right.heartbeat_response_full',
94 94 :value_type => 'string',
95 95 :default_value => 'OK',
96 96 :description => 'Heart beat response text when user got full score (set this value to the empty string to disable this feature)'
97 97 },
98 98
99 99 {
100 100 :key => 'right.view_testcase',
101 101 :value_type => 'boolean',
102 102 :default_value => 'false',
103 103 :description => 'When true, any user can view/download test data'
104 104 },
105 105 # If Configuration['system.online_registration'] is true, the
106 106 # system allows online registration, and will use these
107 107 # information for sending confirmation emails.
108 108 {
109 109 :key => 'system.online_registration.smtp',
110 110 :value_type => 'string',
111 111 :default_value => 'smtp.somehost.com'
112 112 },
113 113
114 114 {
115 115 :key => 'system.online_registration.from',
116 116 :value_type => 'string',
117 117 :default_value => 'your.email@address'
118 118 },
119 119
120 120 {
121 121 :key => 'system.admin_email',
122 122 :value_type => 'string',
123 123 :default_value => 'admin@admin.email'
124 124 },
125 125
126 126 {
127 127 :key => 'system.user_setting_enabled',
128 128 :value_type => 'boolean',
129 129 :default_value => 'true',
130 130 :description => 'If this option is true, users can change their settings'
131 131 },
132 132
133 133 {
134 134 :key => 'system.user_setting_enabled',
135 135 :value_type => 'boolean',
136 136 :default_value => 'true',
137 137 :description => 'If this option is true, users can change their settings'
138 138 },
139 139
140 140 # If Configuration['contest.test_request.early_timeout'] is true
141 141 # the user will not be able to use test request at 30 minutes
142 142 # before the contest ends.
143 143 {
144 144 :key => 'contest.test_request.early_timeout',
145 145 :value_type => 'boolean',
146 146 :default_value => 'false'
147 147 },
148 148
149 149 {
150 150 :key => 'system.multicontests',
151 151 :value_type => 'boolean',
152 152 :default_value => 'false'
153 153 },
154 154
155 155 {
156 156 :key => 'contest.confirm_indv_contest_start',
157 157 :value_type => 'boolean',
158 158 :default_value => 'false'
159 159 },
160 160
161 161 {
162 162 :key => 'contest.default_contest_name',
163 163 :value_type => 'string',
164 164 :default_value => 'none',
165 165 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
166 - }
166 + },
167 +
168 + {
169 + :key => 'system.use_problem_group',
170 + :value_type => 'boolean',
171 + :default_value => 'false',
172 + :description => "If true, available problem to the user will be only ones associated with the group of the user."
173 + },
174 +
175 +
167 176
168 177 ]
169 178
170 179
171 180 def create_configuration_key(key,
172 181 value_type,
173 182 default_value,
174 183 description='')
175 184 conf = (GraderConfiguration.find_by_key(key) ||
176 185 GraderConfiguration.new(:key => key,
177 186 :value_type => value_type,
178 187 :value => default_value))
179 188 conf.description = description
180 189 conf.save
181 190 end
182 191
183 192 def seed_config
184 193 CONFIGURATIONS.each do |conf|
185 194 if conf.has_key? :description
186 195 desc = conf[:description]
187 196 else
188 197 desc = ''
189 198 end
190 199 create_configuration_key(conf[:key],
191 200 conf[:value_type],
192 201 conf[:default_value],
193 202 desc)
194 203 end
195 204 end
196 205
197 206 def seed_roles
198 207 return if Role.find_by_name('admin')
199 208
200 209 role = Role.create(:name => 'admin')
201 210 user_admin_right = Right.create(:name => 'user_admin',
202 211 :controller => 'user_admin',
203 212 :action => 'all')
204 213 problem_admin_right = Right.create(:name=> 'problem_admin',
205 214 :controller => 'problems',
206 215 :action => 'all')
207 216
208 217 graders_right = Right.create(:name => 'graders_admin',
209 218 :controller => 'graders',
210 219 :action => 'all')
211 220
212 221 role.rights << user_admin_right;
213 222 role.rights << problem_admin_right;
214 223 role.rights << graders_right;
215 224 role.save
216 225 end
217 226
218 227 def seed_root
219 228 return if User.find_by_login('root')
220 229
221 230 root = User.new(:login => 'root',
222 231 :full_name => 'Administrator',
223 232 :alias => 'root')
224 233 root.password = 'ioionrails';
225 234
226 235 class << root
227 236 public :encrypt_new_password
228 237 def valid?(context=nil)
229 238 true
230 239 end
231 240 end
232 241
233 242 root.encrypt_new_password
234 243
235 244 root.roles << Role.find_by_name('admin')
236 245
237 246 root.activated = true
238 247 root.save
239 248 end
240 249
241 250 def seed_users_and_roles
242 251 seed_roles
243 252 seed_root
244 253 end
245 254
246 255 def seed_more_languages
247 256 Language.delete_all
248 257 Language.create( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
249 258 Language.create( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
250 259 Language.create( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
251 260 Language.create( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
252 261 Language.create( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
253 262 Language.create( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
254 263 end
255 264
256 265 seed_config
257 266 seed_users_and_roles
258 267 seed_more_languages
You need to be logged in to leave comments. Login now