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

r689:124074894d4b - - 16 files changed: 106 inserted, 21 deleted

new file 100644
new file 100644
new file 100644
new file 100644
@@ -0,0 +1,7
1 + class GroupProblem < ActiveRecord::Base
2 + self.table_name = 'groups_problems'
3 +
4 + belongs_to :problem
5 + belongs_to :group
6 + validates_uniqueness_of :problem_id, scope: :group_id, message: ->(object, data) { "'#{Problem.find(data[:value]).full_name}' is already in the group" }
7 + end
@@ -0,0 +1,7
1 + class GroupUser < ActiveRecord::Base
2 + self.table_name = 'groups_users'
3 +
4 + belongs_to :user
5 + belongs_to :group
6 + validates_uniqueness_of :user_id, scope: :group_id, message: ->(object, data) { "'#{User.find(data[:value]).full_name}' is already in the group" }
7 + end
@@ -0,0 +1,23
1 + # Be sure to restart your server when you modify this file.
2 +
3 + # Version of your assets, change this if you want to expire all your assets.
4 + Rails.application.config.assets.version = '1.0'
5 +
6 + # Add additional assets to the asset load path.
7 + # Rails.application.config.assets.paths << Emoji.images_path
8 + # Add Yarn node_modules folder to the asset load path.
9 + Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 + Rails.application.config.assets.paths << Rails.root.join('vendor/assets/fonts')
11 +
12 + # Precompile additional assets.
13 + # application.js, application.css, and all non-JS/CSS in the app/assets
14 + # folder are already added.
15 + # Rails.application.config.assets.precompile += %w( admin.js admin.css )
16 +
17 + Rails.application.config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js']
18 + Rails.application.config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css']
19 + %w( announcements submissions configurations contests contest_management graders heartbeat
20 + login main messages problems report site sites sources tasks groups
21 + test user_admin users ).each do |controller|
22 + Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.css"]
23 + end
@@ -1,86 +1,94
1 1 class GroupsController < ApplicationController
2 2 before_action :set_group, only: [:show, :edit, :update, :destroy,
3 3 :add_user, :remove_user,
4 4 :add_problem, :remove_problem,
5 5 ]
6 6 before_action :authenticate, :admin_authorization
7 7
8 8 # GET /groups
9 9 def index
10 10 @groups = Group.all
11 11 end
12 12
13 13 # GET /groups/1
14 14 def show
15 15 end
16 16
17 17 # GET /groups/new
18 18 def new
19 19 @group = Group.new
20 20 end
21 21
22 22 # GET /groups/1/edit
23 23 def edit
24 24 end
25 25
26 26 # POST /groups
27 27 def create
28 28 @group = Group.new(group_params)
29 29
30 30 if @group.save
31 31 redirect_to @group, notice: 'Group was successfully created.'
32 32 else
33 33 render :new
34 34 end
35 35 end
36 36
37 37 # PATCH/PUT /groups/1
38 38 def update
39 39 if @group.update(group_params)
40 40 redirect_to @group, notice: 'Group was successfully updated.'
41 41 else
42 42 render :edit
43 43 end
44 44 end
45 45
46 46 # DELETE /groups/1
47 47 def destroy
48 48 @group.destroy
49 49 redirect_to groups_url, notice: 'Group was successfully destroyed.'
50 50 end
51 51
52 52 def remove_user
53 53 user = User.find(params[:user_id])
54 54 @group.users.delete(user)
55 - redirect_to group_path(@group), notice: "User #{user.login} was removed from the group #{@group.name}"
55 + redirect_to group_path(@group), flash: {success: "User #{user.login} was removed from the group #{@group.name}"}
56 56 end
57 57
58 58 def add_user
59 59 user = User.find(params[:user_id])
60 + begin
60 61 @group.users << user
61 - redirect_to group_path(@group), notice: "User #{user.login} was add to the group #{@group.name}"
62 + redirect_to group_path(@group), flash: { success: "User #{user.login} was add to the group #{@group.name}"}
63 + rescue => e
64 + redirect_to group_path(@group), alert: e.message
65 + end
62 66 end
63 67
64 68 def remove_problem
65 69 problem = Problem.find(params[:problem_id])
66 70 @group.problems.delete(problem)
67 - redirect_to group_path(@group), notice: "Problem #{problem.name} was removed from the group #{@group.name}"
71 + redirect_to group_path(@group), flash: {success: "Problem #{problem.name} was removed from the group #{@group.name}" }
68 72 end
69 73
70 74 def add_problem
71 75 problem = Problem.find(params[:problem_id])
76 + begin
72 77 @group.problems << problem
73 - redirect_to group_path(@group), notice: "Problem #{problem.name} was add to the group #{@group.name}"
78 + redirect_to group_path(@group), flash: {success: "Problem #{problem.name} was add to the group #{@group.name}" }
79 + rescue => e
80 + redirect_to group_path(@group), alert: e.message
81 + end
74 82 end
75 83
76 84 private
77 85 # Use callbacks to share common setup or constraints between actions.
78 86 def set_group
79 87 @group = Group.find(params[:id])
80 88 end
81 89
82 90 # Only allow a trusted parameter "white list" through.
83 91 def group_params
84 92 params.require(:group).permit(:name, :description)
85 93 end
86 94 end
@@ -104,196 +104,206
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 198 elsif params.has_key? 'add_group'
199 199 group = Group.find(params[:group_id])
200 + ok = []
201 + failed = []
200 202 get_problems_from_params.each do |p|
203 + begin
201 204 group.problems << p
205 + ok << p.full_name
206 + rescue => e
207 + failed << p.full_name
202 208 end
203 209 end
210 + flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
211 + flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
212 + end
213 +
204 214 redirect_to :action => 'manage'
205 215 end
206 216
207 217 def import
208 218 @allow_test_pair_import = allow_test_pair_import?
209 219 end
210 220
211 221 def do_import
212 222 old_problem = Problem.find_by_name(params[:name])
213 223 if !allow_test_pair_import? and params.has_key? :import_to_db
214 224 params.delete :import_to_db
215 225 end
216 226 @problem, import_log = Problem.create_from_import_form_params(params,
217 227 old_problem)
218 228
219 229 if !@problem.errors.empty?
220 230 render :action => 'import' and return
221 231 end
222 232
223 233 if old_problem!=nil
224 234 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
225 235 end
226 236 @log = import_log
227 237 end
228 238
229 239 def remove_contest
230 240 problem = Problem.find(params[:id])
231 241 contest = Contest.find(params[:contest_id])
232 242 if problem!=nil and contest!=nil
233 243 problem.contests.delete(contest)
234 244 end
235 245 redirect_to :action => 'manage'
236 246 end
237 247
238 248 ##################################
239 249 protected
240 250
241 251 def allow_test_pair_import?
242 252 if defined? ALLOW_TEST_PAIR_IMPORT
243 253 return ALLOW_TEST_PAIR_IMPORT
244 254 else
245 255 return false
246 256 end
247 257 end
248 258
249 259 def change_date_added
250 260 problems = get_problems_from_params
251 261 year = params[:date_added][:year].to_i
252 262 month = params[:date_added][:month].to_i
253 263 day = params[:date_added][:day].to_i
254 264 date = Date.new(year,month,day)
255 265 problems.each do |p|
256 266 p.date_added = date
257 267 p.save
258 268 end
259 269 end
260 270
261 271 def add_to_contest
262 272 problems = get_problems_from_params
263 273 contest = Contest.find(params[:contest][:id])
264 274 if contest!=nil and contest.enabled
265 275 problems.each do |p|
266 276 p.contests << contest
267 277 end
268 278 end
269 279 end
270 280
271 281 def set_available(avail)
272 282 problems = get_problems_from_params
273 283 problems.each do |p|
274 284 p.available = avail
275 285 p.save
276 286 end
277 287 end
278 288
279 289 def get_problems_from_params
280 290 problems = []
281 291 params.keys.each do |k|
282 292 if k.index('prob-')==0
283 293 name, id, order = k.split('-')
284 294 problems << Problem.find(id)
285 295 end
286 296 end
287 297 problems
288 298 end
289 299
290 300 def get_problems_stat
291 301 end
292 302
293 303 private
294 304
295 305 def problem_params
296 306 params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description)
297 307 end
298 308
299 309 end
@@ -349,193 +349,204
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 ?) OR (remark REGEXP ?)',params[:regex],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 443 if @action[:add_group] and @action[:group_name]
444 444 @group = Group.find(@action[:group_name])
445 - @users.each { |user| @group.users << user }
445 + ok = []
446 + failed = []
447 + @users.each do |user|
448 + begin
449 + @group.users << user
450 + ok << user.login
451 + rescue => e
452 + failed << user.login
453 + end
454 + end
455 + flash[:success] = "The following users are added to the 'group #{@group.name}': " + ok.join(', ') if ok.count > 0
456 + flash[:alert] = "The following users are already in the 'group #{@group.name}': " + failed.join(', ') if failed.count > 0
446 457 end
447 458 end
448 459 end
449 460
450 461 protected
451 462
452 463 def random_password(length=5)
453 464 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
454 465 newpass = ""
455 466 length.times { newpass << chars[rand(chars.size-1)] }
456 467 return newpass
457 468 end
458 469
459 470 def import_from_file(f)
460 471 data_hash = YAML.load(f)
461 472 @import_log = ""
462 473
463 474 country_data = data_hash[:countries]
464 475 site_data = data_hash[:sites]
465 476 user_data = data_hash[:users]
466 477
467 478 # import country
468 479 countries = {}
469 480 country_data.each_pair do |id,country|
470 481 c = Country.find_by_name(country[:name])
471 482 if c!=nil
472 483 countries[id] = c
473 484 @import_log << "Found #{country[:name]}\n"
474 485 else
475 486 countries[id] = Country.new(:name => country[:name])
476 487 countries[id].save
477 488 @import_log << "Created #{country[:name]}\n"
478 489 end
479 490 end
480 491
481 492 # import sites
482 493 sites = {}
483 494 site_data.each_pair do |id,site|
484 495 s = Site.find_by_name(site[:name])
485 496 if s!=nil
486 497 @import_log << "Found #{site[:name]}\n"
487 498 else
488 499 s = Site.new(:name => site[:name])
489 500 @import_log << "Created #{site[:name]}\n"
490 501 end
491 502 s.password = site[:password]
492 503 s.country = countries[site[:country_id]]
493 504 s.save
494 505 sites[id] = s
495 506 end
496 507
497 508 # import users
498 509 user_data.each_pair do |id,user|
499 510 u = User.find_by_login(user[:login])
500 511 if u!=nil
501 512 @import_log << "Found #{user[:login]}\n"
502 513 else
503 514 u = User.new(:login => user[:login])
504 515 @import_log << "Created #{user[:login]}\n"
505 516 end
506 517 u.full_name = user[:name]
507 518 u.password = user[:password]
508 519 u.country = countries[user[:country_id]]
509 520 u.site = sites[user[:site_id]]
510 521 u.activated = true
511 522 u.email = "empty-#{u.login}@none.com"
512 523 if not u.save
513 524 @import_log << "Errors\n"
514 525 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
515 526 end
516 527 end
517 528
518 529 end
519 530
520 531 def logout_users(users)
521 532 users.each do |user|
522 533 contest_stat = user.contest_stat(true)
523 534 if contest_stat and !contest_stat.forced_logout
524 535 contest_stat.forced_logout = true
525 536 contest_stat.save
526 537 end
527 538 end
528 539 end
529 540
530 541 def send_contest_update_notification_email(user, contest)
531 542 contest_title_name = GraderConfiguration['contest.name']
532 543 contest_name = contest.name
533 544 mail_subject = t('contest.notification.email_subject', {
534 545 :contest_title_name => contest_title_name,
535 546 :contest_name => contest_name })
536 547 mail_body = t('contest.notification.email_body', {
537 548 :full_name => user.full_name,
538 549 :contest_title_name => contest_title_name,
539 550 :contest_name => contest.name,
540 551 })
541 552
@@ -107,115 +107,115
107 107 nil
108 108 end
109 109 end
110 110
111 111 def toggle_button(on,toggle_url,id, option={})
112 112 btn_size = option[:size] || 'btn-xs'
113 113 link_to (on ? "Yes" : "No"), toggle_url,
114 114 {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
115 115 id: id,
116 116 data: {remote: true, method: 'get'}}
117 117 end
118 118
119 119 def get_ace_mode(language)
120 120 # return ace mode string from Language
121 121
122 122 case language.pretty_name
123 123 when 'Pascal'
124 124 'ace/mode/pascal'
125 125 when 'C++','C'
126 126 'ace/mode/c_cpp'
127 127 when 'Ruby'
128 128 'ace/mode/ruby'
129 129 when 'Python'
130 130 'ace/mode/python'
131 131 when 'Java'
132 132 'ace/mode/java'
133 133 else
134 134 'ace/mode/c_cpp'
135 135 end
136 136 end
137 137
138 138
139 139 def user_title_bar(user)
140 140 header = ''
141 141 time_left = ''
142 142
143 143 #
144 144 # if the contest is over
145 145 if GraderConfiguration.time_limit_mode?
146 146 if user.contest_finished?
147 147 header = <<CONTEST_OVER
148 148 <tr><td colspan="2" align="center">
149 149 <span class="contest-over-msg">THE CONTEST IS OVER</span>
150 150 </td></tr>
151 151 CONTEST_OVER
152 152 end
153 153 if !user.contest_started?
154 154 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
155 155 else
156 156 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
157 157 " #{format_short_duration(user.contest_time_left)}"
158 158 end
159 159 end
160 160
161 161 #
162 162 # if the contest is in the anaysis mode
163 163 if GraderConfiguration.analysis_mode?
164 164 header = <<ANALYSISMODE
165 165 <tr><td colspan="2" align="center">
166 166 <span class="contest-over-msg">ANALYSIS MODE</span>
167 167 </td></tr>
168 168 ANALYSISMODE
169 169 end
170 170
171 171 contest_name = GraderConfiguration['contest.name']
172 172
173 173 #
174 174 # build real title bar
175 175 result = <<TITLEBAR
176 176 <div class="title">
177 177 <table>
178 178 #{header}
179 179 <tr>
180 180 <td class="left-col">
181 181 #{user.full_name}<br/>
182 182 #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
183 183 #{time_left}
184 184 <br/>
185 185 </td>
186 186 <td class="right-col">#{contest_name}</td>
187 187 </tr>
188 188 </table>
189 189 </div>
190 190 TITLEBAR
191 191 result.html_safe
192 192 end
193 193
194 194 def markdown(text)
195 195 markdown = RDiscount.new(text)
196 196 markdown.to_html.html_safe
197 197 end
198 198
199 199
200 200 BOOTSTRAP_FLASH_MSG = {
201 201 success: 'alert-success',
202 202 error: 'alert-danger',
203 - alert: 'alert-block',
203 + alert: 'alert-danger',
204 204 notice: 'alert-info'
205 205 }
206 206
207 207 def bootstrap_class_for(flash_type)
208 208 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
209 209 end
210 210
211 211 def flash_messages
212 212 flash.each do |msg_type, message|
213 213 concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do
214 214 concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' })
215 215 concat message
216 216 end)
217 217 end
218 218 nil
219 219 end
220 220
221 221 end
@@ -1,5 +1,13
1 1 class Group < ActiveRecord::Base
2 - has_and_belongs_to_many :problems
3 - has_and_belongs_to_many :users
2 + has_many :groups_problems, class_name: GroupProblem
3 + has_many :problems, :through => :groups_problems
4 +
5 + has_many :groups_users, class_name: GroupUser
6 + has_many :users, :through => :groups_users
7 +
8 + #has_and_belongs_to_many :problems
9 + #has_and_belongs_to_many :users
10 +
11 +
4 12 end
5 13
@@ -1,101 +1,105
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_and_belongs_to_many :groups
7 + has_many :groups_problems, class_name: GroupProblem
8 + has_many :groups, :through => :groups_problems
9 +
6 10 has_many :test_pairs, :dependent => :delete_all
7 11 has_many :testcases, :dependent => :destroy
8 12
9 13 validates_presence_of :name
10 14 validates_format_of :name, :with => /\A\w+\z/
11 15 validates_presence_of :full_name
12 16
13 17 scope :available, -> { where(available: true) }
14 18
15 19 DEFAULT_TIME_LIMIT = 1
16 20 DEFAULT_MEMORY_LIMIT = 32
17 21
18 22 def self.available_problems
19 23 available.order(date_added: :desc).order(:name)
20 24 #Problem.available.all(:order => "date_added DESC, name ASC")
21 25 end
22 26
23 27 def self.create_from_import_form_params(params, old_problem=nil)
24 28 org_problem = old_problem || Problem.new
25 29 import_params, problem = Problem.extract_params_and_check(params,
26 30 org_problem)
27 31
28 32 if !problem.errors.empty?
29 33 return problem, 'Error importing'
30 34 end
31 35
32 36 problem.full_score = 100
33 37 problem.date_added = Time.new
34 38 problem.test_allowed = true
35 39 problem.output_only = false
36 40 problem.available = false
37 41
38 42 if not problem.save
39 43 return problem, 'Error importing'
40 44 end
41 45
42 46 import_to_db = params.has_key? :import_to_db
43 47
44 48 importer = TestdataImporter.new(problem)
45 49
46 50 if not importer.import_from_file(import_params[:file],
47 51 import_params[:time_limit],
48 52 import_params[:memory_limit],
49 53 import_params[:checker_name],
50 54 import_to_db)
51 55 problem.errors.add(:base,'Import error.')
52 56 end
53 57
54 58 return problem, importer.log_msg
55 59 end
56 60
57 61 def self.download_file_basedir
58 62 return "#{Rails.root}/data/tasks"
59 63 end
60 64
61 65 def get_submission_stat
62 66 result = Hash.new
63 67 #total number of submission
64 68 result[:total_sub] = Submission.where(problem_id: self.id).count
65 69 result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
66 70 result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
67 71 return result
68 72 end
69 73
70 74 def long_name
71 75 "[#{name}] #{full_name}"
72 76 end
73 77
74 78 protected
75 79
76 80 def self.to_i_or_default(st, default)
77 81 if st!=''
78 82 result = st.to_i
79 83 end
80 84 result ||= default
81 85 end
82 86
83 87 def self.to_f_or_default(st, default)
84 88 if st!=''
85 89 result = st.to_f
86 90 end
87 91 result ||= default
88 92 end
89 93
90 94 def self.extract_params_and_check(params, problem)
91 95 time_limit = Problem.to_f_or_default(params[:time_limit],
92 96 DEFAULT_TIME_LIMIT)
93 97 memory_limit = Problem.to_i_or_default(params[:memory_limit],
94 98 DEFAULT_MEMORY_LIMIT)
95 99
96 100 if time_limit<=0 or time_limit >60
97 101 problem.errors.add(:base,'Time limit out of range.')
98 102 end
99 103
100 104 if memory_limit==0 and params[:memory_limit]!='0'
101 105 problem.errors.add(:base,'Memory limit format errors.')
@@ -1,106 +1,109
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 + #has_and_belongs_to_many :groups
12 + has_many :groups_users, class_name: GroupUser
13 + has_many :groups, :through => :groups_users
11 14
12 15 has_many :test_requests, -> {order(submitted_at: DESC)}
13 16
14 17 has_many :messages, -> { order(created_at: DESC) },
15 18 :class_name => "Message",
16 19 :foreign_key => "sender_id"
17 20
18 21 has_many :replied_messages, -> { order(created_at: DESC) },
19 22 :class_name => "Message",
20 23 :foreign_key => "receiver_id"
21 24
22 25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
23 26
24 27 belongs_to :site
25 28 belongs_to :country
26 29
27 30 has_and_belongs_to_many :contests, -> { order(:name); uniq}
28 31
29 32 scope :activated_users, -> {where activated: true}
30 33
31 34 validates_presence_of :login
32 35 validates_uniqueness_of :login
33 36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
34 37 validates_length_of :login, :within => 3..30
35 38
36 39 validates_presence_of :full_name
37 40 validates_length_of :full_name, :minimum => 1
38 41
39 42 validates_presence_of :password, :if => :password_required?
40 43 validates_length_of :password, :within => 4..20, :if => :password_required?
41 44 validates_confirmation_of :password, :if => :password_required?
42 45
43 46 validates_format_of :email,
44 47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
45 48 :if => :email_validation?
46 49 validate :uniqueness_of_email_from_activated_users,
47 50 :if => :email_validation?
48 51 validate :enough_time_interval_between_same_email_registrations,
49 52 :if => :email_validation?
50 53
51 54 # these are for ytopc
52 55 # disable for now
53 56 #validates_presence_of :province
54 57
55 58 attr_accessor :password
56 59
57 60 before_save :encrypt_new_password
58 61 before_save :assign_default_site
59 62 before_save :assign_default_contest
60 63
61 64 # this is for will_paginate
62 65 cattr_reader :per_page
63 66 @@per_page = 50
64 67
65 68 def self.authenticate(login, password)
66 69 user = find_by_login(login)
67 70 if user
68 71 return user if user.authenticated?(password)
69 72 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
70 73 user.password = password
71 74 user.save
72 75 return user
73 76 end
74 77 end
75 78 end
76 79
77 80 def authenticated?(password)
78 81 if self.activated
79 82 hashed_password == User.encrypt(password,self.salt)
80 83 else
81 84 false
82 85 end
83 86 end
84 87
85 88 def authenticated_by_pop3?(password)
86 89 Net::POP3.enable_ssl
87 90 pop = Net::POP3.new('pops.it.chula.ac.th')
88 91 authen = true
89 92 begin
90 93 pop.start(login, password)
91 94 pop.finish
92 95 return true
93 96 rescue
94 97 return false
95 98 end
96 99 end
97 100
98 101 def authenticated_by_cucas?(password)
99 102 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
100 103 appid = '41508763e340d5858c00f8c1a0f5a2bb'
101 104 appsecret ='d9cbb5863091dbe186fded85722a1e31'
102 105 post_args = {
103 106 'appid' => appid,
104 107 'appsecret' => appsecret,
105 108 'username' => login,
106 109 'password' => password
@@ -1,54 +1,54
1 1 - content_for :head do
2 2 = stylesheet_link_tag 'problems'
3 - %h1 Listing problems
3 + %h1 Problems
4 4 %p
5 - = link_to 'New problem', new_problem_path, class: 'btn btn-default btn-sm'
6 - = link_to 'Manage problems', { action: 'manage'}, class: 'btn btn-default btn-sm'
7 - = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-default btn-sm'
5 + = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm'
6 + = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm'
7 + = link_to 'Bulk Manage', { action: 'manage'}, class: 'btn btn-info btn-sm'
8 8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm'
9 9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm'
10 10 .submitbox
11 11 = form_tag :action => 'quick_create' do
12 12 %b Quick New:
13 13 %label{:for => "problem_name"} Name
14 14 = text_field 'problem', 'name'
15 15 |
16 16 %label{:for => "problem_full_name"} Full name
17 17 = text_field 'problem', 'full_name'
18 18 = submit_tag "Create"
19 19 %table.table.table-condense.table-hover
20 20 %thead
21 21 %th Name
22 22 %th Full name
23 23 %th.text-right Full score
24 24 %th Date added
25 25 %th.text-center
26 26 Avail?
27 27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
28 28 %th.text-center
29 29 View Data?
30 30 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
31 31 %th.text-center
32 32 Test?
33 33 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
34 34 - if GraderConfiguration.multicontests?
35 35 %th Contests
36 36 - for problem in @problems
37 37 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
38 38 - @problem=problem
39 39 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
40 40 %td= problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
41 41 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
42 42 %td= problem.date_added
43 43 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
44 44 %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
45 45 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
46 46 - if GraderConfiguration.multicontests?
47 47 %td
48 48 = problem.contests.collect { |c| c.name }.join(', ')
49 49 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
50 50 %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
51 51 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
52 52 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
53 53 %br/
54 54 = link_to '[New problem]', :action => 'new'
@@ -1,90 +1,94
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 - %p= link_to '[Back to problem list]', :action => 'list'
39 + %p= link_to '[Back to problem list]', problems_path
40 40
41 41 = form_tag :action=>'do_manage' do
42 - .submitbox.panel
42 + .panel.panel-primary
43 + .panel-heading
44 + Action
45 + .panel-body
46 + .submit-box
43 47 What do you want to do to the selected problem?
44 48 %br/
45 49 (You can shift-click to select a range of problems)
46 50 %ul
47 51 %li
48 52 Change date added to
49 53 = select_date Date.current, :prefix => 'date_added'
50 54 &nbsp;&nbsp;&nbsp;
51 - = submit_tag 'Change', :name => 'change_date_added'
55 + = submit_tag 'Change', :name => 'change_date_added', class: 'btn btn-default'
52 56 %li
53 57 Set available to
54 - = submit_tag 'True', :name => 'enable_problem'
55 - = submit_tag 'False', :name => 'disable_problem'
58 + = submit_tag 'True', :name => 'enable_problem', class: 'btn btn-default'
59 + = submit_tag 'False', :name => 'disable_problem', class: 'btn btn-default'
56 60
57 61 - if GraderConfiguration.multicontests?
58 62 %li
59 63 Add to
60 64 = select("contest","id",Contest.all.collect {|c| [c.title, c.id]})
61 - = submit_tag 'Add', :name => 'add_to_contest'
65 + = submit_tag 'Add', :name => 'add_to_contest', class: 'btn btn-default'
62 66 %li
63 67 Add problems to group
64 68 = 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'
69 + = submit_tag 'Add', name: 'add_group', class: 'btn btn-default'
66 70
67 71
68 72 %table.table.table-hover
69 73 %tr{style: "text-align: left;"}
70 74 %th= check_box_tag 'select_all'
71 75 %th Name
72 76 %th Full name
73 77 %th Available
74 78 %th Date added
75 79 - if GraderConfiguration.multicontests?
76 80 %th Contests
77 81
78 82 - num = 0
79 83 - for problem in @problems
80 84 - num += 1
81 85 %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"}
82 86 %td= check_box_tag "prob-#{problem.id}-#{num}"
83 87 %td= problem.name
84 88 %td= problem.full_name
85 89 %td= problem.available
86 90 %td= problem.date_added
87 91 - if GraderConfiguration.multicontests?
88 92 %td
89 93 - problem.contests.each do |contest|
90 94 = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])"
You need to be logged in to leave comments. Login now