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

r701:f26733606a76 - - 21 files changed: 132 inserted, 57 deleted

@@ -52,97 +52,97
52 def list
52 def list
53 prepare_list_information
53 prepare_list_information
54 end
54 end
55
55
56 def help
56 def help
57 @user = User.find(session[:user_id])
57 @user = User.find(session[:user_id])
58 end
58 end
59
59
60 def submit
60 def submit
61 user = User.find(session[:user_id])
61 user = User.find(session[:user_id])
62
62
63 @submission = Submission.new
63 @submission = Submission.new
64 @submission.problem_id = params[:submission][:problem_id]
64 @submission.problem_id = params[:submission][:problem_id]
65 @submission.user = user
65 @submission.user = user
66 @submission.language_id = 0
66 @submission.language_id = 0
67 if (params['file']) and (params['file']!='')
67 if (params['file']) and (params['file']!='')
68 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
68 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
69 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
69 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
70 @submission.source_filename = params['file'].original_filename
70 @submission.source_filename = params['file'].original_filename
71 end
71 end
72
72
73 if (params[:editor_text])
73 if (params[:editor_text])
74 language = Language.find_by_id(params[:language_id])
74 language = Language.find_by_id(params[:language_id])
75 @submission.source = params[:editor_text]
75 @submission.source = params[:editor_text]
76 @submission.source_filename = "live_edit.#{language.ext}"
76 @submission.source_filename = "live_edit.#{language.ext}"
77 @submission.language = language
77 @submission.language = language
78 end
78 end
79
79
80 @submission.submitted_at = Time.new.gmtime
80 @submission.submitted_at = Time.new.gmtime
81 @submission.ip_address = request.remote_ip
81 @submission.ip_address = request.remote_ip
82
82
83 if GraderConfiguration.time_limit_mode? and user.contest_finished?
83 if GraderConfiguration.time_limit_mode? and user.contest_finished?
84 @submission.errors.add(:base,"The contest is over.")
84 @submission.errors.add(:base,"The contest is over.")
85 prepare_list_information
85 prepare_list_information
86 render :action => 'list' and return
86 render :action => 'list' and return
87 end
87 end
88
88
89 if @submission.valid?(@current_user)
89 if @submission.valid?(@current_user)
90 if @submission.save == false
90 if @submission.save == false
91 flash[:notice] = 'Error saving your submission'
91 flash[:notice] = 'Error saving your submission'
92 elsif Task.create(:submission_id => @submission.id,
92 elsif Task.create(:submission_id => @submission.id,
93 :status => Task::STATUS_INQUEUE) == false
93 :status => Task::STATUS_INQUEUE) == false
94 flash[:notice] = 'Error adding your submission to task queue'
94 flash[:notice] = 'Error adding your submission to task queue'
95 end
95 end
96 else
96 else
97 prepare_list_information
97 prepare_list_information
98 render :action => 'list' and return
98 render :action => 'list' and return
99 end
99 end
100 - redirect_to :action => 'list'
100 + redirect_to edit_submission_path(@submission)
101 end
101 end
102
102
103 def source
103 def source
104 submission = Submission.find(params[:id])
104 submission = Submission.find(params[:id])
105 if ((submission.user_id == session[:user_id]) and
105 if ((submission.user_id == session[:user_id]) and
106 (submission.problem != nil) and
106 (submission.problem != nil) and
107 (submission.problem.available))
107 (submission.problem.available))
108 send_data(submission.source,
108 send_data(submission.source,
109 {:filename => submission.download_filename,
109 {:filename => submission.download_filename,
110 :type => 'text/plain'})
110 :type => 'text/plain'})
111 else
111 else
112 flash[:notice] = 'Error viewing source'
112 flash[:notice] = 'Error viewing source'
113 redirect_to :action => 'list'
113 redirect_to :action => 'list'
114 end
114 end
115 end
115 end
116
116
117 def compiler_msg
117 def compiler_msg
118 @submission = Submission.find(params[:id])
118 @submission = Submission.find(params[:id])
119 if @submission.user_id == session[:user_id]
119 if @submission.user_id == session[:user_id]
120 render :action => 'compiler_msg', :layout => 'empty'
120 render :action => 'compiler_msg', :layout => 'empty'
121 else
121 else
122 flash[:notice] = 'Error viewing source'
122 flash[:notice] = 'Error viewing source'
123 redirect_to :action => 'list'
123 redirect_to :action => 'list'
124 end
124 end
125 end
125 end
126
126
127 def result
127 def result
128 if !GraderConfiguration.show_grading_result
128 if !GraderConfiguration.show_grading_result
129 redirect_to :action => 'list' and return
129 redirect_to :action => 'list' and return
130 end
130 end
131 @user = User.find(session[:user_id])
131 @user = User.find(session[:user_id])
132 @submission = Submission.find(params[:id])
132 @submission = Submission.find(params[:id])
133 if @submission.user!=@user
133 if @submission.user!=@user
134 flash[:notice] = 'You are not allowed to view result of other users.'
134 flash[:notice] = 'You are not allowed to view result of other users.'
135 redirect_to :action => 'list' and return
135 redirect_to :action => 'list' and return
136 end
136 end
137 prepare_grading_result(@submission)
137 prepare_grading_result(@submission)
138 end
138 end
139
139
140 def load_output
140 def load_output
141 if !GraderConfiguration.show_grading_result or params[:num]==nil
141 if !GraderConfiguration.show_grading_result or params[:num]==nil
142 redirect_to :action => 'list' and return
142 redirect_to :action => 'list' and return
143 end
143 end
144 @user = User.find(session[:user_id])
144 @user = User.find(session[:user_id])
145 @submission = Submission.find(params[:id])
145 @submission = Submission.find(params[:id])
146 if @submission.user!=@user
146 if @submission.user!=@user
147 flash[:notice] = 'You are not allowed to view result of other users.'
147 flash[:notice] = 'You are not allowed to view result of other users.'
148 redirect_to :action => 'list' and return
148 redirect_to :action => 'list' and return
@@ -120,187 +120,191
120 end
120 end
121
121
122 def toggle
122 def toggle
123 @problem = Problem.find(params[:id])
123 @problem = Problem.find(params[:id])
124 @problem.update_attributes(available: !(@problem.available) )
124 @problem.update_attributes(available: !(@problem.available) )
125 respond_to do |format|
125 respond_to do |format|
126 format.js { }
126 format.js { }
127 end
127 end
128 end
128 end
129
129
130 def toggle_test
130 def toggle_test
131 @problem = Problem.find(params[:id])
131 @problem = Problem.find(params[:id])
132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
133 respond_to do |format|
133 respond_to do |format|
134 format.js { }
134 format.js { }
135 end
135 end
136 end
136 end
137
137
138 def toggle_view_testcase
138 def toggle_view_testcase
139 @problem = Problem.find(params[:id])
139 @problem = Problem.find(params[:id])
140 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
140 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
141 respond_to do |format|
141 respond_to do |format|
142 format.js { }
142 format.js { }
143 end
143 end
144 end
144 end
145
145
146 def turn_all_off
146 def turn_all_off
147 Problem.available.all.each do |problem|
147 Problem.available.all.each do |problem|
148 problem.available = false
148 problem.available = false
149 problem.save
149 problem.save
150 end
150 end
151 redirect_to action: :index
151 redirect_to action: :index
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).includes(:language).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 = []
200 ok = []
201 failed = []
201 failed = []
202 get_problems_from_params.each do |p|
202 get_problems_from_params.each do |p|
203 begin
203 begin
204 group.problems << p
204 group.problems << p
205 ok << p.full_name
205 ok << p.full_name
206 rescue => e
206 rescue => e
207 failed << p.full_name
207 failed << p.full_name
208 end
208 end
209 end
209 end
210 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
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
211 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
212 + elsif params.has_key? 'add_tags'
213 + get_problems_from_params.each do |p|
214 + p.tag_ids += params[:tag_ids]
215 + end
212 end
216 end
213
217
214 redirect_to :action => 'manage'
218 redirect_to :action => 'manage'
215 end
219 end
216
220
217 def import
221 def import
218 @allow_test_pair_import = allow_test_pair_import?
222 @allow_test_pair_import = allow_test_pair_import?
219 end
223 end
220
224
221 def do_import
225 def do_import
222 old_problem = Problem.find_by_name(params[:name])
226 old_problem = Problem.find_by_name(params[:name])
223 if !allow_test_pair_import? and params.has_key? :import_to_db
227 if !allow_test_pair_import? and params.has_key? :import_to_db
224 params.delete :import_to_db
228 params.delete :import_to_db
225 end
229 end
226 @problem, import_log = Problem.create_from_import_form_params(params,
230 @problem, import_log = Problem.create_from_import_form_params(params,
227 old_problem)
231 old_problem)
228
232
229 if !@problem.errors.empty?
233 if !@problem.errors.empty?
230 render :action => 'import' and return
234 render :action => 'import' and return
231 end
235 end
232
236
233 if old_problem!=nil
237 if old_problem!=nil
234 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
238 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
235 end
239 end
236 @log = import_log
240 @log = import_log
237 end
241 end
238
242
239 def remove_contest
243 def remove_contest
240 problem = Problem.find(params[:id])
244 problem = Problem.find(params[:id])
241 contest = Contest.find(params[:contest_id])
245 contest = Contest.find(params[:contest_id])
242 if problem!=nil and contest!=nil
246 if problem!=nil and contest!=nil
243 problem.contests.delete(contest)
247 problem.contests.delete(contest)
244 end
248 end
245 redirect_to :action => 'manage'
249 redirect_to :action => 'manage'
246 end
250 end
247
251
248 ##################################
252 ##################################
249 protected
253 protected
250
254
251 def allow_test_pair_import?
255 def allow_test_pair_import?
252 if defined? ALLOW_TEST_PAIR_IMPORT
256 if defined? ALLOW_TEST_PAIR_IMPORT
253 return ALLOW_TEST_PAIR_IMPORT
257 return ALLOW_TEST_PAIR_IMPORT
254 else
258 else
255 return false
259 return false
256 end
260 end
257 end
261 end
258
262
259 def change_date_added
263 def change_date_added
260 problems = get_problems_from_params
264 problems = get_problems_from_params
261 date = Date.parse(params[:date_added])
265 date = Date.parse(params[:date_added])
262 problems.each do |p|
266 problems.each do |p|
263 p.date_added = date
267 p.date_added = date
264 p.save
268 p.save
265 end
269 end
266 end
270 end
267
271
268 def add_to_contest
272 def add_to_contest
269 problems = get_problems_from_params
273 problems = get_problems_from_params
270 contest = Contest.find(params[:contest][:id])
274 contest = Contest.find(params[:contest][:id])
271 if contest!=nil and contest.enabled
275 if contest!=nil and contest.enabled
272 problems.each do |p|
276 problems.each do |p|
273 p.contests << contest
277 p.contests << contest
274 end
278 end
275 end
279 end
276 end
280 end
277
281
278 def set_available(avail)
282 def set_available(avail)
279 problems = get_problems_from_params
283 problems = get_problems_from_params
280 problems.each do |p|
284 problems.each do |p|
281 p.available = avail
285 p.available = avail
282 p.save
286 p.save
283 end
287 end
284 end
288 end
285
289
286 def get_problems_from_params
290 def get_problems_from_params
287 problems = []
291 problems = []
288 params.keys.each do |k|
292 params.keys.each do |k|
289 if k.index('prob-')==0
293 if k.index('prob-')==0
290 name, id, order = k.split('-')
294 name, id, order = k.split('-')
291 problems << Problem.find(id)
295 problems << Problem.find(id)
292 end
296 end
293 end
297 end
294 problems
298 problems
295 end
299 end
296
300
297 def get_problems_stat
301 def get_problems_stat
298 end
302 end
299
303
300 private
304 private
301
305
302 def problem_params
306 def problem_params
303 - params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description)
307 + params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[])
304 end
308 end
305
309
306 end
310 end
@@ -7,96 +7,98
7 before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score]
7 before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score]
8
8
9 before_filter(only: [:problem_hof]) { |c|
9 before_filter(only: [:problem_hof]) { |c|
10 return false unless authenticate
10 return false unless authenticate
11
11
12 admin_authorization unless GraderConfiguration["right.user_view_submission"]
12 admin_authorization unless GraderConfiguration["right.user_view_submission"]
13 }
13 }
14
14
15 def max_score
15 def max_score
16 end
16 end
17
17
18 def current_score
18 def current_score
19 @problems = Problem.available_problems
19 @problems = Problem.available_problems
20 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
20 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
21 @scorearray = calculate_max_score(@problems, @users,0,0,true)
21 @scorearray = calculate_max_score(@problems, @users,0,0,true)
22
22
23 #rencer accordingly
23 #rencer accordingly
24 if params[:button] == 'download' then
24 if params[:button] == 'download' then
25 csv = gen_csv_from_scorearray(@scorearray,@problems)
25 csv = gen_csv_from_scorearray(@scorearray,@problems)
26 send_data csv, filename: 'max_score.csv'
26 send_data csv, filename: 'max_score.csv'
27 else
27 else
28 #render template: 'user_admin/user_stat'
28 #render template: 'user_admin/user_stat'
29 render 'current_score'
29 render 'current_score'
30 end
30 end
31 end
31 end
32
32
33 def show_max_score
33 def show_max_score
34 #process parameters
34 #process parameters
35 #problems
35 #problems
36 @problems = []
36 @problems = []
37 if params[:problem_id]
37 if params[:problem_id]
38 params[:problem_id].each do |id|
38 params[:problem_id].each do |id|
39 next unless id.strip != ""
39 next unless id.strip != ""
40 pid = Problem.find_by_id(id.to_i)
40 pid = Problem.find_by_id(id.to_i)
41 @problems << pid if pid
41 @problems << pid if pid
42 end
42 end
43 end
43 end
44
44
45 #users
45 #users
46 @users = if params[:user] == "all" then
46 @users = if params[:user] == "all" then
47 User.includes(:contests).includes(:contest_stat)
47 User.includes(:contests).includes(:contest_stat)
48 else
48 else
49 User.includes(:contests).includes(:contest_stat).where(enabled: true)
49 User.includes(:contests).includes(:contest_stat).where(enabled: true)
50 end
50 end
51
51
52 #set up range from param
52 #set up range from param
53 @since_id = params.fetch(:from_id, 0).to_i
53 @since_id = params.fetch(:from_id, 0).to_i
54 @until_id = params.fetch(:to_id, 0).to_i
54 @until_id = params.fetch(:to_id, 0).to_i
55 + @since_id = nil if @since_id == 0
56 + @until_id = nil if @until_id == 0
55
57
56 #calculate the routine
58 #calculate the routine
57 @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
59 @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
58
60
59 #rencer accordingly
61 #rencer accordingly
60 if params[:button] == 'download' then
62 if params[:button] == 'download' then
61 csv = gen_csv_from_scorearray(@scorearray,@problems)
63 csv = gen_csv_from_scorearray(@scorearray,@problems)
62 send_data csv, filename: 'max_score.csv'
64 send_data csv, filename: 'max_score.csv'
63 else
65 else
64 #render template: 'user_admin/user_stat'
66 #render template: 'user_admin/user_stat'
65 render 'max_score'
67 render 'max_score'
66 end
68 end
67
69
68 end
70 end
69
71
70 def score
72 def score
71 if params[:commit] == 'download csv'
73 if params[:commit] == 'download csv'
72 @problems = Problem.all
74 @problems = Problem.all
73 else
75 else
74 @problems = Problem.available_problems
76 @problems = Problem.available_problems
75 end
77 end
76 @users = User.includes(:contests, :contest_stat).where(enabled: true)
78 @users = User.includes(:contests, :contest_stat).where(enabled: true)
77 @scorearray = Array.new
79 @scorearray = Array.new
78 @users.each do |u|
80 @users.each do |u|
79 ustat = Array.new
81 ustat = Array.new
80 ustat[0] = u
82 ustat[0] = u
81 @problems.each do |p|
83 @problems.each do |p|
82 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
84 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
83 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
85 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
84 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
86 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
85 else
87 else
86 ustat << [0,false]
88 ustat << [0,false]
87 end
89 end
88 end
90 end
89 @scorearray << ustat
91 @scorearray << ustat
90 end
92 end
91 if params[:commit] == 'download csv' then
93 if params[:commit] == 'download csv' then
92 csv = gen_csv_from_scorearray(@scorearray,@problems)
94 csv = gen_csv_from_scorearray(@scorearray,@problems)
93 send_data csv, filename: 'last_score.csv'
95 send_data csv, filename: 'last_score.csv'
94 else
96 else
95 render template: 'user_admin/user_stat'
97 render template: 'user_admin/user_stat'
96 end
98 end
97
99
98 end
100 end
99
101
100 def login_stat
102 def login_stat
101 @logins = Array.new
103 @logins = Array.new
102
104
@@ -1,58 +1,60
1 class TagsController < ApplicationController
1 class TagsController < ApplicationController
2 before_action :set_tag, only: [:show, :edit, :update, :destroy]
2 before_action :set_tag, only: [:show, :edit, :update, :destroy]
3
3
4 # GET /tags
4 # GET /tags
5 def index
5 def index
6 @tags = Tag.all
6 @tags = Tag.all
7 end
7 end
8
8
9 # GET /tags/1
9 # GET /tags/1
10 def show
10 def show
11 end
11 end
12
12
13 # GET /tags/new
13 # GET /tags/new
14 def new
14 def new
15 @tag = Tag.new
15 @tag = Tag.new
16 end
16 end
17
17
18 # GET /tags/1/edit
18 # GET /tags/1/edit
19 def edit
19 def edit
20 end
20 end
21
21
22 # POST /tags
22 # POST /tags
23 def create
23 def create
24 @tag = Tag.new(tag_params)
24 @tag = Tag.new(tag_params)
25
25
26 if @tag.save
26 if @tag.save
27 redirect_to @tag, notice: 'Tag was successfully created.'
27 redirect_to @tag, notice: 'Tag was successfully created.'
28 else
28 else
29 render :new
29 render :new
30 end
30 end
31 end
31 end
32
32
33 # PATCH/PUT /tags/1
33 # PATCH/PUT /tags/1
34 def update
34 def update
35 if @tag.update(tag_params)
35 if @tag.update(tag_params)
36 redirect_to @tag, notice: 'Tag was successfully updated.'
36 redirect_to @tag, notice: 'Tag was successfully updated.'
37 else
37 else
38 render :edit
38 render :edit
39 end
39 end
40 end
40 end
41
41
42 # DELETE /tags/1
42 # DELETE /tags/1
43 def destroy
43 def destroy
44 + #remove any association
45 + ProblemTag.where(tag_id: @tag.id).destroy_all
44 @tag.destroy
46 @tag.destroy
45 redirect_to tags_url, notice: 'Tag was successfully destroyed.'
47 redirect_to tags_url, notice: 'Tag was successfully destroyed.'
46 end
48 end
47
49
48 private
50 private
49 # Use callbacks to share common setup or constraints between actions.
51 # Use callbacks to share common setup or constraints between actions.
50 def set_tag
52 def set_tag
51 @tag = Tag.find(params[:id])
53 @tag = Tag.find(params[:id])
52 end
54 end
53
55
54 # Only allow a trusted parameter "white list" through.
56 # Only allow a trusted parameter "white list" through.
55 def tag_params
57 def tag_params
56 params.require(:tag).permit(:name, :description, :public)
58 params.require(:tag).permit(:name, :description, :public)
57 end
59 end
58 end
60 end
@@ -40,111 +40,114
40
40
41 if (user!=nil) and (session[:admin])
41 if (user!=nil) and (session[:admin])
42 # admin menu
42 # admin menu
43 menu_items << "<b>Administrative task:</b> "
43 menu_items << "<b>Administrative task:</b> "
44 append_to menu_items, '[Announcements]', 'announcements', 'index'
44 append_to menu_items, '[Announcements]', 'announcements', 'index'
45 append_to menu_items, '[Msg console]', 'messages', 'console'
45 append_to menu_items, '[Msg console]', 'messages', 'console'
46 append_to menu_items, '[Problems]', 'problems', 'index'
46 append_to menu_items, '[Problems]', 'problems', 'index'
47 append_to menu_items, '[Users]', 'user_admin', 'index'
47 append_to menu_items, '[Users]', 'user_admin', 'index'
48 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
48 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
49 append_to menu_items, '[Report]', 'report', 'multiple_login'
49 append_to menu_items, '[Report]', 'report', 'multiple_login'
50 append_to menu_items, '[Graders]', 'graders', 'list'
50 append_to menu_items, '[Graders]', 'graders', 'list'
51 append_to menu_items, '[Contests]', 'contest_management', 'index'
51 append_to menu_items, '[Contests]', 'contest_management', 'index'
52 append_to menu_items, '[Sites]', 'sites', 'index'
52 append_to menu_items, '[Sites]', 'sites', 'index'
53 append_to menu_items, '[System config]', 'configurations', 'index'
53 append_to menu_items, '[System config]', 'configurations', 'index'
54 menu_items << "<br/>"
54 menu_items << "<br/>"
55 end
55 end
56
56
57 # main page
57 # main page
58 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
58 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
59 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
59 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
60
60
61 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
61 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
62 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
62 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
63 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
63 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
64 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
64 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
65 end
65 end
66
66
67 if GraderConfiguration['right.user_hall_of_fame']
67 if GraderConfiguration['right.user_hall_of_fame']
68 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
68 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
69 end
69 end
70 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
70 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
71
71
72 if GraderConfiguration['system.user_setting_enabled']
72 if GraderConfiguration['system.user_setting_enabled']
73 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
73 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
74 end
74 end
75 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
75 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
76
76
77 menu_items.html_safe
77 menu_items.html_safe
78 end
78 end
79
79
80 def append_to(option,label, controller, action)
80 def append_to(option,label, controller, action)
81 option << ' ' if option!=''
81 option << ' ' if option!=''
82 option << link_to_unless_current(label,
82 option << link_to_unless_current(label,
83 :controller => controller,
83 :controller => controller,
84 :action => action)
84 :action => action)
85 end
85 end
86
86
87 def format_short_time(time)
87 def format_short_time(time)
88 - now = Time.now.gmtime
88 + now = Time.zone.now
89 st = ''
89 st = ''
90 - if (time.yday != now.yday) or
90 + if (time.yday != now.yday) or (time.year != now.year)
91 - (time.year != now.year)
91 + st = time.strftime("%d/%m/%y ")
92 - st = time.strftime("%x ")
93 end
92 end
94 st + time.strftime("%X")
93 st + time.strftime("%X")
95 end
94 end
96
95
97 def format_short_duration(duration)
96 def format_short_duration(duration)
98 return '' if duration==nil
97 return '' if duration==nil
99 d = duration.to_f
98 d = duration.to_f
100 return Time.at(d).gmtime.strftime("%X")
99 return Time.at(d).gmtime.strftime("%X")
101 end
100 end
102
101
102 + def format_full_time_ago(time)
103 + st = time_ago_in_words(time) + ' ago (' + format_short_time(time) + ')'
104 + end
105 +
103 def read_textfile(fname,max_size=2048)
106 def read_textfile(fname,max_size=2048)
104 begin
107 begin
105 File.open(fname).read(max_size)
108 File.open(fname).read(max_size)
106 rescue
109 rescue
107 nil
110 nil
108 end
111 end
109 end
112 end
110
113
111 def toggle_button(on,toggle_url,id, option={})
114 def toggle_button(on,toggle_url,id, option={})
112 btn_size = option[:size] || 'btn-xs'
115 btn_size = option[:size] || 'btn-xs'
113 link_to (on ? "Yes" : "No"), toggle_url,
116 link_to (on ? "Yes" : "No"), toggle_url,
114 {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
117 {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
115 id: id,
118 id: id,
116 data: {remote: true, method: 'get'}}
119 data: {remote: true, method: 'get'}}
117 end
120 end
118
121
119 def get_ace_mode(language)
122 def get_ace_mode(language)
120 # return ace mode string from Language
123 # return ace mode string from Language
121
124
122 case language.pretty_name
125 case language.pretty_name
123 when 'Pascal'
126 when 'Pascal'
124 'ace/mode/pascal'
127 'ace/mode/pascal'
125 when 'C++','C'
128 when 'C++','C'
126 'ace/mode/c_cpp'
129 'ace/mode/c_cpp'
127 when 'Ruby'
130 when 'Ruby'
128 'ace/mode/ruby'
131 'ace/mode/ruby'
129 when 'Python'
132 when 'Python'
130 'ace/mode/python'
133 'ace/mode/python'
131 when 'Java'
134 when 'Java'
132 'ace/mode/java'
135 'ace/mode/java'
133 else
136 else
134 'ace/mode/c_cpp'
137 'ace/mode/c_cpp'
135 end
138 end
136 end
139 end
137
140
138
141
139 def user_title_bar(user)
142 def user_title_bar(user)
140 header = ''
143 header = ''
141 time_left = ''
144 time_left = ''
142
145
143 #
146 #
144 # if the contest is over
147 # if the contest is over
145 if GraderConfiguration.time_limit_mode?
148 if GraderConfiguration.time_limit_mode?
146 if user.contest_finished?
149 if user.contest_finished?
147 header = <<CONTEST_OVER
150 header = <<CONTEST_OVER
148 <tr><td colspan="2" align="center">
151 <tr><td colspan="2" align="center">
149 <span class="contest-over-msg">THE CONTEST IS OVER</span>
152 <span class="contest-over-msg">THE CONTEST IS OVER</span>
150 </td></tr>
153 </td></tr>
@@ -1,86 +1,86
1 class Submission < ActiveRecord::Base
1 class Submission < ActiveRecord::Base
2
2
3 belongs_to :language
3 belongs_to :language
4 belongs_to :problem
4 belongs_to :problem
5 belongs_to :user
5 belongs_to :user
6
6
7 before_validation :assign_problem
7 before_validation :assign_problem
8 before_validation :assign_language
8 before_validation :assign_language
9
9
10 validates_presence_of :source
10 validates_presence_of :source
11 validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'too long'
11 validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'too long'
12 validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short'
12 validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short'
13 validate :must_have_valid_problem
13 validate :must_have_valid_problem
14 validate :must_specify_language
14 validate :must_specify_language
15
15
16 has_one :task
16 has_one :task
17
17
18 before_save :assign_latest_number_if_new_recond
18 before_save :assign_latest_number_if_new_recond
19
19
20 def self.find_last_by_user_and_problem(user_id, problem_id)
20 def self.find_last_by_user_and_problem(user_id, problem_id)
21 where("user_id = ? AND problem_id = ?",user_id,problem_id).last
21 where("user_id = ? AND problem_id = ?",user_id,problem_id).last
22 end
22 end
23
23
24 def self.find_all_last_by_problem(problem_id)
24 def self.find_all_last_by_problem(problem_id)
25 # need to put in SQL command, maybe there's a better way
25 # need to put in SQL command, maybe there's a better way
26 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
26 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
27 "WHERE id = " +
27 "WHERE id = " +
28 "(SELECT MAX(id) FROM submissions AS subs " +
28 "(SELECT MAX(id) FROM submissions AS subs " +
29 "WHERE subs.user_id = submissions.user_id AND " +
29 "WHERE subs.user_id = submissions.user_id AND " +
30 "problem_id = " + problem_id.to_s + " " +
30 "problem_id = " + problem_id.to_s + " " +
31 "GROUP BY user_id) " +
31 "GROUP BY user_id) " +
32 "ORDER BY user_id")
32 "ORDER BY user_id")
33 end
33 end
34
34
35 def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id)
35 def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id)
36 records = Submission.where(problem_id: problem_id,user_id: user_id)
36 records = Submission.where(problem_id: problem_id,user_id: user_id)
37 - records = records.where('id >= ?',since_id) if since_id > 0
37 + records = records.where('id >= ?',since_id) if since_id and since_id > 0
38 - records = records.where('id <= ?',until_id) if until_id > 0
38 + records = records.where('id <= ?',until_id) if until_id and until_id > 0
39 records.all
39 records.all
40 end
40 end
41
41
42 def self.find_last_for_all_available_problems(user_id)
42 def self.find_last_for_all_available_problems(user_id)
43 submissions = Array.new
43 submissions = Array.new
44 problems = Problem.available_problems
44 problems = Problem.available_problems
45 problems.each do |problem|
45 problems.each do |problem|
46 sub = Submission.find_last_by_user_and_problem(user_id, problem.id)
46 sub = Submission.find_last_by_user_and_problem(user_id, problem.id)
47 submissions << sub if sub!=nil
47 submissions << sub if sub!=nil
48 end
48 end
49 submissions
49 submissions
50 end
50 end
51
51
52 def self.find_by_user_problem_number(user_id, problem_id, number)
52 def self.find_by_user_problem_number(user_id, problem_id, number)
53 where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first
53 where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first
54 end
54 end
55
55
56 def self.find_all_by_user_problem(user_id, problem_id)
56 def self.find_all_by_user_problem(user_id, problem_id)
57 where("user_id = ? AND problem_id = ?",user_id,problem_id)
57 where("user_id = ? AND problem_id = ?",user_id,problem_id)
58 end
58 end
59
59
60 def download_filename
60 def download_filename
61 if self.problem.output_only
61 if self.problem.output_only
62 return self.source_filename
62 return self.source_filename
63 else
63 else
64 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
64 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
65 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
65 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
66 end
66 end
67 end
67 end
68
68
69 protected
69 protected
70
70
71 def self.find_option_in_source(option, source)
71 def self.find_option_in_source(option, source)
72 if source==nil
72 if source==nil
73 return nil
73 return nil
74 end
74 end
75 i = 0
75 i = 0
76 source.each_line do |s|
76 source.each_line do |s|
77 if s =~ option
77 if s =~ option
78 words = s.split
78 words = s.split
79 return words[1]
79 return words[1]
80 end
80 end
81 i = i + 1
81 i = i + 1
82 if i==10
82 if i==10
83 return nil
83 return nil
84 end
84 end
85 end
85 end
86 return nil
86 return nil
@@ -1,26 +1,28
1 -
2 - if submission.nil?
1 - if submission.nil?
3 = "-"
2 = "-"
4 - else
3 - else
4 + %strong= "Submission ID:"
5 + = submission.id
6 + %br
5 - unless submission.graded_at
7 - unless submission.graded_at
6 - = t 'main.submitted_at'
8 + %strong= t 'main.submitted_at:'
7 - = format_short_time(submission.submitted_at.localtime)
9 + = format_full_time_ago(submission.submitted_at.localtime)
8 - else
10 - else
9 - %strong= t 'main.graded_at'
11 + %strong= t 'main.graded_at:'
10 - = "#{format_short_time(submission.graded_at.localtime)} "
12 + = format_full_time_ago(submission.graded_at.localtime)
11 %br
13 %br
12 - if GraderConfiguration['ui.show_score']
14 - if GraderConfiguration['ui.show_score']
13 %strong=t 'main.score'
15 %strong=t 'main.score'
14 = "#{(submission.points*100/submission.problem.full_score).to_i} "
16 = "#{(submission.points*100/submission.problem.full_score).to_i} "
15 = " ["
17 = " ["
16 %tt
18 %tt
17 = submission.grader_comment
19 = submission.grader_comment
18 = "]"
20 = "]"
19 %br
21 %br
20 - %strong View:
22 + %strong View:
21 - - if GraderConfiguration.show_grading_result
23 + - if GraderConfiguration.show_grading_result
22 - = link_to '[detailed result]', :action => 'result', :id => submission.id
24 + = link_to '[detailed result]', :action => 'result', :id => submission.id
23 - = link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
25 + = link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'} if submission.graded_at
24 = link_to "#{t 'main.src_link'}", download_submission_path(submission.id), class: 'btn btn-xs btn-info'
26 = link_to "#{t 'main.src_link'}", download_submission_path(submission.id), class: 'btn btn-xs btn-info'
25 = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info'
27 = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info'
26
28
@@ -1,94 +1,95
1 %header.navbar.navbar-default.navbar-fixed-top
1 %header.navbar.navbar-default.navbar-fixed-top
2 %nav
2 %nav
3 .container-fluid
3 .container-fluid
4 .navbar-header
4 .navbar-header
5 %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} }
5 %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} }
6 %span.sr-only Togggle Navigation
6 %span.sr-only Togggle Navigation
7 %span.icon-bar
7 %span.icon-bar
8 %span.icon-bar
8 %span.icon-bar
9 %span.icon-bar
9 %span.icon-bar
10 %a.navbar-brand{href: main_list_path}
10 %a.navbar-brand{href: main_list_path}
11 %span.glyphicon.glyphicon-home
11 %span.glyphicon.glyphicon-home
12 MAIN
12 MAIN
13 .collapse.navbar-collapse#navbar-collapse
13 .collapse.navbar-collapse#navbar-collapse
14 %ul.nav.navbar-nav
14 %ul.nav.navbar-nav
15 / submission
15 / submission
16 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
16 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
17 %li.dropdown
17 %li.dropdown
18 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
18 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
19 = "#{I18n.t 'menu.submissions'}"
19 = "#{I18n.t 'menu.submissions'}"
20 %span.caret
20 %span.caret
21 %ul.dropdown-menu
21 %ul.dropdown-menu
22 = add_menu("View", 'submissions', 'index')
22 = add_menu("View", 'submissions', 'index')
23 = add_menu("Self Test", 'test', 'index')
23 = add_menu("Self Test", 'test', 'index')
24 / hall of fame
24 / hall of fame
25 - if GraderConfiguration['right.user_hall_of_fame']
25 - if GraderConfiguration['right.user_hall_of_fame']
26 = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
26 = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
27 / display MODE button (with countdown in contest mode)
27 / display MODE button (with countdown in contest mode)
28 - if GraderConfiguration.analysis_mode?
28 - if GraderConfiguration.analysis_mode?
29 %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE"
29 %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE"
30 - elsif GraderConfiguration.time_limit_mode?
30 - elsif GraderConfiguration.time_limit_mode?
31 - if @current_user.contest_finished?
31 - if @current_user.contest_finished?
32 %div.navbar-btn.btn.btn-danger#countdown= "Contest is over"
32 %div.navbar-btn.btn.btn-danger#countdown= "Contest is over"
33 - elsif !@current_user.contest_started?
33 - elsif !@current_user.contest_started?
34 %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
34 %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
35 - else
35 - else
36 %div.navbar-btn.btn.btn-primary#countdown asdf
36 %div.navbar-btn.btn.btn-primary#countdown asdf
37 :javascript
37 :javascript
38 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
38 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
39 / admin section
39 / admin section
40 - if (@current_user!=nil) and (session[:admin])
40 - if (@current_user!=nil) and (session[:admin])
41 / management
41 / management
42 %li.dropdown
42 %li.dropdown
43 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
43 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
44 Manage
44 Manage
45 %span.caret
45 %span.caret
46 %ul.dropdown-menu
46 %ul.dropdown-menu
47 = add_menu( 'Announcements', 'announcements', 'index')
47 = add_menu( 'Announcements', 'announcements', 'index')
48 = add_menu( 'Problems', 'problems', 'index')
48 = add_menu( 'Problems', 'problems', 'index')
49 + = add_menu( 'Tags', 'tags', 'index')
49 = add_menu( 'Users', 'user_admin', 'index')
50 = add_menu( 'Users', 'user_admin', 'index')
50 = add_menu( 'User Groups', 'groups', 'index')
51 = add_menu( 'User Groups', 'groups', 'index')
51 = add_menu( 'Graders', 'graders', 'list')
52 = add_menu( 'Graders', 'graders', 'list')
52 = add_menu( 'Message ', 'messages', 'console')
53 = add_menu( 'Message ', 'messages', 'console')
53 %li.divider{role: 'separator'}
54 %li.divider{role: 'separator'}
54 = add_menu( 'System config', 'configurations', 'index')
55 = add_menu( 'System config', 'configurations', 'index')
55 %li.divider{role: 'separator'}
56 %li.divider{role: 'separator'}
56 = add_menu( 'Sites', 'sites', 'index')
57 = add_menu( 'Sites', 'sites', 'index')
57 = add_menu( 'Contests', 'contest_management', 'index')
58 = add_menu( 'Contests', 'contest_management', 'index')
58 / report
59 / report
59 %li.dropdown
60 %li.dropdown
60 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
61 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
61 Report
62 Report
62 %span.caret
63 %span.caret
63 %ul.dropdown-menu
64 %ul.dropdown-menu
64 = add_menu( 'Current Score', 'report', 'current_score')
65 = add_menu( 'Current Score', 'report', 'current_score')
65 = add_menu( 'Score Report', 'report', 'max_score')
66 = add_menu( 'Score Report', 'report', 'max_score')
66 = add_menu( 'Report', 'report', 'multiple_login')
67 = add_menu( 'Report', 'report', 'multiple_login')
67 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
68 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
68 =link_to "#{ungraded} backlogs!",
69 =link_to "#{ungraded} backlogs!",
69 grader_list_path,
70 grader_list_path,
70 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
71 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
71
72
72 %ul.nav.navbar-nav.navbar-right
73 %ul.nav.navbar-nav.navbar-right
73 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
74 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
74 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
75 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
75 - if GraderConfiguration['system.user_setting_enabled']
76 - if GraderConfiguration['system.user_setting_enabled']
76 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
77 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
77 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
78 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
78
79
79 /
80 /
80 - if (@current_user!=nil) and (session[:admin])
81 - if (@current_user!=nil) and (session[:admin])
81 %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar
82 %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar
82 .container-fluid
83 .container-fluid
83 .collapse.navbar-collapse
84 .collapse.navbar-collapse
84 %ul.nav.navbar-nav
85 %ul.nav.navbar-nav
85 = add_menu( '[Announcements]', 'announcements', 'index')
86 = add_menu( '[Announcements]', 'announcements', 'index')
86 = add_menu( '[Msg console]', 'messages', 'console')
87 = add_menu( '[Msg console]', 'messages', 'console')
87 = add_menu( '[Problems]', 'problems', 'index')
88 = add_menu( '[Problems]', 'problems', 'index')
88 = add_menu( '[Users]', 'user_admin', 'index')
89 = add_menu( '[Users]', 'user_admin', 'index')
89 = add_menu( '[Results]', 'user_admin', 'user_stat')
90 = add_menu( '[Results]', 'user_admin', 'user_stat')
90 = add_menu( '[Report]', 'report', 'multiple_login')
91 = add_menu( '[Report]', 'report', 'multiple_login')
91 = add_menu( '[Graders]', 'graders', 'list')
92 = add_menu( '[Graders]', 'graders', 'list')
92 = add_menu( '[Contests]', 'contest_management', 'index')
93 = add_menu( '[Contests]', 'contest_management', 'index')
93 = add_menu( '[Sites]', 'sites', 'index')
94 = add_menu( '[Sites]', 'sites', 'index')
94 = add_menu( '[System config]', 'configurations', 'index')
95 = add_menu( '[System config]', 'configurations', 'index')
@@ -1,52 +1,55
1 = error_messages_for 'problem'
1 = error_messages_for 'problem'
2 / [form:problem]
2 / [form:problem]
3 .form-group
3 .form-group
4 %label{:for => "problem_name"} Name
4 %label{:for => "problem_name"} Name
5 = text_field 'problem', 'name', class: 'form-control'
5 = text_field 'problem', 'name', class: 'form-control'
6 %small
6 %small
7 Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead.
7 Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead.
8 .form-group
8 .form-group
9 %label{:for => "problem_full_name"} Full name
9 %label{:for => "problem_full_name"} Full name
10 = text_field 'problem', 'full_name', class: 'form-control'
10 = text_field 'problem', 'full_name', class: 'form-control'
11 .form-group
11 .form-group
12 %label{:for => "problem_full_score"} Full score
12 %label{:for => "problem_full_score"} Full score
13 = text_field 'problem', 'full_score', class: 'form-control'
13 = text_field 'problem', 'full_score', class: 'form-control'
14 .form-group
14 .form-group
15 + %label{:for => "problem_full_score"} Tags
16 + = collection_select(:problem, :tag_ids, Tag.all, :id, :name, {}, {multiple: true, class: 'form-control select2'})
17 + .form-group
15 %label{:for => "problem_date_added"} Date added
18 %label{:for => "problem_date_added"} Date added
16 = date_select 'problem', 'date_added', class: 'form-control'
19 = date_select 'problem', 'date_added', class: 'form-control'
17 - # TODO: these should be put in model Problem, but I can't think of
20 - # TODO: these should be put in model Problem, but I can't think of
18 - # nice default values for them. These values look fine only
21 - # nice default values for them. These values look fine only
19 - # in this case (of lazily adding new problems).
22 - # in this case (of lazily adding new problems).
20 - @problem.available = true if @problem!=nil and @problem.available==nil
23 - @problem.available = true if @problem!=nil and @problem.available==nil
21 - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil
24 - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil
22 - @problem.output_only = false if @problem!=nil and @problem.output_only==nil
25 - @problem.output_only = false if @problem!=nil and @problem.output_only==nil
23 .checkbox
26 .checkbox
24 %label{:for => "problem_available"}
27 %label{:for => "problem_available"}
25 = check_box :problem, :available
28 = check_box :problem, :available
26 Available?
29 Available?
27 .checkbox
30 .checkbox
28 %label{:for => "problem_test_allowed"}
31 %label{:for => "problem_test_allowed"}
29 = check_box :problem, :test_allowed
32 = check_box :problem, :test_allowed
30 Test allowed?
33 Test allowed?
31 .checkbox
34 .checkbox
32 %label{:for => "problem_output_only"}
35 %label{:for => "problem_output_only"}
33 = check_box :problem, :output_only
36 = check_box :problem, :output_only
34 Output only?
37 Output only?
35 = error_messages_for 'description'
38 = error_messages_for 'description'
36 .form-group
39 .form-group
37 %label{:for => "description_body"} Description
40 %label{:for => "description_body"} Description
38 %br/
41 %br/
39 = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control'
42 = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control'
40 .form-group
43 .form-group
41 %label{:for => "description_markdowned"} Markdowned?
44 %label{:for => "description_markdowned"} Markdowned?
42 = select "description", |
45 = select "description", |
43 "markdowned", |
46 "markdowned", |
44 [['True',true],['False',false]], |
47 [['True',true],['False',false]], |
45 {:selected => (@description) ? @description.markdowned : false } |
48 {:selected => (@description) ? @description.markdowned : false } |
46 .form-group
49 .form-group
47 %label{:for => "problem_url"} URL
50 %label{:for => "problem_url"} URL
48 %br/
51 %br/
49 = text_field 'problem', 'url',class: 'form-control'
52 = text_field 'problem', 'url',class: 'form-control'
50 %p
53 %p
51 Task PDF #{file_field_tag 'file'}
54 Task PDF #{file_field_tag 'file'}
52 / [eoform:problem]
55 / [eoform:problem]
@@ -1,60 +1,65
1 - content_for :head do
1 - content_for :head do
2 = stylesheet_link_tag 'problems'
2 = stylesheet_link_tag 'problems'
3 %h1 Problems
3 %h1 Problems
4 %p
4 %p
5 = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success 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'
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'
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 Tags
24 %th
25 %th
25 Submit
26 Submit
26 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?]
27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?]
27 %th Date added
28 %th Date added
28 %th.text-center
29 %th.text-center
29 Avail?
30 Avail?
30 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
31 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
31 %th.text-center
32 %th.text-center
32 View Data?
33 View Data?
33 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
34 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
34 %th.text-center
35 %th.text-center
35 Test?
36 Test?
36 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
37 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
37 - if GraderConfiguration.multicontests?
38 - if GraderConfiguration.multicontests?
38 %th Contests
39 %th Contests
39 - for problem in @problems
40 - for problem in @problems
40 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
41 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
41 - @problem=problem
42 - @problem=problem
42 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
43 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
43 %td
44 %td
44 = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
45 = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
45 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
46 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
46 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
47 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
48 + %td
49 + - problem.tags.each do |t|
50 + - #%button.btn.btn-default.btn-xs= t.name
51 + %span.label.label-default= t.name
47 %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-xs btn-primary'
52 %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-xs btn-primary'
48 %td= problem.date_added
53 %td= problem.date_added
49 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
54 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
50 %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
55 %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
51 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
56 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
52 - if GraderConfiguration.multicontests?
57 - if GraderConfiguration.multicontests?
53 %td
58 %td
54 = problem.contests.collect { |c| c.name }.join(', ')
59 = problem.contests.collect { |c| c.name }.join(', ')
55 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
60 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
56 %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
61 %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
57 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
62 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
58 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
63 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
59 %br/
64 %br/
60 = link_to '[New problem]', :action => 'new'
65 = link_to '[New problem]', :action => 'new'
@@ -4,104 +4,115
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]', problems_path
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 .panel.panel-primary
42 .panel.panel-primary
43 .panel-heading
43 .panel-heading
44 Action
44 Action
45 .panel-body
45 .panel-body
46 .submit-box
46 .submit-box
47 What do you want to do to the selected problem?
47 What do you want to do to the selected problem?
48 %br/
48 %br/
49 (You can shift-click to select a range of problems)
49 (You can shift-click to select a range of problems)
50 %ul.form-inline
50 %ul.form-inline
51 %li
51 %li
52 - Change date added to
52 + Change "Date added" to
53 .input-group.date
53 .input-group.date
54 = text_field_tag :date_added, class: 'form-control'
54 = text_field_tag :date_added, class: 'form-control'
55 %span.input-group-addon
55 %span.input-group-addon
56 %span.glyphicon.glyphicon-calendar
56 %span.glyphicon.glyphicon-calendar
57 -# = select_date Date.current, :prefix => 'date_added'
57 -# = select_date Date.current, :prefix => 'date_added'
58 &nbsp;&nbsp;&nbsp;
58 &nbsp;&nbsp;&nbsp;
59 = submit_tag 'Change', :name => 'change_date_added', class: 'btn btn-primary btn-sm'
59 = submit_tag 'Change', :name => 'change_date_added', class: 'btn btn-primary btn-sm'
60 %li
60 %li
61 - Set available to
61 + Set "Available" to
62 = submit_tag 'True', :name => 'enable_problem', class: 'btn btn-primary btn-sm'
62 = submit_tag 'True', :name => 'enable_problem', class: 'btn btn-primary btn-sm'
63 = submit_tag 'False', :name => 'disable_problem', class: 'btn btn-primary btn-sm'
63 = submit_tag 'False', :name => 'disable_problem', class: 'btn btn-primary btn-sm'
64
64
65 - if GraderConfiguration.multicontests?
65 - if GraderConfiguration.multicontests?
66 %li
66 %li
67 - Add to
67 + Add selected problems to contest
68 = select("contest","id",Contest.all.collect {|c| [c.title, c.id]})
68 = select("contest","id",Contest.all.collect {|c| [c.title, c.id]})
69 = submit_tag 'Add', :name => 'add_to_contest', class: 'btn btn-primary btn-sm'
69 = submit_tag 'Add', :name => 'add_to_contest', class: 'btn btn-primary btn-sm'
70 %li
70 %li
71 - Add problems to group
71 + Add selected problems to user group
72 = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
72 = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
73 - = submit_tag 'Add', name: 'add_group', class: 'btn btn-default'
73 + = submit_tag 'Add', name: 'add_group', class: 'btn btn-primary'
74 -
74 + %li
75 + Add the following tags to the selected problems
76 + = select_tag "tag_ids", options_from_collection_for_select( Tag.all, 'id','name'), id: 'tags_name',class: 'select2', multiple: true, data: {placeholder: 'Select tags by clicking', width: "200px"}
77 + = submit_tag 'Add', name: 'add_tags', class: 'btn btn-primary'
75
78
76 - %table.table.table-hover
79 + %table.table.table-hover.datatable
77 - %tr{style: "text-align: left;"}
80 + %thead
78 - %th= check_box_tag 'select_all'
81 + %tr{style: "text-align: left;"}
79 - %th Name
82 + %th= check_box_tag 'select_all'
80 - %th Full name
83 + %th Name
81 - %th Available
84 + %th Full name
82 - %th Date added
85 + %th Tags
83 - - if GraderConfiguration.multicontests?
86 + %th Available
84 - %th Contests
87 + %th Date added
88 + - if GraderConfiguration.multicontests?
89 + %th Contests
85
90
86 - - num = 0
91 + %tbody
87 - - for problem in @problems
92 + - num = 0
88 - - num += 1
93 + - for problem in @problems
89 - %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"}
94 + - num += 1
90 - %td= check_box_tag "prob-#{problem.id}-#{num}"
95 + %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"}
91 - %td= problem.name
96 + %td= check_box_tag "prob-#{problem.id}-#{num}"
92 - %td= problem.full_name
97 + %td= problem.name
93 - %td= problem.available
98 + %td= problem.full_name
94 - %td= problem.date_added
95 - - if GraderConfiguration.multicontests?
96 %td
99 %td
97 - - problem.contests.each do |contest|
100 + - problem.tags.each do |t|
98 - = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])"
101 + %span.label.label-default= t.name
102 + %td= problem.available
103 + %td= problem.date_added
104 + - if GraderConfiguration.multicontests?
105 + %td
106 + - problem.contests.each do |contest|
107 + = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])"
99
108
100 :javascript
109 :javascript
101 $('.input-group.date').datetimepicker({
110 $('.input-group.date').datetimepicker({
102 format: 'DD/MMM/YYYY',
111 format: 'DD/MMM/YYYY',
103 showTodayButton: true,
112 showTodayButton: true,
104 widgetPositioning: {horizontal: 'auto', vertical: 'bottom'},
113 widgetPositioning: {horizontal: 'auto', vertical: 'bottom'},
105
114
106 });
115 });
107 -
116 + $('.datatable').DataTable({
117 + paging: false
118 + });
@@ -1,57 +1,59
1 :css
1 :css
2 .fix-width {
2 .fix-width {
3 font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier"
3 font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier"
4 }
4 }
5
5
6 %h1 Problem stat: #{@problem.name}
6 %h1 Problem stat: #{@problem.name}
7 %h2 Overview
7 %h2 Overview
8
8
9
9
10 %table.info
10 %table.info
11 %thead
11 %thead
12 %tr.info-head
12 %tr.info-head
13 %th Stat
13 %th Stat
14 %th Value
14 %th Value
15 %tbody
15 %tbody
16 %tr{class: cycle('info-even','info-odd')}
16 %tr{class: cycle('info-even','info-odd')}
17 %td Submissions
17 %td Submissions
18 %td= @submissions.count
18 %td= @submissions.count
19 %tr{class: cycle('info-even','info-odd')}
19 %tr{class: cycle('info-even','info-odd')}
20 %td Solved/Attempted User
20 %td Solved/Attempted User
21 %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
21 %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
22
22
23 %h2 Submissions Count
23 %h2 Submissions Count
24 = render partial: 'application/bar_graph', locals: { histogram: @histogram }
24 = render partial: 'application/bar_graph', locals: { histogram: @histogram }
25
25
26 %h2 Submissions
26 %h2 Submissions
27 - if @submissions and @submissions.count > 0
27 - if @submissions and @submissions.count > 0
28 %table#main_table.table.table-condensed.table-striped
28 %table#main_table.table.table-condensed.table-striped
29 %thead
29 %thead
30 %tr
30 %tr
31 %th ID
31 %th ID
32 %th Login
32 %th Login
33 %th Name
33 %th Name
34 %th Submitted_at
34 %th Submitted_at
35 + %th language
35 %th Points
36 %th Points
36 %th comment
37 %th comment
37 %th IP
38 %th IP
38 %tbody
39 %tbody
39 - row_odd,curr = true,''
40 - row_odd,curr = true,''
40 - @submissions.each do |sub|
41 - @submissions.each do |sub|
41 - next unless sub.user
42 - next unless sub.user
42 - row_odd,curr = !row_odd, sub.user if curr != sub.user
43 - row_odd,curr = !row_odd, sub.user if curr != sub.user
43 %tr
44 %tr
44 %td= link_to sub.id, submission_path(sub)
45 %td= link_to sub.id, submission_path(sub)
45 %td= link_to sub.user.login, stat_user_path(sub.user)
46 %td= link_to sub.user.login, stat_user_path(sub.user)
46 %td= sub.user.full_name
47 %td= sub.user.full_name
47 %td{data: {order: sub.submitted_at}}= time_ago_in_words(sub.submitted_at) + " ago"
48 %td{data: {order: sub.submitted_at}}= time_ago_in_words(sub.submitted_at) + " ago"
49 + %td= sub.language.name
48 %td= sub.points
50 %td= sub.points
49 %td.fix-width= sub.grader_comment
51 %td.fix-width= sub.grader_comment
50 %td= sub.ip_address
52 %td= sub.ip_address
51 - else
53 - else
52 No submission
54 No submission
53
55
54 :javascript
56 :javascript
55 $("#main_table").DataTable({
57 $("#main_table").DataTable({
56 paging: false
58 paging: false
57 });
59 });
@@ -1,34 +1,69
1 %table.table.sortable.table-striped.table-bordered.table-condensed
1 %table.table.sortable.table-striped.table-bordered.table-condensed
2 %thead
2 %thead
3 %tr
3 %tr
4 %th Login
4 %th Login
5 %th Name
5 %th Name
6 / %th Activated?
6 / %th Activated?
7 / %th Logged_in
7 / %th Logged_in
8 / %th Contest(s)
8 / %th Contest(s)
9 %th Remark
9 %th Remark
10 - @problems.each do |p|
10 - @problems.each do |p|
11 %th.text-right= p.name.gsub('_',' ')
11 %th.text-right= p.name.gsub('_',' ')
12 %th.text-right Total
12 %th.text-right Total
13 %th.text-right Passed
13 %th.text-right Passed
14 %tbody
14 %tbody
15 + - sum = Array.new(@scorearray[0].count,0)
16 + - nonzero = Array.new(@scorearray[0].count,0)
17 + - full = Array.new(@scorearray[0].count,0)
15 - @scorearray.each do |sc|
18 - @scorearray.each do |sc|
16 %tr
19 %tr
17 - total,num_passed = 0,0
20 - total,num_passed = 0,0
18 - sc.each_index do |i|
21 - sc.each_index do |i|
19 - if i == 0
22 - if i == 0
20 %td= link_to sc[i].login, stat_user_path(sc[i])
23 %td= link_to sc[i].login, stat_user_path(sc[i])
21 %td= sc[i].full_name
24 %td= sc[i].full_name
22 / %td= sc[i].activated
25 / %td= sc[i].activated
23 / %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no'
26 / %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no'
24 / %td= sc[i].contests.collect {|c| c.name}.join(', ')
27 / %td= sc[i].contests.collect {|c| c.name}.join(', ')
25 %td= sc[i].remark
28 %td= sc[i].remark
26 - else
29 - else
27 %td.text-right= sc[i][0]
30 %td.text-right= sc[i][0]
28 - total += sc[i][0]
31 - total += sc[i][0]
29 - num_passed += 1 if sc[i][1]
32 - num_passed += 1 if sc[i][1]
33 + - sum[i] += sc[i][0]
34 + - nonzero[i] += 1 if sc[i][0] > 0
35 + - full[i] += 1 if sc[i][1]
30 %td.text-right= total
36 %td.text-right= total
31 %td.text-right= num_passed
37 %td.text-right= num_passed
38 + %tfoot
39 + %tr
40 + %td Summation
41 + %td
42 + %td
43 + - sum.each.with_index do |s,i|
44 + - next if i == 0
45 + %td.text-right= number_with_delimiter(s)
46 + %td
47 + %td
48 + %tr
49 + %td partial solver
50 + %td
51 + %td
52 + - nonzero.each.with_index do |s,i|
53 + - next if i == 0
54 + %td.text-right= number_with_delimiter(s)
55 + %td
56 + %td
57 + %tr
58 + %td Full solver
59 + %td
60 + %td
61 + - full.each.with_index do |s,i|
62 + - next if i == 0
63 + %td.text-right= number_with_delimiter(s)
64 + %td
65 + %td
66 +
32
67
33 :javascript
68 :javascript
34 $.bootstrapSortable(true,'reversed')
69 $.bootstrapSortable(true,'reversed')
@@ -1,49 +1,49
1 %h1 Maximum score
1 %h1 Maximum score
2
2
3 = form_tag report_show_max_score_path
3 = form_tag report_show_max_score_path
4 .row
4 .row
5 .col-md-4
5 .col-md-4
6 .panel.panel-primary
6 .panel.panel-primary
7 .panel-heading
7 .panel-heading
8 Problems
8 Problems
9 .panel-body
9 .panel-body
10 %p
10 %p
11 Select problem(s) that we wish to know the score.
11 Select problem(s) that we wish to know the score.
12 = label_tag :problem_id, "Problems"
12 = label_tag :problem_id, "Problems"
13 = select_tag 'problem_id[]',
13 = select_tag 'problem_id[]',
14 options_for_select(Problem.all.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},params[:problem_id]),
14 options_for_select(Problem.all.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},params[:problem_id]),
15 { class: 'select2 form-control', multiple: "true" }
15 { class: 'select2 form-control', multiple: "true" }
16 .col-md-4
16 .col-md-4
17 .panel.panel-primary
17 .panel.panel-primary
18 .panel-heading
18 .panel-heading
19 Submission range
19 Submission range
20 .panel-body
20 .panel-body
21 %p
21 %p
22 Input minimum and maximum range of submission ID that should be included. A blank value for min and max means -1 and infinity, respectively.
22 Input minimum and maximum range of submission ID that should be included. A blank value for min and max means -1 and infinity, respectively.
23 .form-group
23 .form-group
24 = label_tag :from, "Min"
24 = label_tag :from, "Min"
25 = text_field_tag 'from_id', @since_id, class: "form-control"
25 = text_field_tag 'from_id', @since_id, class: "form-control"
26 .form-group
26 .form-group
27 = label_tag :from, "Max"
27 = label_tag :from, "Max"
28 = text_field_tag 'to_id', @until_id, class: "form-control"
28 = text_field_tag 'to_id', @until_id, class: "form-control"
29 .col-md-4
29 .col-md-4
30 .panel.panel-primary
30 .panel.panel-primary
31 .panel-heading
31 .panel-heading
32 Users
32 Users
33 .panel-body
33 .panel-body
34 .radio
34 .radio
35 %label
35 %label
36 - = radio_button_tag 'users', 'all', true
36 + = radio_button_tag 'users', 'all', (params[:users] == "all")
37 All users
37 All users
38 .radio
38 .radio
39 %label
39 %label
40 - = radio_button_tag 'users', 'enabled'
40 + = radio_button_tag 'users', 'enabled', (params[:users] == "enabled")
41 Only enabled users
41 Only enabled users
42 .row
42 .row
43 .col-md-12
43 .col-md-12
44 = button_tag 'Show', class: "btn btn-primary btn-large", value: "show"
44 = button_tag 'Show', class: "btn btn-primary btn-large", value: "show"
45 = button_tag 'Download CSV', class: "btn btn-primary btn-large", value: "download"
45 = button_tag 'Download CSV', class: "btn btn-primary btn-large", value: "download"
46
46
47 - if @scorearray
47 - if @scorearray
48 %h2 Result
48 %h2 Result
49 =render "score_table"
49 =render "score_table"
@@ -1,35 +1,36
1 %h1 Editing site
1 %h1 Editing site
2 = error_messages_for :site
2 = error_messages_for :site
3 = form_for(@site) do |f|
3 = form_for(@site) do |f|
4 .row
4 .row
5 .col-md-4
5 .col-md-4
6 .form-group.field
6 .form-group.field
7 = f.label :name, "Name"
7 = f.label :name, "Name"
8 = f.text_field :name, class: 'form-control'
8 = f.text_field :name, class: 'form-control'
9 .form-group.field
9 .form-group.field
10 = f.label :password, "Password"
10 = f.label :password, "Password"
11 = f.text_field :password, class: 'form-control'
11 = f.text_field :password, class: 'form-control'
12 .form-group.field
12 .form-group.field
13 = f.label :started, "Started"
13 = f.label :started, "Started"
14 = f.check_box :started, class: 'form-control'
14 = f.check_box :started, class: 'form-control'
15 .form-group.field
15 .form-group.field
16 = f.label :start_time, "Start time"
16 = f.label :start_time, "Start time"
17 -# = f.datetime_select :start_time, :include_blank => true
17 -# = f.datetime_select :start_time, :include_blank => true
18 .input-group.date
18 .input-group.date
19 = f.text_field :start_time, class:'form-control' , value: (@site.start_time ? @site.start_time.strftime('%d/%b/%Y %H:%M') : '')
19 = f.text_field :start_time, class:'form-control' , value: (@site.start_time ? @site.start_time.strftime('%d/%b/%Y %H:%M') : '')
20 %span.input-group-addon
20 %span.input-group-addon
21 %span.glyphicon.glyphicon-calendar
21 %span.glyphicon.glyphicon-calendar
22 .actions
22 .actions
23 = f.submit "Update", class: 'btn btn-primary'
23 = f.submit "Update", class: 'btn btn-primary'
24 .col-md-8
24 .col-md-8
25
25
26 = link_to 'Show', @site
26 = link_to 'Show', @site
27 |
27 |
28 = link_to 'Back', sites_path
28 = link_to 'Back', sites_path
29
29
30
30
31 :javascript
31 :javascript
32 $('.input-group.date').datetimepicker({
32 $('.input-group.date').datetimepicker({
33 format: 'DD/MMM/YYYY HH:mm',
33 format: 'DD/MMM/YYYY HH:mm',
34 + showTodayButton: true,
34 });
35 });
35
36
@@ -1,85 +1,86
1 %h2 Live submit
1 %h2 Live submit
2 %br
2 %br
3
3
4 %textarea#text_sourcecode{style: "display:none"}~ @source
4 %textarea#text_sourcecode{style: "display:none"}~ @source
5 .container
5 .container
6 .row
6 .row
7 .col-md-12
7 .col-md-12
8 .alert.alert-info
8 .alert.alert-info
9 Write your code in the following box, choose language, and click submit button when finished
9 Write your code in the following box, choose language, and click submit button when finished
10 .row
10 .row
11 .col-md-8
11 .col-md-8
12 %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'}
12 %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'}
13 .col-md-4
13 .col-md-4
14 - # submission form
14 - # submission form
15 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
15 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
16
16
17 = hidden_field_tag 'editor_text', @source
17 = hidden_field_tag 'editor_text', @source
18 = hidden_field_tag 'submission[problem_id]', @problem.id
18 = hidden_field_tag 'submission[problem_id]', @problem.id
19 .form-group
19 .form-group
20 = label_tag "Task:"
20 = label_tag "Task:"
21 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
21 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
22
22
23 .form-group
23 .form-group
24 = label_tag 'Language'
24 = label_tag 'Language'
25 = select_tag 'language_id', options_from_collection_for_select(Language.all, 'id', 'pretty_name', @lang_id || Language.find_by_pretty_name("Python").id || Language.first.id), class: 'form-control select', style: "width: 100px"
25 = select_tag 'language_id', options_from_collection_for_select(Language.all, 'id', 'pretty_name', @lang_id || Language.find_by_pretty_name("Python").id || Language.first.id), class: 'form-control select', style: "width: 100px"
26 .form-group
26 .form-group
27 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
27 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
28 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
28 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
29 - # latest submission status
29 - # latest submission status
30 - .panel.panel-info
30 + .panel{class: (@submission && @submission.graded_at) ? "panel-info" : "panel-warning"}
31 .panel-heading
31 .panel-heading
32 Latest Submission Status
32 Latest Submission Status
33 = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission
33 = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission
34 .panel-body
34 .panel-body
35 - - if @submission
35 + %div#latest_status
36 - = render :partial => 'submission_short',
36 + - if @submission
37 - :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
37 + = render :partial => 'submission_short',
38 + :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
38 .row
39 .row
39 .col-md-12
40 .col-md-12
40 %h2 Console
41 %h2 Console
41 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
42 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
42
43
43 :javascript
44 :javascript
44 $(document).ready(function() {
45 $(document).ready(function() {
45 e = ace.edit("editor")
46 e = ace.edit("editor")
46 e.setValue($("#text_sourcecode").val());
47 e.setValue($("#text_sourcecode").val());
47 e.gotoLine(1);
48 e.gotoLine(1);
48 $("#language_id").trigger('change');
49 $("#language_id").trigger('change');
49 brython();
50 brython();
50 });
51 });
51
52
52
53
53 %script#__main__{type:'text/python3'}
54 %script#__main__{type:'text/python3'}
54 :plain
55 :plain
55 import sys
56 import sys
56 import traceback
57 import traceback
57
58
58 from browser import document as doc
59 from browser import document as doc
59 from browser import window, alert, console
60 from browser import window, alert, console
60
61
61 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
62 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
62 for supporting Python development. See www.python.org for more information."""
63 for supporting Python development. See www.python.org for more information."""
63
64
64 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
65 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
65 All Rights Reserved.
66 All Rights Reserved.
66
67
67 Copyright (c) 2001-2013 Python Software Foundation.
68 Copyright (c) 2001-2013 Python Software Foundation.
68 All Rights Reserved.
69 All Rights Reserved.
69
70
70 Copyright (c) 2000 BeOpen.com.
71 Copyright (c) 2000 BeOpen.com.
71 All Rights Reserved.
72 All Rights Reserved.
72
73
73 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
74 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
74 All Rights Reserved.
75 All Rights Reserved.
75
76
76 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
77 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
77 All Rights Reserved."""
78 All Rights Reserved."""
78
79
79 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
80 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
80 All rights reserved.
81 All rights reserved.
81
82
82 Redistribution and use in source and binary forms, with or without
83 Redistribution and use in source and binary forms, with or without
83 modification, are permitted provided that the following conditions are met:
84 modification, are permitted provided that the following conditions are met:
84
85
85 Redistributions of source code must retain the above copyright notice, this
86 Redistributions of source code must retain the above copyright notice, this
@@ -1,2 +1,2
1 :plain
1 :plain
2 - $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name}})}")
2 + $("#latest_status").html("#{j render({partial: 'submission_short', locals: {submission: @submission, problem_name: @problem.name, problem_id: @problem.id}})}")
@@ -20,53 +20,53
20
20
21 # Only load the plugins named here, in the order given (default is alphabetical).
21 # Only load the plugins named here, in the order given (default is alphabetical).
22 # :all can be used as a placeholder for all plugins not explicitly named.
22 # :all can be used as a placeholder for all plugins not explicitly named.
23 # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
23 # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24
24
25 # Activate observers that should always be running.
25 # Activate observers that should always be running.
26 # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
26 # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
27
27
28 # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
28 # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29 # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
29 # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
30 config.time_zone = 'UTC'
30 config.time_zone = 'UTC'
31
31
32 # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
32 # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
33 # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
33 # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
34 config.i18n.default_locale = :en
34 config.i18n.default_locale = :en
35
35
36 # Configure the default encoding used in templates for Ruby 1.9.
36 # Configure the default encoding used in templates for Ruby 1.9.
37 config.encoding = "utf-8"
37 config.encoding = "utf-8"
38
38
39 # Configure sensitive parameters which will be filtered from the log file.
39 # Configure sensitive parameters which will be filtered from the log file.
40 config.filter_parameters += [:password]
40 config.filter_parameters += [:password]
41
41
42 # Enable escaping HTML in JSON.
42 # Enable escaping HTML in JSON.
43 config.active_support.escape_html_entities_in_json = true
43 config.active_support.escape_html_entities_in_json = true
44
44
45 # Use SQL instead of Active Record's schema dumper when creating the database.
45 # Use SQL instead of Active Record's schema dumper when creating the database.
46 # This is necessary if your schema can't be completely dumped by the schema dumper,
46 # This is necessary if your schema can't be completely dumped by the schema dumper,
47 # like if you have constraints or database-specific column types
47 # like if you have constraints or database-specific column types
48 # config.active_record.schema_format = :sql
48 # config.active_record.schema_format = :sql
49
49
50 # Enable the asset pipeline
50 # Enable the asset pipeline
51 config.assets.enabled = true
51 config.assets.enabled = true
52
52
53 # Version of your assets, change this if you want to expire all your assets
53 # Version of your assets, change this if you want to expire all your assets
54 config.assets.version = '1.0'
54 config.assets.version = '1.0'
55
55
56 # ---------------- IMPORTANT ----------------------
56 # ---------------- IMPORTANT ----------------------
57 # If we deploy the app into a subdir name "grader", be sure to do "rake assets:precompile RAILS_RELATIVE_URL_ROOT=/grader"
57 # If we deploy the app into a subdir name "grader", be sure to do "rake assets:precompile RAILS_RELATIVE_URL_ROOT=/grader"
58 # moreover, using the following line instead also known to works
58 # moreover, using the following line instead also known to works
59 #config.action_controller.relative_url_root = '/grader'
59 #config.action_controller.relative_url_root = '/grader'
60
60
61 #font path
61 #font path
62 config.assets.paths << "#{Rails}/vendor/assets/fonts"
62 config.assets.paths << "#{Rails}/vendor/assets/fonts"
63
63
64 config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js']
64 config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js']
65 config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css']
65 config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css']
66 %w( announcements submissions configurations contests contest_management graders heartbeat
66 %w( announcements submissions configurations contests contest_management graders heartbeat
67 login main messages problems report site sites sources tasks
67 login main messages problems report site sites sources tasks
68 - test user_admin users ).each do |controller|
68 + test user_admin users testcases).each do |controller|
69 config.assets.precompile += ["#{controller}.js", "#{controller}.css"]
69 config.assets.precompile += ["#{controller}.js", "#{controller}.css"]
70 end
70 end
71 end
71 end
72 end
72 end
@@ -1,23 +1,23
1 # Be sure to restart your server when you modify this file.
1 # Be sure to restart your server when you modify this file.
2
2
3 # Version of your assets, change this if you want to expire all your assets.
3 # Version of your assets, change this if you want to expire all your assets.
4 Rails.application.config.assets.version = '1.0'
4 Rails.application.config.assets.version = '1.0'
5
5
6 # Add additional assets to the asset load path.
6 # Add additional assets to the asset load path.
7 # Rails.application.config.assets.paths << Emoji.images_path
7 # Rails.application.config.assets.paths << Emoji.images_path
8 # Add Yarn node_modules folder to the asset load path.
8 # Add Yarn node_modules folder to the asset load path.
9 Rails.application.config.assets.paths << Rails.root.join('node_modules')
9 Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 Rails.application.config.assets.paths << Rails.root.join('vendor/assets/fonts')
10 Rails.application.config.assets.paths << Rails.root.join('vendor/assets/fonts')
11
11
12 # Precompile additional assets.
12 # Precompile additional assets.
13 # application.js, application.css, and all non-JS/CSS in the app/assets
13 # application.js, application.css, and all non-JS/CSS in the app/assets
14 # folder are already added.
14 # folder are already added.
15 # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 # Rails.application.config.assets.precompile += %w( admin.js admin.css )
16
16
17 Rails.application.config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js']
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']
18 Rails.application.config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css']
19 %w( announcements submissions configurations contests contest_management graders heartbeat
19 %w( announcements submissions configurations contests contest_management graders heartbeat
20 login main messages problems report site sites sources tasks groups
20 login main messages problems report site sites sources tasks groups
21 - test user_admin users tags).each do |controller|
21 + test user_admin users tags testcases).each do |controller|
22 Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.css"]
22 Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.css"]
23 end
23 end
@@ -1,73 +1,74
1 module GraderScript
1 module GraderScript
2
2
3 def self.grader_control_enabled?
3 def self.grader_control_enabled?
4 if defined? GRADER_ROOT_DIR
4 if defined? GRADER_ROOT_DIR
5 GRADER_ROOT_DIR != ''
5 GRADER_ROOT_DIR != ''
6 else
6 else
7 false
7 false
8 end
8 end
9 end
9 end
10
10
11 def self.raw_dir
11 def self.raw_dir
12 File.join GRADER_ROOT_DIR, "raw"
12 File.join GRADER_ROOT_DIR, "raw"
13 end
13 end
14
14
15 def self.call_grader(params)
15 def self.call_grader(params)
16 if GraderScript.grader_control_enabled?
16 if GraderScript.grader_control_enabled?
17 cmd = File.join(GRADER_ROOT_DIR, "scripts/grader") + " " + params
17 cmd = File.join(GRADER_ROOT_DIR, "scripts/grader") + " " + params
18 system(cmd)
18 system(cmd)
19 end
19 end
20 end
20 end
21
21
22 def self.stop_grader(pid)
22 def self.stop_grader(pid)
23 GraderScript.call_grader "stop #{pid}"
23 GraderScript.call_grader "stop #{pid}"
24 end
24 end
25
25
26 def self.stop_graders(pids)
26 def self.stop_graders(pids)
27 pid_str = (pids.map { |process| process.pid.to_s }).join ' '
27 pid_str = (pids.map { |process| process.pid.to_s }).join ' '
28 GraderScript.call_grader "stop #{pid_str}"
28 GraderScript.call_grader "stop #{pid_str}"
29 end
29 end
30
30
31 def self.start_grader(env)
31 def self.start_grader(env)
32 GraderScript.call_grader "#{env} queue --err-log &"
32 GraderScript.call_grader "#{env} queue --err-log &"
33 GraderScript.call_grader "#{env} test_request -err-log &"
33 GraderScript.call_grader "#{env} test_request -err-log &"
34 end
34 end
35
35
36 + #call the import problem script
36 def self.call_import_problem(problem_name,
37 def self.call_import_problem(problem_name,
37 problem_dir,
38 problem_dir,
38 time_limit=1,
39 time_limit=1,
39 memory_limit=32,
40 memory_limit=32,
40 checker_name='text')
41 checker_name='text')
41 if GraderScript.grader_control_enabled?
42 if GraderScript.grader_control_enabled?
42 cur_dir = `pwd`.chomp
43 cur_dir = `pwd`.chomp
43 Dir.chdir(GRADER_ROOT_DIR)
44 Dir.chdir(GRADER_ROOT_DIR)
44
45
45 script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem")
46 script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem")
46 cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" +
47 cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" +
47 " -t #{time_limit} -m #{memory_limit}"
48 " -t #{time_limit} -m #{memory_limit}"
48
49
49 output = `#{cmd}`
50 output = `#{cmd}`
50
51
51 Dir.chdir(cur_dir)
52 Dir.chdir(cur_dir)
52
53
53 return "import CMD: #{cmd}\n" + output
54 return "import CMD: #{cmd}\n" + output
54 end
55 end
55 return ''
56 return ''
56 end
57 end
57
58
58 def self.call_import_testcase(problem_name)
59 def self.call_import_testcase(problem_name)
59 if GraderScript.grader_control_enabled?
60 if GraderScript.grader_control_enabled?
60 cur_dir = `pwd`.chomp
61 cur_dir = `pwd`.chomp
61 Dir.chdir(GRADER_ROOT_DIR)
62 Dir.chdir(GRADER_ROOT_DIR)
62
63
63 script_name = File.join(GRADER_ROOT_DIR, "scripts/load_testcase")
64 script_name = File.join(GRADER_ROOT_DIR, "scripts/load_testcase")
64 cmd = "#{script_name} #{problem_name}"
65 cmd = "#{script_name} #{problem_name}"
65
66
66 output = `#{cmd}`
67 output = `#{cmd}`
67
68
68 Dir.chdir(cur_dir)
69 Dir.chdir(cur_dir)
69 return "Testcase import result:\n" + output
70 return "Testcase import result:\n" + output
70 end
71 end
71 end
72 end
72
73
73 end
74 end
@@ -1,102 +1,104
1 require 'tmpdir'
1 require 'tmpdir'
2
2
3 class TestdataImporter
3 class TestdataImporter
4
4
5 attr :log_msg
5 attr :log_msg
6
6
7 def initialize(problem)
7 def initialize(problem)
8 @problem = problem
8 @problem = problem
9 end
9 end
10
10
11 - def import_from_file(tempfile,
11 + #Create or update problem according to the parameter
12 - time_limit,
12 + def import_from_file(tempfile,
13 + time_limit,
13 memory_limit,
14 memory_limit,
14 checker_name='text',
15 checker_name='text',
15 import_to_db=false)
16 import_to_db=false)
16
17
17 dirname = extract(tempfile)
18 dirname = extract(tempfile)
18 return false if not dirname
19 return false if not dirname
19 if not import_to_db
20 if not import_to_db
20 @log_msg = GraderScript.call_import_problem(@problem.name,
21 @log_msg = GraderScript.call_import_problem(@problem.name,
21 dirname,
22 dirname,
22 time_limit,
23 time_limit,
23 memory_limit,
24 memory_limit,
24 checker_name)
25 checker_name)
25 else
26 else
26 # Import test data to test pairs.
27 # Import test data to test pairs.
27
28
28 @problem.test_pairs.clear
29 @problem.test_pairs.clear
29 if import_test_pairs(dirname)
30 if import_test_pairs(dirname)
30 test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}"
31 test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}"
31 @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)"
32 @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)"
32 else
33 else
33 @log_msg = "Importing test pair failed. (0 test pairs imported)"
34 @log_msg = "Importing test pair failed. (0 test pairs imported)"
34 end
35 end
35 end
36 end
36
37
37 @log_msg << import_problem_description(dirname)
38 @log_msg << import_problem_description(dirname)
38 @log_msg << import_problem_pdf(dirname)
39 @log_msg << import_problem_pdf(dirname)
39 @log_msg << import_full_score(dirname)
40 @log_msg << import_full_score(dirname)
40
41
41 #import test data
42 #import test data
42 @log_msg << GraderScript.call_import_testcase(@problem.name)
43 @log_msg << GraderScript.call_import_testcase(@problem.name)
43
44
44 return true
45 return true
45 end
46 end
46
47
47 protected
48 protected
48
49
49 def self.long_ext(filename)
50 def self.long_ext(filename)
50 i = filename.index('.')
51 i = filename.index('.')
51 len = filename.length
52 len = filename.length
52 return filename.slice(i..len)
53 return filename.slice(i..len)
53 end
54 end
54
55
56 + # extract an archive file located at +tempfile+ to the +raw_dir+
55 def extract(tempfile)
57 def extract(tempfile)
56 testdata_filename = save_testdata_file(tempfile)
58 testdata_filename = save_testdata_file(tempfile)
57 ext = TestdataImporter.long_ext(tempfile.original_filename)
59 ext = TestdataImporter.long_ext(tempfile.original_filename)
58
60
59 extract_dir = File.join(GraderScript.raw_dir, @problem.name)
61 extract_dir = File.join(GraderScript.raw_dir, @problem.name)
60 if File.exists? extract_dir
62 if File.exists? extract_dir
61 backup_count = 0
63 backup_count = 0
62 begin
64 begin
63 backup_count += 1
65 backup_count += 1
64 backup_dirname = "#{extract_dir}.backup.#{backup_count}"
66 backup_dirname = "#{extract_dir}.backup.#{backup_count}"
65 end while File.exists? backup_dirname
67 end while File.exists? backup_dirname
66 File.rename(extract_dir, backup_dirname)
68 File.rename(extract_dir, backup_dirname)
67 end
69 end
68 Dir.mkdir extract_dir
70 Dir.mkdir extract_dir
69
71
70 if ext=='.tar.gz' or ext=='.tgz'
72 if ext=='.tar.gz' or ext=='.tgz'
71 cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}"
73 cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}"
72 elsif ext=='.tar'
74 elsif ext=='.tar'
73 cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}"
75 cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}"
74 elsif ext=='.zip'
76 elsif ext=='.zip'
75 cmd = "unzip -o #{testdata_filename} -d #{extract_dir}"
77 cmd = "unzip -o #{testdata_filename} -d #{extract_dir}"
76 else
78 else
77 return nil
79 return nil
78 end
80 end
79
81
80 system(cmd)
82 system(cmd)
81
83
82 files = Dir["#{extract_dir}/**/*1*.in"]
84 files = Dir["#{extract_dir}/**/*1*.in"]
83 return nil if files.length==0
85 return nil if files.length==0
84
86
85 File.delete(testdata_filename)
87 File.delete(testdata_filename)
86
88
87 return File.dirname(files[0])
89 return File.dirname(files[0])
88 end
90 end
89
91
90 def save_testdata_file(tempfile)
92 def save_testdata_file(tempfile)
91 ext = TestdataImporter.long_ext(tempfile.original_filename)
93 ext = TestdataImporter.long_ext(tempfile.original_filename)
92 testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}")
94 testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}")
93
95
94 return nil if tempfile==""
96 return nil if tempfile==""
95
97
96 if tempfile.instance_of?(Tempfile)
98 if tempfile.instance_of?(Tempfile)
97 tempfile.close
99 tempfile.close
98 FileUtils.move(tempfile.path,testdata_filename)
100 FileUtils.move(tempfile.path,testdata_filename)
99 else
101 else
100 File.open(testdata_filename, "wb") do |f|
102 File.open(testdata_filename, "wb") do |f|
101 f.write(tempfile.read)
103 f.write(tempfile.read)
102 end
104 end
You need to be logged in to leave comments. Login now