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: 126 inserted, 41 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
@@ -7,80 +7,88
7
7
8 # GET /groups
8 # GET /groups
9 def index
9 def index
10 @groups = Group.all
10 @groups = Group.all
11 end
11 end
12
12
13 # GET /groups/1
13 # GET /groups/1
14 def show
14 def show
15 end
15 end
16
16
17 # GET /groups/new
17 # GET /groups/new
18 def new
18 def new
19 @group = Group.new
19 @group = Group.new
20 end
20 end
21
21
22 # GET /groups/1/edit
22 # GET /groups/1/edit
23 def edit
23 def edit
24 end
24 end
25
25
26 # POST /groups
26 # POST /groups
27 def create
27 def create
28 @group = Group.new(group_params)
28 @group = Group.new(group_params)
29
29
30 if @group.save
30 if @group.save
31 redirect_to @group, notice: 'Group was successfully created.'
31 redirect_to @group, notice: 'Group was successfully created.'
32 else
32 else
33 render :new
33 render :new
34 end
34 end
35 end
35 end
36
36
37 # PATCH/PUT /groups/1
37 # PATCH/PUT /groups/1
38 def update
38 def update
39 if @group.update(group_params)
39 if @group.update(group_params)
40 redirect_to @group, notice: 'Group was successfully updated.'
40 redirect_to @group, notice: 'Group was successfully updated.'
41 else
41 else
42 render :edit
42 render :edit
43 end
43 end
44 end
44 end
45
45
46 # DELETE /groups/1
46 # DELETE /groups/1
47 def destroy
47 def destroy
48 @group.destroy
48 @group.destroy
49 redirect_to groups_url, notice: 'Group was successfully destroyed.'
49 redirect_to groups_url, notice: 'Group was successfully destroyed.'
50 end
50 end
51
51
52 def remove_user
52 def remove_user
53 user = User.find(params[:user_id])
53 user = User.find(params[:user_id])
54 @group.users.delete(user)
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 end
56 end
57
57
58 def add_user
58 def add_user
59 user = User.find(params[:user_id])
59 user = User.find(params[:user_id])
60 - @group.users << user
60 + begin
61 - redirect_to group_path(@group), notice: "User #{user.login} was add to the group #{@group.name}"
61 + @group.users << user
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 end
66 end
63
67
64 def remove_problem
68 def remove_problem
65 problem = Problem.find(params[:problem_id])
69 problem = Problem.find(params[:problem_id])
66 @group.problems.delete(problem)
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 end
72 end
69
73
70 def add_problem
74 def add_problem
71 problem = Problem.find(params[:problem_id])
75 problem = Problem.find(params[:problem_id])
72 - @group.problems << problem
76 + begin
73 - redirect_to group_path(@group), notice: "Problem #{problem.name} was add to the group #{@group.name}"
77 + @group.problems << problem
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 end
82 end
75
83
76 private
84 private
77 # Use callbacks to share common setup or constraints between actions.
85 # Use callbacks to share common setup or constraints between actions.
78 def set_group
86 def set_group
79 @group = Group.find(params[:id])
87 @group = Group.find(params[:id])
80 end
88 end
81
89
82 # Only allow a trusted parameter "white list" through.
90 # Only allow a trusted parameter "white list" through.
83 def group_params
91 def group_params
84 params.require(:group).permit(:name, :description)
92 params.require(:group).permit(:name, :description)
85 end
93 end
86 end
94 end
@@ -152,100 +152,110
152 end
152 end
153
153
154 def turn_all_on
154 def turn_all_on
155 Problem.where.not(available: true).each do |problem|
155 Problem.where.not(available: true).each do |problem|
156 problem.available = true
156 problem.available = true
157 problem.save
157 problem.save
158 end
158 end
159 redirect_to action: :index
159 redirect_to action: :index
160 end
160 end
161
161
162 def stat
162 def stat
163 @problem = Problem.find(params[:id])
163 @problem = Problem.find(params[:id])
164 unless @problem.available or session[:admin]
164 unless @problem.available or session[:admin]
165 redirect_to :controller => 'main', :action => 'list'
165 redirect_to :controller => 'main', :action => 'list'
166 return
166 return
167 end
167 end
168 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
168 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
169
169
170 #stat summary
170 #stat summary
171 range =65
171 range =65
172 @histogram = { data: Array.new(range,0), summary: {} }
172 @histogram = { data: Array.new(range,0), summary: {} }
173 user = Hash.new(0)
173 user = Hash.new(0)
174 @submissions.find_each do |sub|
174 @submissions.find_each do |sub|
175 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
175 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
176 @histogram[:data][d.to_i] += 1 if d < range
176 @histogram[:data][d.to_i] += 1 if d < range
177 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
177 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
178 end
178 end
179 @histogram[:summary][:max] = [@histogram[:data].max,1].max
179 @histogram[:summary][:max] = [@histogram[:data].max,1].max
180
180
181 @summary = { attempt: user.count, solve: 0 }
181 @summary = { attempt: user.count, solve: 0 }
182 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
182 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
183 end
183 end
184
184
185 def manage
185 def manage
186 @problems = Problem.order(date_added: :desc)
186 @problems = Problem.order(date_added: :desc)
187 end
187 end
188
188
189 def do_manage
189 def do_manage
190 if params.has_key? 'change_date_added'
190 if params.has_key? 'change_date_added'
191 change_date_added
191 change_date_added
192 elsif params.has_key? 'add_to_contest'
192 elsif params.has_key? 'add_to_contest'
193 add_to_contest
193 add_to_contest
194 elsif params.has_key? 'enable_problem'
194 elsif params.has_key? 'enable_problem'
195 set_available(true)
195 set_available(true)
196 elsif params.has_key? 'disable_problem'
196 elsif params.has_key? 'disable_problem'
197 set_available(false)
197 set_available(false)
198 elsif params.has_key? 'add_group'
198 elsif params.has_key? 'add_group'
199 group = Group.find(params[:group_id])
199 group = Group.find(params[:group_id])
200 + ok = []
201 + failed = []
200 get_problems_from_params.each do |p|
202 get_problems_from_params.each do |p|
201 - group.problems << p
203 + begin
204 + group.problems << p
205 + ok << p.full_name
206 + rescue => e
207 + failed << p.full_name
208 + end
202 end
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
203 end
212 end
213 +
204 redirect_to :action => 'manage'
214 redirect_to :action => 'manage'
205 end
215 end
206
216
207 def import
217 def import
208 @allow_test_pair_import = allow_test_pair_import?
218 @allow_test_pair_import = allow_test_pair_import?
209 end
219 end
210
220
211 def do_import
221 def do_import
212 old_problem = Problem.find_by_name(params[:name])
222 old_problem = Problem.find_by_name(params[:name])
213 if !allow_test_pair_import? and params.has_key? :import_to_db
223 if !allow_test_pair_import? and params.has_key? :import_to_db
214 params.delete :import_to_db
224 params.delete :import_to_db
215 end
225 end
216 @problem, import_log = Problem.create_from_import_form_params(params,
226 @problem, import_log = Problem.create_from_import_form_params(params,
217 old_problem)
227 old_problem)
218
228
219 if !@problem.errors.empty?
229 if !@problem.errors.empty?
220 render :action => 'import' and return
230 render :action => 'import' and return
221 end
231 end
222
232
223 if old_problem!=nil
233 if old_problem!=nil
224 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
234 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
225 end
235 end
226 @log = import_log
236 @log = import_log
227 end
237 end
228
238
229 def remove_contest
239 def remove_contest
230 problem = Problem.find(params[:id])
240 problem = Problem.find(params[:id])
231 contest = Contest.find(params[:contest_id])
241 contest = Contest.find(params[:contest_id])
232 if problem!=nil and contest!=nil
242 if problem!=nil and contest!=nil
233 problem.contests.delete(contest)
243 problem.contests.delete(contest)
234 end
244 end
235 redirect_to :action => 'manage'
245 redirect_to :action => 'manage'
236 end
246 end
237
247
238 ##################################
248 ##################################
239 protected
249 protected
240
250
241 def allow_test_pair_import?
251 def allow_test_pair_import?
242 if defined? ALLOW_TEST_PAIR_IMPORT
252 if defined? ALLOW_TEST_PAIR_IMPORT
243 return ALLOW_TEST_PAIR_IMPORT
253 return ALLOW_TEST_PAIR_IMPORT
244 else
254 else
245 return false
255 return false
246 end
256 end
247 end
257 end
248
258
249 def change_date_added
259 def change_date_added
250 problems = get_problems_from_params
260 problems = get_problems_from_params
251 year = params[:date_added][:year].to_i
261 year = params[:date_added][:year].to_i
@@ -397,97 +397,108
397 users = []
397 users = []
398 lines.split("\n").each do |line|
398 lines.split("\n").each do |line|
399 user = User.find_by_login(line.chomp)
399 user = User.find_by_login(line.chomp)
400 if user
400 if user
401 send_mail(user.email, mail_subject, mail_body)
401 send_mail(user.email, mail_subject, mail_body)
402 note << user.login
402 note << user.login
403 end
403 end
404 end
404 end
405
405
406 flash[:notice] = 'User(s) ' + note.join(', ') +
406 flash[:notice] = 'User(s) ' + note.join(', ') +
407 ' were successfully modified. '
407 ' were successfully modified. '
408 redirect_to :action => 'mass_mailing'
408 redirect_to :action => 'mass_mailing'
409 end
409 end
410
410
411 #bulk manage
411 #bulk manage
412 def bulk_manage
412 def bulk_manage
413
413
414 begin
414 begin
415 @users = User.where('(login REGEXP ?) OR (remark REGEXP ?)',params[:regex],params[:regex]) if params[:regex]
415 @users = User.where('(login REGEXP ?) OR (remark REGEXP ?)',params[:regex],params[:regex]) if params[:regex]
416 @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised
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 rescue Exception
417 rescue Exception
418 flash[:error] = 'Regular Expression is malformed'
418 flash[:error] = 'Regular Expression is malformed'
419 @users = nil
419 @users = nil
420 end
420 end
421
421
422 if params[:commit]
422 if params[:commit]
423 @action = {}
423 @action = {}
424 @action[:set_enable] = params[:enabled]
424 @action[:set_enable] = params[:enabled]
425 @action[:enabled] = params[:enable] == "1"
425 @action[:enabled] = params[:enable] == "1"
426 @action[:gen_password] = params[:gen_password]
426 @action[:gen_password] = params[:gen_password]
427 @action[:add_group] = params[:add_group]
427 @action[:add_group] = params[:add_group]
428 @action[:group_name] = params[:group_name]
428 @action[:group_name] = params[:group_name]
429 end
429 end
430
430
431 if params[:commit] == "Perform"
431 if params[:commit] == "Perform"
432 if @action[:set_enable]
432 if @action[:set_enable]
433 @users.update_all(enabled: @action[:enabled])
433 @users.update_all(enabled: @action[:enabled])
434 end
434 end
435 if @action[:gen_password]
435 if @action[:gen_password]
436 @users.each do |u|
436 @users.each do |u|
437 password = random_password
437 password = random_password
438 u.password = password
438 u.password = password
439 u.password_confirmation = password
439 u.password_confirmation = password
440 u.save
440 u.save
441 end
441 end
442 end
442 end
443 if @action[:add_group] and @action[:group_name]
443 if @action[:add_group] and @action[:group_name]
444 @group = Group.find(@action[:group_name])
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 end
457 end
447 end
458 end
448 end
459 end
449
460
450 protected
461 protected
451
462
452 def random_password(length=5)
463 def random_password(length=5)
453 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
464 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
454 newpass = ""
465 newpass = ""
455 length.times { newpass << chars[rand(chars.size-1)] }
466 length.times { newpass << chars[rand(chars.size-1)] }
456 return newpass
467 return newpass
457 end
468 end
458
469
459 def import_from_file(f)
470 def import_from_file(f)
460 data_hash = YAML.load(f)
471 data_hash = YAML.load(f)
461 @import_log = ""
472 @import_log = ""
462
473
463 country_data = data_hash[:countries]
474 country_data = data_hash[:countries]
464 site_data = data_hash[:sites]
475 site_data = data_hash[:sites]
465 user_data = data_hash[:users]
476 user_data = data_hash[:users]
466
477
467 # import country
478 # import country
468 countries = {}
479 countries = {}
469 country_data.each_pair do |id,country|
480 country_data.each_pair do |id,country|
470 c = Country.find_by_name(country[:name])
481 c = Country.find_by_name(country[:name])
471 if c!=nil
482 if c!=nil
472 countries[id] = c
483 countries[id] = c
473 @import_log << "Found #{country[:name]}\n"
484 @import_log << "Found #{country[:name]}\n"
474 else
485 else
475 countries[id] = Country.new(:name => country[:name])
486 countries[id] = Country.new(:name => country[:name])
476 countries[id].save
487 countries[id].save
477 @import_log << "Created #{country[:name]}\n"
488 @import_log << "Created #{country[:name]}\n"
478 end
489 end
479 end
490 end
480
491
481 # import sites
492 # import sites
482 sites = {}
493 sites = {}
483 site_data.each_pair do |id,site|
494 site_data.each_pair do |id,site|
484 s = Site.find_by_name(site[:name])
495 s = Site.find_by_name(site[:name])
485 if s!=nil
496 if s!=nil
486 @import_log << "Found #{site[:name]}\n"
497 @import_log << "Found #{site[:name]}\n"
487 else
498 else
488 s = Site.new(:name => site[:name])
499 s = Site.new(:name => site[:name])
489 @import_log << "Created #{site[:name]}\n"
500 @import_log << "Created #{site[:name]}\n"
490 end
501 end
491 s.password = site[:password]
502 s.password = site[:password]
492 s.country = countries[site[:country_id]]
503 s.country = countries[site[:country_id]]
493 s.save
504 s.save
@@ -155,67 +155,67
155 else
155 else
156 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
156 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
157 " #{format_short_duration(user.contest_time_left)}"
157 " #{format_short_duration(user.contest_time_left)}"
158 end
158 end
159 end
159 end
160
160
161 #
161 #
162 # if the contest is in the anaysis mode
162 # if the contest is in the anaysis mode
163 if GraderConfiguration.analysis_mode?
163 if GraderConfiguration.analysis_mode?
164 header = <<ANALYSISMODE
164 header = <<ANALYSISMODE
165 <tr><td colspan="2" align="center">
165 <tr><td colspan="2" align="center">
166 <span class="contest-over-msg">ANALYSIS MODE</span>
166 <span class="contest-over-msg">ANALYSIS MODE</span>
167 </td></tr>
167 </td></tr>
168 ANALYSISMODE
168 ANALYSISMODE
169 end
169 end
170
170
171 contest_name = GraderConfiguration['contest.name']
171 contest_name = GraderConfiguration['contest.name']
172
172
173 #
173 #
174 # build real title bar
174 # build real title bar
175 result = <<TITLEBAR
175 result = <<TITLEBAR
176 <div class="title">
176 <div class="title">
177 <table>
177 <table>
178 #{header}
178 #{header}
179 <tr>
179 <tr>
180 <td class="left-col">
180 <td class="left-col">
181 #{user.full_name}<br/>
181 #{user.full_name}<br/>
182 #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
182 #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
183 #{time_left}
183 #{time_left}
184 <br/>
184 <br/>
185 </td>
185 </td>
186 <td class="right-col">#{contest_name}</td>
186 <td class="right-col">#{contest_name}</td>
187 </tr>
187 </tr>
188 </table>
188 </table>
189 </div>
189 </div>
190 TITLEBAR
190 TITLEBAR
191 result.html_safe
191 result.html_safe
192 end
192 end
193
193
194 def markdown(text)
194 def markdown(text)
195 markdown = RDiscount.new(text)
195 markdown = RDiscount.new(text)
196 markdown.to_html.html_safe
196 markdown.to_html.html_safe
197 end
197 end
198
198
199
199
200 BOOTSTRAP_FLASH_MSG = {
200 BOOTSTRAP_FLASH_MSG = {
201 success: 'alert-success',
201 success: 'alert-success',
202 error: 'alert-danger',
202 error: 'alert-danger',
203 - alert: 'alert-block',
203 + alert: 'alert-danger',
204 notice: 'alert-info'
204 notice: 'alert-info'
205 }
205 }
206
206
207 def bootstrap_class_for(flash_type)
207 def bootstrap_class_for(flash_type)
208 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
208 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
209 end
209 end
210
210
211 def flash_messages
211 def flash_messages
212 flash.each do |msg_type, message|
212 flash.each do |msg_type, message|
213 concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do
213 concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do
214 concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' })
214 concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' })
215 concat message
215 concat message
216 end)
216 end)
217 end
217 end
218 nil
218 nil
219 end
219 end
220
220
221 end
221 end
@@ -1,5 +1,13
1 class Group < ActiveRecord::Base
1 class Group < ActiveRecord::Base
2 - has_and_belongs_to_many :problems
2 + has_many :groups_problems, class_name: GroupProblem
3 - has_and_belongs_to_many :users
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 end
12 end
5
13
@@ -1,53 +1,57
1 class Problem < ActiveRecord::Base
1 class Problem < ActiveRecord::Base
2
2
3 belongs_to :description
3 belongs_to :description
4 has_and_belongs_to_many :contests, :uniq => true
4 has_and_belongs_to_many :contests, :uniq => true
5 - has_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 has_many :test_pairs, :dependent => :delete_all
10 has_many :test_pairs, :dependent => :delete_all
7 has_many :testcases, :dependent => :destroy
11 has_many :testcases, :dependent => :destroy
8
12
9 validates_presence_of :name
13 validates_presence_of :name
10 validates_format_of :name, :with => /\A\w+\z/
14 validates_format_of :name, :with => /\A\w+\z/
11 validates_presence_of :full_name
15 validates_presence_of :full_name
12
16
13 scope :available, -> { where(available: true) }
17 scope :available, -> { where(available: true) }
14
18
15 DEFAULT_TIME_LIMIT = 1
19 DEFAULT_TIME_LIMIT = 1
16 DEFAULT_MEMORY_LIMIT = 32
20 DEFAULT_MEMORY_LIMIT = 32
17
21
18 def self.available_problems
22 def self.available_problems
19 available.order(date_added: :desc).order(:name)
23 available.order(date_added: :desc).order(:name)
20 #Problem.available.all(:order => "date_added DESC, name ASC")
24 #Problem.available.all(:order => "date_added DESC, name ASC")
21 end
25 end
22
26
23 def self.create_from_import_form_params(params, old_problem=nil)
27 def self.create_from_import_form_params(params, old_problem=nil)
24 org_problem = old_problem || Problem.new
28 org_problem = old_problem || Problem.new
25 import_params, problem = Problem.extract_params_and_check(params,
29 import_params, problem = Problem.extract_params_and_check(params,
26 org_problem)
30 org_problem)
27
31
28 if !problem.errors.empty?
32 if !problem.errors.empty?
29 return problem, 'Error importing'
33 return problem, 'Error importing'
30 end
34 end
31
35
32 problem.full_score = 100
36 problem.full_score = 100
33 problem.date_added = Time.new
37 problem.date_added = Time.new
34 problem.test_allowed = true
38 problem.test_allowed = true
35 problem.output_only = false
39 problem.output_only = false
36 problem.available = false
40 problem.available = false
37
41
38 if not problem.save
42 if not problem.save
39 return problem, 'Error importing'
43 return problem, 'Error importing'
40 end
44 end
41
45
42 import_to_db = params.has_key? :import_to_db
46 import_to_db = params.has_key? :import_to_db
43
47
44 importer = TestdataImporter.new(problem)
48 importer = TestdataImporter.new(problem)
45
49
46 if not importer.import_from_file(import_params[:file],
50 if not importer.import_from_file(import_params[:file],
47 import_params[:time_limit],
51 import_params[:time_limit],
48 import_params[:memory_limit],
52 import_params[:memory_limit],
49 import_params[:checker_name],
53 import_params[:checker_name],
50 import_to_db)
54 import_to_db)
51 problem.errors.add(:base,'Import error.')
55 problem.errors.add(:base,'Import error.')
52 end
56 end
53
57
@@ -1,58 +1,61
1 require 'digest/sha1'
1 require 'digest/sha1'
2 require 'net/pop'
2 require 'net/pop'
3 require 'net/https'
3 require 'net/https'
4 require 'net/http'
4 require 'net/http'
5 require 'json'
5 require 'json'
6
6
7 class User < ActiveRecord::Base
7 class User < ActiveRecord::Base
8
8
9 has_and_belongs_to_many :roles
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 has_many :test_requests, -> {order(submitted_at: DESC)}
15 has_many :test_requests, -> {order(submitted_at: DESC)}
13
16
14 has_many :messages, -> { order(created_at: DESC) },
17 has_many :messages, -> { order(created_at: DESC) },
15 :class_name => "Message",
18 :class_name => "Message",
16 :foreign_key => "sender_id"
19 :foreign_key => "sender_id"
17
20
18 has_many :replied_messages, -> { order(created_at: DESC) },
21 has_many :replied_messages, -> { order(created_at: DESC) },
19 :class_name => "Message",
22 :class_name => "Message",
20 :foreign_key => "receiver_id"
23 :foreign_key => "receiver_id"
21
24
22 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
23
26
24 belongs_to :site
27 belongs_to :site
25 belongs_to :country
28 belongs_to :country
26
29
27 has_and_belongs_to_many :contests, -> { order(:name); uniq}
30 has_and_belongs_to_many :contests, -> { order(:name); uniq}
28
31
29 scope :activated_users, -> {where activated: true}
32 scope :activated_users, -> {where activated: true}
30
33
31 validates_presence_of :login
34 validates_presence_of :login
32 validates_uniqueness_of :login
35 validates_uniqueness_of :login
33 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
34 validates_length_of :login, :within => 3..30
37 validates_length_of :login, :within => 3..30
35
38
36 validates_presence_of :full_name
39 validates_presence_of :full_name
37 validates_length_of :full_name, :minimum => 1
40 validates_length_of :full_name, :minimum => 1
38
41
39 validates_presence_of :password, :if => :password_required?
42 validates_presence_of :password, :if => :password_required?
40 validates_length_of :password, :within => 4..20, :if => :password_required?
43 validates_length_of :password, :within => 4..20, :if => :password_required?
41 validates_confirmation_of :password, :if => :password_required?
44 validates_confirmation_of :password, :if => :password_required?
42
45
43 validates_format_of :email,
46 validates_format_of :email,
44 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
45 :if => :email_validation?
48 :if => :email_validation?
46 validate :uniqueness_of_email_from_activated_users,
49 validate :uniqueness_of_email_from_activated_users,
47 :if => :email_validation?
50 :if => :email_validation?
48 validate :enough_time_interval_between_same_email_registrations,
51 validate :enough_time_interval_between_same_email_registrations,
49 :if => :email_validation?
52 :if => :email_validation?
50
53
51 # these are for ytopc
54 # these are for ytopc
52 # disable for now
55 # disable for now
53 #validates_presence_of :province
56 #validates_presence_of :province
54
57
55 attr_accessor :password
58 attr_accessor :password
56
59
57 before_save :encrypt_new_password
60 before_save :encrypt_new_password
58 before_save :assign_default_site
61 before_save :assign_default_site
@@ -1,54 +1,54
1 - content_for :head do
1 - content_for :head do
2 = stylesheet_link_tag 'problems'
2 = stylesheet_link_tag 'problems'
3 - %h1 Listing problems
3 + %h1 Problems
4 %p
4 %p
5 - = link_to 'New problem', new_problem_path, class: 'btn btn-default btn-sm'
5 + = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm'
6 - = link_to 'Manage problems', { action: 'manage'}, class: 'btn btn-default btn-sm'
6 + = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm'
7 - = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-default btn-sm'
7 + = link_to 'Bulk Manage', { action: 'manage'}, class: 'btn btn-info btn-sm'
8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm'
8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm'
9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm'
9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm'
10 .submitbox
10 .submitbox
11 = form_tag :action => 'quick_create' do
11 = form_tag :action => 'quick_create' do
12 %b Quick New:
12 %b Quick New:
13 %label{:for => "problem_name"} Name
13 %label{:for => "problem_name"} Name
14 = text_field 'problem', 'name'
14 = text_field 'problem', 'name'
15 |
15 |
16 %label{:for => "problem_full_name"} Full name
16 %label{:for => "problem_full_name"} Full name
17 = text_field 'problem', 'full_name'
17 = text_field 'problem', 'full_name'
18 = submit_tag "Create"
18 = submit_tag "Create"
19 %table.table.table-condense.table-hover
19 %table.table.table-condense.table-hover
20 %thead
20 %thead
21 %th Name
21 %th Name
22 %th Full name
22 %th Full name
23 %th.text-right Full score
23 %th.text-right Full score
24 %th Date added
24 %th Date added
25 %th.text-center
25 %th.text-center
26 Avail?
26 Avail?
27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
28 %th.text-center
28 %th.text-center
29 View Data?
29 View Data?
30 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
30 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
31 %th.text-center
31 %th.text-center
32 Test?
32 Test?
33 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
33 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
34 - if GraderConfiguration.multicontests?
34 - if GraderConfiguration.multicontests?
35 %th Contests
35 %th Contests
36 - for problem in @problems
36 - for problem in @problems
37 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
37 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
38 - @problem=problem
38 - @problem=problem
39 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
39 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
40 %td= problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
40 %td= problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
41 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
41 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
42 %td= problem.date_added
42 %td= problem.date_added
43 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
43 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
44 %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
44 %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
45 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
45 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
46 - if GraderConfiguration.multicontests?
46 - if GraderConfiguration.multicontests?
47 %td
47 %td
48 = problem.contests.collect { |c| c.name }.join(', ')
48 = problem.contests.collect { |c| c.name }.join(', ')
49 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
49 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
50 %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
50 %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
51 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
51 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
52 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
52 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
53 %br/
53 %br/
54 = link_to '[New problem]', :action => 'new'
54 = link_to '[New problem]', :action => 'new'
@@ -1,90 +1,94
1 - content_for :head do
1 - content_for :head do
2 = stylesheet_link_tag 'problems'
2 = stylesheet_link_tag 'problems'
3 = javascript_include_tag 'local_jquery'
3 = javascript_include_tag 'local_jquery'
4
4
5 :javascript
5 :javascript
6 $(document).ready( function() {
6 $(document).ready( function() {
7 function shiftclick(start,stop,value) {
7 function shiftclick(start,stop,value) {
8 $('tr input').each( function(id,input) {
8 $('tr input').each( function(id,input) {
9 var $input=$(input);
9 var $input=$(input);
10 var iid=parseInt($input.attr('id').split('-')[2]);
10 var iid=parseInt($input.attr('id').split('-')[2]);
11 if(iid>=start&&iid<=stop){
11 if(iid>=start&&iid<=stop){
12 $input.prop('checked',value)
12 $input.prop('checked',value)
13 }
13 }
14 });
14 });
15 }
15 }
16
16
17 $('tr input').click( function(e) {
17 $('tr input').click( function(e) {
18 if (e.shiftKey) {
18 if (e.shiftKey) {
19 stop = parseInt($(this).attr('id').split('-')[2]);
19 stop = parseInt($(this).attr('id').split('-')[2]);
20 var orig_stop = stop
20 var orig_stop = stop
21 if (typeof start !== 'undefined') {
21 if (typeof start !== 'undefined') {
22 if (start > stop) {
22 if (start > stop) {
23 var tmp = start;
23 var tmp = start;
24 start = stop;
24 start = stop;
25 stop = tmp;
25 stop = tmp;
26 }
26 }
27 shiftclick(start,stop,$(this).is(':checked') )
27 shiftclick(start,stop,$(this).is(':checked') )
28 }
28 }
29 start = orig_stop
29 start = orig_stop
30 } else {
30 } else {
31 start = parseInt($(this).attr('id').split('-')[2]);
31 start = parseInt($(this).attr('id').split('-')[2]);
32 }
32 }
33 });
33 });
34 });
34 });
35
35
36
36
37 %h1 Manage problems
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 = form_tag :action=>'do_manage' do
41 = form_tag :action=>'do_manage' do
42 - .submitbox.panel
42 + .panel.panel-primary
43 - What do you want to do to the selected problem?
43 + .panel-heading
44 - %br/
44 + Action
45 - (You can shift-click to select a range of problems)
45 + .panel-body
46 - %ul
46 + .submit-box
47 - %li
47 + What do you want to do to the selected problem?
48 - Change date added to
48 + %br/
49 - = select_date Date.current, :prefix => 'date_added'
49 + (You can shift-click to select a range of problems)
50 - &nbsp;&nbsp;&nbsp;
50 + %ul
51 - = submit_tag 'Change', :name => 'change_date_added'
51 + %li
52 - %li
52 + Change date added to
53 - Set available to
53 + = select_date Date.current, :prefix => 'date_added'
54 - = submit_tag 'True', :name => 'enable_problem'
54 + &nbsp;&nbsp;&nbsp;
55 - = submit_tag 'False', :name => 'disable_problem'
55 + = submit_tag 'Change', :name => 'change_date_added', class: 'btn btn-default'
56 + %li
57 + Set available to
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 - - if GraderConfiguration.multicontests?
61 + - if GraderConfiguration.multicontests?
58 - %li
62 + %li
59 - Add to
63 + Add to
60 - = select("contest","id",Contest.all.collect {|c| [c.title, c.id]})
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 - %li
66 + %li
63 - Add problems to group
67 + 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'
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 %table.table.table-hover
72 %table.table.table-hover
69 %tr{style: "text-align: left;"}
73 %tr{style: "text-align: left;"}
70 %th= check_box_tag 'select_all'
74 %th= check_box_tag 'select_all'
71 %th Name
75 %th Name
72 %th Full name
76 %th Full name
73 %th Available
77 %th Available
74 %th Date added
78 %th Date added
75 - if GraderConfiguration.multicontests?
79 - if GraderConfiguration.multicontests?
76 %th Contests
80 %th Contests
77
81
78 - num = 0
82 - num = 0
79 - for problem in @problems
83 - for problem in @problems
80 - num += 1
84 - num += 1
81 %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"}
85 %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"}
82 %td= check_box_tag "prob-#{problem.id}-#{num}"
86 %td= check_box_tag "prob-#{problem.id}-#{num}"
83 %td= problem.name
87 %td= problem.name
84 %td= problem.full_name
88 %td= problem.full_name
85 %td= problem.available
89 %td= problem.available
86 %td= problem.date_added
90 %td= problem.date_added
87 - if GraderConfiguration.multicontests?
91 - if GraderConfiguration.multicontests?
88 %td
92 %td
89 - problem.contests.each do |contest|
93 - problem.contests.each do |contest|
90 = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])"
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