Description:
fix allow admin to submit to any problem
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r682:405106fcdb33 - - 7 files changed: 18 inserted, 6 deleted

@@ -1,376 +1,376
1 class MainController < ApplicationController
1 class MainController < ApplicationController
2
2
3 before_filter :authenticate, :except => [:index, :login]
3 before_filter :authenticate, :except => [:index, :login]
4 before_filter :check_viewability, :except => [:index, :login]
4 before_filter :check_viewability, :except => [:index, :login]
5
5
6 append_before_filter :confirm_and_update_start_time,
6 append_before_filter :confirm_and_update_start_time,
7 :except => [:index,
7 :except => [:index,
8 :login,
8 :login,
9 :confirm_contest_start]
9 :confirm_contest_start]
10
10
11 # to prevent log in box to be shown when user logged out of the
11 # to prevent log in box to be shown when user logged out of the
12 # system only in some tab
12 # system only in some tab
13 prepend_before_filter :reject_announcement_refresh_when_logged_out,
13 prepend_before_filter :reject_announcement_refresh_when_logged_out,
14 :only => [:announcements]
14 :only => [:announcements]
15
15
16 before_filter :authenticate_by_ip_address, :only => [:list]
16 before_filter :authenticate_by_ip_address, :only => [:list]
17
17
18 # COMMENTED OUT: filter in each action instead
18 # COMMENTED OUT: filter in each action instead
19 # before_filter :verify_time_limit, :only => [:submit]
19 # before_filter :verify_time_limit, :only => [:submit]
20
20
21 verify :method => :post, :only => [:submit],
21 verify :method => :post, :only => [:submit],
22 :redirect_to => { :action => :index }
22 :redirect_to => { :action => :index }
23
23
24 # COMMENT OUT: only need when having high load
24 # COMMENT OUT: only need when having high load
25 # caches_action :index, :login
25 # caches_action :index, :login
26
26
27 # NOTE: This method is not actually needed, 'config/routes.rb' has
27 # NOTE: This method is not actually needed, 'config/routes.rb' has
28 # assigned action login as a default action.
28 # assigned action login as a default action.
29 def index
29 def index
30 redirect_to :action => 'login'
30 redirect_to :action => 'login'
31 end
31 end
32
32
33 def login
33 def login
34 saved_notice = flash[:notice]
34 saved_notice = flash[:notice]
35 reset_session
35 reset_session
36 flash.now[:notice] = saved_notice
36 flash.now[:notice] = saved_notice
37
37
38 # EXPERIMENT:
38 # EXPERIMENT:
39 # Hide login if in single user mode and the url does not
39 # Hide login if in single user mode and the url does not
40 # explicitly specify /login
40 # explicitly specify /login
41 #
41 #
42 # logger.info "PATH: #{request.path}"
42 # logger.info "PATH: #{request.path}"
43 # if GraderConfiguration['system.single_user_mode'] and
43 # if GraderConfiguration['system.single_user_mode'] and
44 # request.path!='/main/login'
44 # request.path!='/main/login'
45 # @hidelogin = true
45 # @hidelogin = true
46 # end
46 # end
47
47
48 @announcements = Announcement.frontpage
48 @announcements = Announcement.frontpage
49 render :action => 'login', :layout => 'empty'
49 render :action => 'login', :layout => 'empty'
50 end
50 end
51
51
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?
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 :action => 'list'
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
149 end
149 end
150 case_num = params[:num].to_i
150 case_num = params[:num].to_i
151 out_filename = output_filename(@user.login,
151 out_filename = output_filename(@user.login,
152 @submission.problem.name,
152 @submission.problem.name,
153 @submission.id,
153 @submission.id,
154 case_num)
154 case_num)
155 if !FileTest.exists?(out_filename)
155 if !FileTest.exists?(out_filename)
156 flash[:notice] = 'Output not found.'
156 flash[:notice] = 'Output not found.'
157 redirect_to :action => 'list' and return
157 redirect_to :action => 'list' and return
158 end
158 end
159
159
160 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
160 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
161 response.headers['Content-Type'] = "application/force-download"
161 response.headers['Content-Type'] = "application/force-download"
162 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
162 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
163 response.headers["X-Sendfile"] = out_filename
163 response.headers["X-Sendfile"] = out_filename
164 response.headers['Content-length'] = File.size(out_filename)
164 response.headers['Content-length'] = File.size(out_filename)
165 render :nothing => true
165 render :nothing => true
166 else
166 else
167 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
167 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
168 end
168 end
169 end
169 end
170
170
171 def error
171 def error
172 @user = User.find(session[:user_id])
172 @user = User.find(session[:user_id])
173 end
173 end
174
174
175 # announcement refreshing and hiding methods
175 # announcement refreshing and hiding methods
176
176
177 def announcements
177 def announcements
178 if params.has_key? 'recent'
178 if params.has_key? 'recent'
179 prepare_announcements(params[:recent])
179 prepare_announcements(params[:recent])
180 else
180 else
181 prepare_announcements
181 prepare_announcements
182 end
182 end
183 render(:partial => 'announcement',
183 render(:partial => 'announcement',
184 :collection => @announcements,
184 :collection => @announcements,
185 :locals => {:announcement_effect => true})
185 :locals => {:announcement_effect => true})
186 end
186 end
187
187
188 def confirm_contest_start
188 def confirm_contest_start
189 user = User.find(session[:user_id])
189 user = User.find(session[:user_id])
190 if request.method == 'POST'
190 if request.method == 'POST'
191 user.update_start_time
191 user.update_start_time
192 redirect_to :action => 'list'
192 redirect_to :action => 'list'
193 else
193 else
194 @contests = user.contests
194 @contests = user.contests
195 @user = user
195 @user = user
196 end
196 end
197 end
197 end
198
198
199 protected
199 protected
200
200
201 def prepare_announcements(recent=nil)
201 def prepare_announcements(recent=nil)
202 if GraderConfiguration.show_tasks_to?(@user)
202 if GraderConfiguration.show_tasks_to?(@user)
203 @announcements = Announcement.published(true)
203 @announcements = Announcement.published(true)
204 else
204 else
205 @announcements = Announcement.published
205 @announcements = Announcement.published
206 end
206 end
207 if recent!=nil
207 if recent!=nil
208 recent_id = recent.to_i
208 recent_id = recent.to_i
209 @announcements = @announcements.find_all { |a| a.id > recent_id }
209 @announcements = @announcements.find_all { |a| a.id > recent_id }
210 end
210 end
211 end
211 end
212
212
213 def prepare_list_information
213 def prepare_list_information
214 @user = User.find(session[:user_id])
214 @user = User.find(session[:user_id])
215 if not GraderConfiguration.multicontests?
215 if not GraderConfiguration.multicontests?
216 @problems = @user.available_problems
216 @problems = @user.available_problems
217 else
217 else
218 @contest_problems = @user.available_problems_group_by_contests
218 @contest_problems = @user.available_problems_group_by_contests
219 @problems = @user.available_problems
219 @problems = @user.available_problems
220 end
220 end
221 @prob_submissions = {}
221 @prob_submissions = {}
222 @problems.each do |p|
222 @problems.each do |p|
223 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
223 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
224 if sub!=nil
224 if sub!=nil
225 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
225 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
226 else
226 else
227 @prob_submissions[p.id] = { :count => 0, :submission => nil }
227 @prob_submissions[p.id] = { :count => 0, :submission => nil }
228 end
228 end
229 end
229 end
230 prepare_announcements
230 prepare_announcements
231 end
231 end
232
232
233 def check_viewability
233 def check_viewability
234 @user = User.find(session[:user_id])
234 @user = User.find(session[:user_id])
235 if (!GraderConfiguration.show_tasks_to?(@user)) and
235 if (!GraderConfiguration.show_tasks_to?(@user)) and
236 ((action_name=='submission') or (action_name=='submit'))
236 ((action_name=='submission') or (action_name=='submit'))
237 redirect_to :action => 'list' and return
237 redirect_to :action => 'list' and return
238 end
238 end
239 end
239 end
240
240
241 def prepare_grading_result(submission)
241 def prepare_grading_result(submission)
242 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
242 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
243 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
243 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
244 else
244 else
245 # guess task info from problem.full_score
245 # guess task info from problem.full_score
246 cases = submission.problem.full_score / 10
246 cases = submission.problem.full_score / 10
247 grading_info = {
247 grading_info = {
248 'testruns' => cases,
248 'testruns' => cases,
249 'testcases' => cases
249 'testcases' => cases
250 }
250 }
251 end
251 end
252 @test_runs = []
252 @test_runs = []
253 if grading_info['testruns'].is_a? Integer
253 if grading_info['testruns'].is_a? Integer
254 trun_count = grading_info['testruns']
254 trun_count = grading_info['testruns']
255 trun_count.times do |i|
255 trun_count.times do |i|
256 @test_runs << [ read_grading_result(@user.login,
256 @test_runs << [ read_grading_result(@user.login,
257 submission.problem.name,
257 submission.problem.name,
258 submission.id,
258 submission.id,
259 i+1) ]
259 i+1) ]
260 end
260 end
261 else
261 else
262 grading_info['testruns'].keys.sort.each do |num|
262 grading_info['testruns'].keys.sort.each do |num|
263 run = []
263 run = []
264 testrun = grading_info['testruns'][num]
264 testrun = grading_info['testruns'][num]
265 testrun.each do |c|
265 testrun.each do |c|
266 run << read_grading_result(@user.login,
266 run << read_grading_result(@user.login,
267 submission.problem.name,
267 submission.problem.name,
268 submission.id,
268 submission.id,
269 c)
269 c)
270 end
270 end
271 @test_runs << run
271 @test_runs << run
272 end
272 end
273 end
273 end
274 end
274 end
275
275
276 def grading_result_dir(user_name, problem_name, submission_id, case_num)
276 def grading_result_dir(user_name, problem_name, submission_id, case_num)
277 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
277 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
278 end
278 end
279
279
280 def output_filename(user_name, problem_name, submission_id, case_num)
280 def output_filename(user_name, problem_name, submission_id, case_num)
281 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
281 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
282 return "#{dir}/output.txt"
282 return "#{dir}/output.txt"
283 end
283 end
284
284
285 def read_grading_result(user_name, problem_name, submission_id, case_num)
285 def read_grading_result(user_name, problem_name, submission_id, case_num)
286 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
286 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
287 result_file_name = "#{dir}/result"
287 result_file_name = "#{dir}/result"
288 if !FileTest.exists?(result_file_name)
288 if !FileTest.exists?(result_file_name)
289 return {:num => case_num, :msg => 'program did not run'}
289 return {:num => case_num, :msg => 'program did not run'}
290 else
290 else
291 results = File.open(result_file_name).readlines
291 results = File.open(result_file_name).readlines
292 run_stat = extract_running_stat(results)
292 run_stat = extract_running_stat(results)
293 output_filename = "#{dir}/output.txt"
293 output_filename = "#{dir}/output.txt"
294 if FileTest.exists?(output_filename)
294 if FileTest.exists?(output_filename)
295 output_file = true
295 output_file = true
296 output_size = File.size(output_filename)
296 output_size = File.size(output_filename)
297 else
297 else
298 output_file = false
298 output_file = false
299 output_size = 0
299 output_size = 0
300 end
300 end
301
301
302 return {
302 return {
303 :num => case_num,
303 :num => case_num,
304 :msg => results[0],
304 :msg => results[0],
305 :run_stat => run_stat,
305 :run_stat => run_stat,
306 :output => output_file,
306 :output => output_file,
307 :output_size => output_size
307 :output_size => output_size
308 }
308 }
309 end
309 end
310 end
310 end
311
311
312 # copied from grader/script/lib/test_request_helper.rb
312 # copied from grader/script/lib/test_request_helper.rb
313 def extract_running_stat(results)
313 def extract_running_stat(results)
314 running_stat_line = results[-1]
314 running_stat_line = results[-1]
315
315
316 # extract exit status line
316 # extract exit status line
317 run_stat = ""
317 run_stat = ""
318 if !(/[Cc]orrect/.match(results[0]))
318 if !(/[Cc]orrect/.match(results[0]))
319 run_stat = results[0].chomp
319 run_stat = results[0].chomp
320 else
320 else
321 run_stat = 'Program exited normally'
321 run_stat = 'Program exited normally'
322 end
322 end
323
323
324 logger.info "Stat line: #{running_stat_line}"
324 logger.info "Stat line: #{running_stat_line}"
325
325
326 # extract running time
326 # extract running time
327 if res = /r(.*)u(.*)s/.match(running_stat_line)
327 if res = /r(.*)u(.*)s/.match(running_stat_line)
328 seconds = (res[1].to_f + res[2].to_f)
328 seconds = (res[1].to_f + res[2].to_f)
329 time_stat = "Time used: #{seconds} sec."
329 time_stat = "Time used: #{seconds} sec."
330 else
330 else
331 seconds = nil
331 seconds = nil
332 time_stat = "Time used: n/a sec."
332 time_stat = "Time used: n/a sec."
333 end
333 end
334
334
335 # extract memory usage
335 # extract memory usage
336 if res = /s(.*)m/.match(running_stat_line)
336 if res = /s(.*)m/.match(running_stat_line)
337 memory_used = res[1].to_i
337 memory_used = res[1].to_i
338 else
338 else
339 memory_used = -1
339 memory_used = -1
340 end
340 end
341
341
342 return {
342 return {
343 :msg => "#{run_stat}\n#{time_stat}",
343 :msg => "#{run_stat}\n#{time_stat}",
344 :running_time => seconds,
344 :running_time => seconds,
345 :exit_status => run_stat,
345 :exit_status => run_stat,
346 :memory_usage => memory_used
346 :memory_usage => memory_used
347 }
347 }
348 end
348 end
349
349
350 def confirm_and_update_start_time
350 def confirm_and_update_start_time
351 user = User.find(session[:user_id])
351 user = User.find(session[:user_id])
352 if (GraderConfiguration.indv_contest_mode? and
352 if (GraderConfiguration.indv_contest_mode? and
353 GraderConfiguration['contest.confirm_indv_contest_start'] and
353 GraderConfiguration['contest.confirm_indv_contest_start'] and
354 !user.contest_started?)
354 !user.contest_started?)
355 redirect_to :action => 'confirm_contest_start' and return
355 redirect_to :action => 'confirm_contest_start' and return
356 end
356 end
357 if not GraderConfiguration.analysis_mode?
357 if not GraderConfiguration.analysis_mode?
358 user.update_start_time
358 user.update_start_time
359 end
359 end
360 end
360 end
361
361
362 def reject_announcement_refresh_when_logged_out
362 def reject_announcement_refresh_when_logged_out
363 if not session[:user_id]
363 if not session[:user_id]
364 render :text => 'Access forbidden', :status => 403
364 render :text => 'Access forbidden', :status => 403
365 end
365 end
366
366
367 if GraderConfiguration.multicontests?
367 if GraderConfiguration.multicontests?
368 user = User.find(session[:user_id])
368 user = User.find(session[:user_id])
369 if user.contest_stat.forced_logout
369 if user.contest_stat.forced_logout
370 render :text => 'Access forbidden', :status => 403
370 render :text => 'Access forbidden', :status => 403
371 end
371 end
372 end
372 end
373 end
373 end
374
374
375 end
375 end
376
376
@@ -1,108 +1,113
1 class SubmissionsController < ApplicationController
1 class SubmissionsController < ApplicationController
2 before_action :authenticate
2 before_action :authenticate
3 before_action :submission_authorization, only: [:show, :direct_edit_submission, :download, :edit]
3 before_action :submission_authorization, only: [:show, :direct_edit_submission, :download, :edit]
4 before_action :admin_authorization, only: [:rejudge]
4 before_action :admin_authorization, only: [:rejudge]
5
5
6 # GET /submissions
6 # GET /submissions
7 # GET /submissions.json
7 # GET /submissions.json
8 # Show problem selection and user's submission of that problem
8 # Show problem selection and user's submission of that problem
9 def index
9 def index
10 @user = @current_user
10 @user = @current_user
11 @problems = @user.available_problems
11 @problems = @user.available_problems
12
12
13 if params[:problem_id]==nil
13 if params[:problem_id]==nil
14 @problem = nil
14 @problem = nil
15 @submissions = nil
15 @submissions = nil
16 else
16 else
17 @problem = Problem.find_by_id(params[:problem_id])
17 @problem = Problem.find_by_id(params[:problem_id])
18 if (@problem == nil) or (not @problem.available)
18 if (@problem == nil) or (not @problem.available)
19 redirect_to main_list_path
19 redirect_to main_list_path
20 flash[:notice] = 'Error: submissions for that problem are not viewable.'
20 flash[:notice] = 'Error: submissions for that problem are not viewable.'
21 return
21 return
22 end
22 end
23 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc)
23 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc)
24 end
24 end
25 end
25 end
26
26
27 # GET /submissions/1
27 # GET /submissions/1
28 # GET /submissions/1.json
28 # GET /submissions/1.json
29 def show
29 def show
30 @submission = Submission.find(params[:id])
30 @submission = Submission.find(params[:id])
31
31
32 #log the viewing
32 #log the viewing
33 user = User.find(session[:user_id])
33 user = User.find(session[:user_id])
34 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
34 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
35
35
36 @task = @submission.task
36 @task = @submission.task
37 end
37 end
38
38
39 def download
39 def download
40 @submission = Submission.find(params[:id])
40 @submission = Submission.find(params[:id])
41 send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
41 send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
42 end
42 end
43
43
44 def compiler_msg
44 def compiler_msg
45 @submission = Submission.find(params[:id])
45 @submission = Submission.find(params[:id])
46 respond_to do |format|
46 respond_to do |format|
47 format.js
47 format.js
48 end
48 end
49 end
49 end
50
50
51 #on-site new submission on specific problem
51 #on-site new submission on specific problem
52 def direct_edit_problem
52 def direct_edit_problem
53 @problem = Problem.find(params[:problem_id])
53 @problem = Problem.find(params[:problem_id])
54 @source = ''
54 @source = ''
55 + if (params[:user_id])
56 + u = User.find(params[:user_id])
57 + @submission = Submission.find_last_by_user_and_problem(u.id,@problem.id)
58 + @source = @submission.source.to_s if @submission and @submission.source
59 + end
55 render 'edit'
60 render 'edit'
56 end
61 end
57
62
58 # GET /submissions/1/edit
63 # GET /submissions/1/edit
59 def edit
64 def edit
60 @submission = Submission.find(params[:id])
65 @submission = Submission.find(params[:id])
61 @source = @submission.source.to_s
66 @source = @submission.source.to_s
62 @problem = @submission.problem
67 @problem = @submission.problem
63 @lang_id = @submission.language.id
68 @lang_id = @submission.language.id
64 end
69 end
65
70
66
71
67 def get_latest_submission_status
72 def get_latest_submission_status
68 @problem = Problem.find(params[:pid])
73 @problem = Problem.find(params[:pid])
69 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
74 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
70 puts User.find(params[:uid]).login
75 puts User.find(params[:uid]).login
71 puts Problem.find(params[:pid]).name
76 puts Problem.find(params[:pid]).name
72 puts 'nil' unless @submission
77 puts 'nil' unless @submission
73 respond_to do |format|
78 respond_to do |format|
74 format.js
79 format.js
75 end
80 end
76 end
81 end
77
82
78 # GET /submissions/:id/rejudge
83 # GET /submissions/:id/rejudge
79 def rejudge
84 def rejudge
80 @submission = Submission.find(params[:id])
85 @submission = Submission.find(params[:id])
81 @task = @submission.task
86 @task = @submission.task
82 @task.status_inqueue! if @task
87 @task.status_inqueue! if @task
83 respond_to do |format|
88 respond_to do |format|
84 format.js
89 format.js
85 end
90 end
86 end
91 end
87
92
88 protected
93 protected
89
94
90 def submission_authorization
95 def submission_authorization
91 #admin always has privileged
96 #admin always has privileged
92 if @current_user.admin?
97 if @current_user.admin?
93 return true
98 return true
94 end
99 end
95
100
96 sub = Submission.find(params[:id])
101 sub = Submission.find(params[:id])
97 if sub.problem.available?
102 if sub.problem.available?
98 puts "sub = #{sub.user.id}, current = #{@current_user.id}"
103 puts "sub = #{sub.user.id}, current = #{@current_user.id}"
99 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
104 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
100 end
105 end
101
106
102 #default to NO
107 #default to NO
103 unauthorized_redirect
108 unauthorized_redirect
104 return false
109 return false
105 end
110 end
106
111
107
112
108 end
113 end
@@ -1,162 +1,166
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 > 0
38 records = records.where('id <= ?',until_id) if until_id > 0
38 records = records.where('id <= ?',until_id) if 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
87 end
87 end
88
88
89 def self.find_language_in_source(source, source_filename="")
89 def self.find_language_in_source(source, source_filename="")
90 langopt = find_option_in_source(/^LANG:/,source)
90 langopt = find_option_in_source(/^LANG:/,source)
91 if langopt
91 if langopt
92 return (Language.find_by_name(langopt) ||
92 return (Language.find_by_name(langopt) ||
93 Language.find_by_pretty_name(langopt))
93 Language.find_by_pretty_name(langopt))
94 else
94 else
95 if source_filename
95 if source_filename
96 return Language.find_by_extension(source_filename.split('.').last)
96 return Language.find_by_extension(source_filename.split('.').last)
97 else
97 else
98 return nil
98 return nil
99 end
99 end
100 end
100 end
101 end
101 end
102
102
103 def self.find_problem_in_source(source, source_filename="")
103 def self.find_problem_in_source(source, source_filename="")
104 prob_opt = find_option_in_source(/^TASK:/,source)
104 prob_opt = find_option_in_source(/^TASK:/,source)
105 if problem = Problem.find_by_name(prob_opt)
105 if problem = Problem.find_by_name(prob_opt)
106 return problem
106 return problem
107 else
107 else
108 if source_filename
108 if source_filename
109 return Problem.find_by_name(source_filename.split('.').first)
109 return Problem.find_by_name(source_filename.split('.').first)
110 else
110 else
111 return nil
111 return nil
112 end
112 end
113 end
113 end
114 end
114 end
115
115
116 def assign_problem
116 def assign_problem
117 if self.problem_id!=-1
117 if self.problem_id!=-1
118 begin
118 begin
119 self.problem = Problem.find(self.problem_id)
119 self.problem = Problem.find(self.problem_id)
120 rescue ActiveRecord::RecordNotFound
120 rescue ActiveRecord::RecordNotFound
121 self.problem = nil
121 self.problem = nil
122 end
122 end
123 else
123 else
124 self.problem = Submission.find_problem_in_source(self.source,
124 self.problem = Submission.find_problem_in_source(self.source,
125 self.source_filename)
125 self.source_filename)
126 end
126 end
127 end
127 end
128
128
129 def assign_language
129 def assign_language
130 self.language = Submission.find_language_in_source(self.source,
130 self.language = Submission.find_language_in_source(self.source,
131 self.source_filename)
131 self.source_filename)
132 end
132 end
133
133
134 # validation codes
134 # validation codes
135 def must_specify_language
135 def must_specify_language
136 return if self.source==nil
136 return if self.source==nil
137
137
138 # for output_only tasks
138 # for output_only tasks
139 return if self.problem!=nil and self.problem.output_only
139 return if self.problem!=nil and self.problem.output_only
140 -
140 +
141 if self.language==nil
141 if self.language==nil
142 errors.add('source',"Cannot detect language. Did you submit a correct source file?") unless self.language!=nil
142 errors.add('source',"Cannot detect language. Did you submit a correct source file?") unless self.language!=nil
143 end
143 end
144 end
144 end
145
145
146 def must_have_valid_problem
146 def must_have_valid_problem
147 return if self.source==nil
147 return if self.source==nil
148 if self.problem==nil
148 if self.problem==nil
149 errors.add('problem',"must be specified.")
149 errors.add('problem',"must be specified.")
150 - elsif (!self.problem.available) and (self.new_record?)
150 + else
151 - errors.add('problem',"must be valid.")
151 + #admin always have right
152 + return if self.user.admin?
153 +
154 + #check if user has the right to submit the problem
155 + errors.add('problem',"must be valid.") if (!self.user.available_problem.include?(self.problem)) and (self.new_record?)
152 end
156 end
153 end
157 end
154
158
155 # callbacks
159 # callbacks
156 def assign_latest_number_if_new_recond
160 def assign_latest_number_if_new_recond
157 return if !self.new_record?
161 return if !self.new_record?
158 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
162 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
159 self.number = (latest==nil) ? 1 : latest.number + 1;
163 self.number = (latest==nil) ? 1 : latest.number + 1;
160 end
164 end
161
165
162 end
166 end
@@ -1,416 +1,417
1 require 'digest/sha1'
1 require 'digest/sha1'
2 require 'net/pop'
2 require 'net/pop'
3 require 'net/https'
3 require 'net/https'
4 require 'net/http'
4 require 'net/http'
5 require 'json'
5 require 'json'
6
6
7 class User < ActiveRecord::Base
7 class User < ActiveRecord::Base
8
8
9 has_and_belongs_to_many :roles
9 has_and_belongs_to_many :roles
10
10
11 #has_and_belongs_to_many :groups
11 #has_and_belongs_to_many :groups
12 has_many :groups_users, class_name: GroupUser
12 has_many :groups_users, class_name: GroupUser
13 has_many :groups, :through => :groups_users
13 has_many :groups, :through => :groups_users
14
14
15 has_many :test_requests, -> {order(submitted_at: DESC)}
15 has_many :test_requests, -> {order(submitted_at: DESC)}
16
16
17 has_many :messages, -> { order(created_at: DESC) },
17 has_many :messages, -> { order(created_at: DESC) },
18 :class_name => "Message",
18 :class_name => "Message",
19 :foreign_key => "sender_id"
19 :foreign_key => "sender_id"
20
20
21 has_many :replied_messages, -> { order(created_at: DESC) },
21 has_many :replied_messages, -> { order(created_at: DESC) },
22 :class_name => "Message",
22 :class_name => "Message",
23 :foreign_key => "receiver_id"
23 :foreign_key => "receiver_id"
24
24
25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
26
26
27 belongs_to :site
27 belongs_to :site
28 belongs_to :country
28 belongs_to :country
29
29
30 has_and_belongs_to_many :contests, -> { order(:name); uniq}
30 has_and_belongs_to_many :contests, -> { order(:name); uniq}
31
31
32 scope :activated_users, -> {where activated: true}
32 scope :activated_users, -> {where activated: true}
33
33
34 validates_presence_of :login
34 validates_presence_of :login
35 validates_uniqueness_of :login
35 validates_uniqueness_of :login
36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
37 validates_length_of :login, :within => 3..30
37 validates_length_of :login, :within => 3..30
38
38
39 validates_presence_of :full_name
39 validates_presence_of :full_name
40 validates_length_of :full_name, :minimum => 1
40 validates_length_of :full_name, :minimum => 1
41
41
42 validates_presence_of :password, :if => :password_required?
42 validates_presence_of :password, :if => :password_required?
43 validates_length_of :password, :within => 4..20, :if => :password_required?
43 validates_length_of :password, :within => 4..20, :if => :password_required?
44 validates_confirmation_of :password, :if => :password_required?
44 validates_confirmation_of :password, :if => :password_required?
45
45
46 validates_format_of :email,
46 validates_format_of :email,
47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
48 :if => :email_validation?
48 :if => :email_validation?
49 validate :uniqueness_of_email_from_activated_users,
49 validate :uniqueness_of_email_from_activated_users,
50 :if => :email_validation?
50 :if => :email_validation?
51 validate :enough_time_interval_between_same_email_registrations,
51 validate :enough_time_interval_between_same_email_registrations,
52 :if => :email_validation?
52 :if => :email_validation?
53
53
54 # these are for ytopc
54 # these are for ytopc
55 # disable for now
55 # disable for now
56 #validates_presence_of :province
56 #validates_presence_of :province
57
57
58 attr_accessor :password
58 attr_accessor :password
59
59
60 before_save :encrypt_new_password
60 before_save :encrypt_new_password
61 before_save :assign_default_site
61 before_save :assign_default_site
62 before_save :assign_default_contest
62 before_save :assign_default_contest
63
63
64 # this is for will_paginate
64 # this is for will_paginate
65 cattr_reader :per_page
65 cattr_reader :per_page
66 @@per_page = 50
66 @@per_page = 50
67
67
68 def self.authenticate(login, password)
68 def self.authenticate(login, password)
69 user = find_by_login(login)
69 user = find_by_login(login)
70 if user
70 if user
71 return user if user.authenticated?(password)
71 return user if user.authenticated?(password)
72 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
72 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
73 user.password = password
73 user.password = password
74 user.save
74 user.save
75 return user
75 return user
76 end
76 end
77 end
77 end
78 end
78 end
79
79
80 def authenticated?(password)
80 def authenticated?(password)
81 if self.activated
81 if self.activated
82 hashed_password == User.encrypt(password,self.salt)
82 hashed_password == User.encrypt(password,self.salt)
83 else
83 else
84 false
84 false
85 end
85 end
86 end
86 end
87
87
88 def authenticated_by_pop3?(password)
88 def authenticated_by_pop3?(password)
89 Net::POP3.enable_ssl
89 Net::POP3.enable_ssl
90 pop = Net::POP3.new('pops.it.chula.ac.th')
90 pop = Net::POP3.new('pops.it.chula.ac.th')
91 authen = true
91 authen = true
92 begin
92 begin
93 pop.start(login, password)
93 pop.start(login, password)
94 pop.finish
94 pop.finish
95 return true
95 return true
96 rescue
96 rescue
97 return false
97 return false
98 end
98 end
99 end
99 end
100
100
101 def authenticated_by_cucas?(password)
101 def authenticated_by_cucas?(password)
102 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
102 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
103 appid = '41508763e340d5858c00f8c1a0f5a2bb'
103 appid = '41508763e340d5858c00f8c1a0f5a2bb'
104 appsecret ='d9cbb5863091dbe186fded85722a1e31'
104 appsecret ='d9cbb5863091dbe186fded85722a1e31'
105 post_args = {
105 post_args = {
106 'appid' => appid,
106 'appid' => appid,
107 'appsecret' => appsecret,
107 'appsecret' => appsecret,
108 'username' => login,
108 'username' => login,
109 'password' => password
109 'password' => password
110 }
110 }
111
111
112 #simple call
112 #simple call
113 begin
113 begin
114 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
114 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
115 http.use_ssl = true
115 http.use_ssl = true
116 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
116 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
117 result = [ ]
117 result = [ ]
118 http.start do |http|
118 http.start do |http|
119 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
119 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
120 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
120 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
121 resp = http.request(req,param)
121 resp = http.request(req,param)
122 result = JSON.parse resp.body
122 result = JSON.parse resp.body
123 end
123 end
124 return true if result["type"] == "beanStudent"
124 return true if result["type"] == "beanStudent"
125 rescue => e
125 rescue => e
126 return false
126 return false
127 end
127 end
128 return false
128 return false
129 end
129 end
130
130
131 def admin?
131 def admin?
132 self.roles.detect {|r| r.name == 'admin' }
132 self.roles.detect {|r| r.name == 'admin' }
133 end
133 end
134
134
135 def email_for_editing
135 def email_for_editing
136 if self.email==nil
136 if self.email==nil
137 "(unknown)"
137 "(unknown)"
138 elsif self.email==''
138 elsif self.email==''
139 "(blank)"
139 "(blank)"
140 else
140 else
141 self.email
141 self.email
142 end
142 end
143 end
143 end
144
144
145 def email_for_editing=(e)
145 def email_for_editing=(e)
146 self.email=e
146 self.email=e
147 end
147 end
148
148
149 def alias_for_editing
149 def alias_for_editing
150 if self.alias==nil
150 if self.alias==nil
151 "(unknown)"
151 "(unknown)"
152 elsif self.alias==''
152 elsif self.alias==''
153 "(blank)"
153 "(blank)"
154 else
154 else
155 self.alias
155 self.alias
156 end
156 end
157 end
157 end
158
158
159 def alias_for_editing=(e)
159 def alias_for_editing=(e)
160 self.alias=e
160 self.alias=e
161 end
161 end
162
162
163 def activation_key
163 def activation_key
164 if self.hashed_password==nil
164 if self.hashed_password==nil
165 encrypt_new_password
165 encrypt_new_password
166 end
166 end
167 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
167 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
168 end
168 end
169
169
170 def verify_activation_key(key)
170 def verify_activation_key(key)
171 key == activation_key
171 key == activation_key
172 end
172 end
173
173
174 def self.random_password(length=5)
174 def self.random_password(length=5)
175 chars = 'abcdefghjkmnopqrstuvwxyz'
175 chars = 'abcdefghjkmnopqrstuvwxyz'
176 password = ''
176 password = ''
177 length.times { password << chars[rand(chars.length - 1)] }
177 length.times { password << chars[rand(chars.length - 1)] }
178 password
178 password
179 end
179 end
180
180
181 def self.find_non_admin_with_prefix(prefix='')
181 def self.find_non_admin_with_prefix(prefix='')
182 users = User.all
182 users = User.all
183 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
183 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
184 end
184 end
185
185
186 # Contest information
186 # Contest information
187
187
188 def self.find_users_with_no_contest()
188 def self.find_users_with_no_contest()
189 users = User.all
189 users = User.all
190 return users.find_all { |u| u.contests.length == 0 }
190 return users.find_all { |u| u.contests.length == 0 }
191 end
191 end
192
192
193
193
194 def contest_time_left
194 def contest_time_left
195 if GraderConfiguration.contest_mode?
195 if GraderConfiguration.contest_mode?
196 return nil if site==nil
196 return nil if site==nil
197 return site.time_left
197 return site.time_left
198 elsif GraderConfiguration.indv_contest_mode?
198 elsif GraderConfiguration.indv_contest_mode?
199 time_limit = GraderConfiguration.contest_time_limit
199 time_limit = GraderConfiguration.contest_time_limit
200 if time_limit == nil
200 if time_limit == nil
201 return nil
201 return nil
202 end
202 end
203 if contest_stat==nil or contest_stat.started_at==nil
203 if contest_stat==nil or contest_stat.started_at==nil
204 return (Time.now.gmtime + time_limit) - Time.now.gmtime
204 return (Time.now.gmtime + time_limit) - Time.now.gmtime
205 else
205 else
206 finish_time = contest_stat.started_at + time_limit
206 finish_time = contest_stat.started_at + time_limit
207 current_time = Time.now.gmtime
207 current_time = Time.now.gmtime
208 if current_time > finish_time
208 if current_time > finish_time
209 return 0
209 return 0
210 else
210 else
211 return finish_time - current_time
211 return finish_time - current_time
212 end
212 end
213 end
213 end
214 else
214 else
215 return nil
215 return nil
216 end
216 end
217 end
217 end
218
218
219 def contest_finished?
219 def contest_finished?
220 if GraderConfiguration.contest_mode?
220 if GraderConfiguration.contest_mode?
221 return false if site==nil
221 return false if site==nil
222 return site.finished?
222 return site.finished?
223 elsif GraderConfiguration.indv_contest_mode?
223 elsif GraderConfiguration.indv_contest_mode?
224 return false if self.contest_stat(true)==nil
224 return false if self.contest_stat(true)==nil
225 return contest_time_left == 0
225 return contest_time_left == 0
226 else
226 else
227 return false
227 return false
228 end
228 end
229 end
229 end
230
230
231 def contest_started?
231 def contest_started?
232 if GraderConfiguration.indv_contest_mode?
232 if GraderConfiguration.indv_contest_mode?
233 stat = self.contest_stat
233 stat = self.contest_stat
234 return ((stat != nil) and (stat.started_at != nil))
234 return ((stat != nil) and (stat.started_at != nil))
235 elsif GraderConfiguration.contest_mode?
235 elsif GraderConfiguration.contest_mode?
236 return true if site==nil
236 return true if site==nil
237 return site.started
237 return site.started
238 else
238 else
239 return true
239 return true
240 end
240 end
241 end
241 end
242
242
243 def update_start_time
243 def update_start_time
244 stat = self.contest_stat
244 stat = self.contest_stat
245 if stat.nil? or stat.started_at.nil?
245 if stat.nil? or stat.started_at.nil?
246 stat ||= UserContestStat.new(:user => self)
246 stat ||= UserContestStat.new(:user => self)
247 stat.started_at = Time.now.gmtime
247 stat.started_at = Time.now.gmtime
248 stat.save
248 stat.save
249 end
249 end
250 end
250 end
251
251
252 def problem_in_user_contests?(problem)
252 def problem_in_user_contests?(problem)
253 problem_contests = problem.contests.all
253 problem_contests = problem.contests.all
254
254
255 if problem_contests.length == 0 # this is public contest
255 if problem_contests.length == 0 # this is public contest
256 return true
256 return true
257 end
257 end
258
258
259 contests.each do |contest|
259 contests.each do |contest|
260 if problem_contests.find {|c| c.id == contest.id }
260 if problem_contests.find {|c| c.id == contest.id }
261 return true
261 return true
262 end
262 end
263 end
263 end
264 return false
264 return false
265 end
265 end
266
266
267 def available_problems_group_by_contests
267 def available_problems_group_by_contests
268 contest_problems = []
268 contest_problems = []
269 pin = {}
269 pin = {}
270 contests.enabled.each do |contest|
270 contests.enabled.each do |contest|
271 available_problems = contest.problems.available
271 available_problems = contest.problems.available
272 contest_problems << {
272 contest_problems << {
273 :contest => contest,
273 :contest => contest,
274 :problems => available_problems
274 :problems => available_problems
275 }
275 }
276 available_problems.each {|p| pin[p.id] = true}
276 available_problems.each {|p| pin[p.id] = true}
277 end
277 end
278 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
278 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
279 contest_problems << {
279 contest_problems << {
280 :contest => nil,
280 :contest => nil,
281 :problems => other_avaiable_problems
281 :problems => other_avaiable_problems
282 }
282 }
283 return contest_problems
283 return contest_problems
284 end
284 end
285
285
286 def solve_all_available_problems?
286 def solve_all_available_problems?
287 available_problems.each do |p|
287 available_problems.each do |p|
288 u = self
288 u = self
289 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
289 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
290 return false if !p or !sub or sub.points < p.full_score
290 return false if !p or !sub or sub.points < p.full_score
291 end
291 end
292 return true
292 return true
293 end
293 end
294
294
295 + #get a list of available problem
295 def available_problems
296 def available_problems
296 if not GraderConfiguration.multicontests?
297 if not GraderConfiguration.multicontests?
297 if GraderConfiguration.use_problem_group?
298 if GraderConfiguration.use_problem_group?
298 return available_problems_in_group
299 return available_problems_in_group
299 else
300 else
300 return Problem.available_problems
301 return Problem.available_problems
301 end
302 end
302 else
303 else
303 contest_problems = []
304 contest_problems = []
304 pin = {}
305 pin = {}
305 contests.enabled.each do |contest|
306 contests.enabled.each do |contest|
306 contest.problems.available.each do |problem|
307 contest.problems.available.each do |problem|
307 if not pin.has_key? problem.id
308 if not pin.has_key? problem.id
308 contest_problems << problem
309 contest_problems << problem
309 end
310 end
310 pin[problem.id] = true
311 pin[problem.id] = true
311 end
312 end
312 end
313 end
313 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
314 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
314 return contest_problems + other_avaiable_problems
315 return contest_problems + other_avaiable_problems
315 end
316 end
316 end
317 end
317
318
318 def available_problems_in_group
319 def available_problems_in_group
319 problem = []
320 problem = []
320 self.groups.each do |group|
321 self.groups.each do |group|
321 group.problems.where(available: true).each { |p| problem << p }
322 group.problems.where(available: true).each { |p| problem << p }
322 end
323 end
323 problem.uniq!
324 problem.uniq!
324 if problem
325 if problem
325 problem.sort! do |a,b|
326 problem.sort! do |a,b|
326 case
327 case
327 when a.date_added < b.date_added
328 when a.date_added < b.date_added
328 1
329 1
329 when a.date_added > b.date_added
330 when a.date_added > b.date_added
330 -1
331 -1
331 else
332 else
332 a.name <=> b.name
333 a.name <=> b.name
333 end
334 end
334 end
335 end
335 return problem
336 return problem
336 else
337 else
337 return []
338 return []
338 end
339 end
339 end
340 end
340
341
341 def can_view_problem?(problem)
342 def can_view_problem?(problem)
342 return true if admin?
343 return true if admin?
343 return available_problems.include? problem
344 return available_problems.include? problem
344 end
345 end
345
346
346 def self.clear_last_login
347 def self.clear_last_login
347 User.update_all(:last_ip => nil)
348 User.update_all(:last_ip => nil)
348 end
349 end
349
350
350 protected
351 protected
351 def encrypt_new_password
352 def encrypt_new_password
352 return if password.blank?
353 return if password.blank?
353 self.salt = (10+rand(90)).to_s
354 self.salt = (10+rand(90)).to_s
354 self.hashed_password = User.encrypt(self.password,self.salt)
355 self.hashed_password = User.encrypt(self.password,self.salt)
355 end
356 end
356
357
357 def assign_default_site
358 def assign_default_site
358 # have to catch error when migrating (because self.site is not available).
359 # have to catch error when migrating (because self.site is not available).
359 begin
360 begin
360 if self.site==nil
361 if self.site==nil
361 self.site = Site.find_by_name('default')
362 self.site = Site.find_by_name('default')
362 if self.site==nil
363 if self.site==nil
363 self.site = Site.find(1) # when 'default has be renamed'
364 self.site = Site.find(1) # when 'default has be renamed'
364 end
365 end
365 end
366 end
366 rescue
367 rescue
367 end
368 end
368 end
369 end
369
370
370 def assign_default_contest
371 def assign_default_contest
371 # have to catch error when migrating (because self.site is not available).
372 # have to catch error when migrating (because self.site is not available).
372 begin
373 begin
373 if self.contests.length == 0
374 if self.contests.length == 0
374 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
375 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
375 if default_contest
376 if default_contest
376 self.contests = [default_contest]
377 self.contests = [default_contest]
377 end
378 end
378 end
379 end
379 rescue
380 rescue
380 end
381 end
381 end
382 end
382
383
383 def password_required?
384 def password_required?
384 self.hashed_password.blank? || !self.password.blank?
385 self.hashed_password.blank? || !self.password.blank?
385 end
386 end
386
387
387 def self.encrypt(string,salt)
388 def self.encrypt(string,salt)
388 Digest::SHA1.hexdigest(salt + string)
389 Digest::SHA1.hexdigest(salt + string)
389 end
390 end
390
391
391 def uniqueness_of_email_from_activated_users
392 def uniqueness_of_email_from_activated_users
392 user = User.activated_users.find_by_email(self.email)
393 user = User.activated_users.find_by_email(self.email)
393 if user and (user.login != self.login)
394 if user and (user.login != self.login)
394 self.errors.add(:base,"Email has already been taken")
395 self.errors.add(:base,"Email has already been taken")
395 end
396 end
396 end
397 end
397
398
398 def enough_time_interval_between_same_email_registrations
399 def enough_time_interval_between_same_email_registrations
399 return if !self.new_record?
400 return if !self.new_record?
400 return if self.activated
401 return if self.activated
401 open_user = User.find_by_email(self.email,
402 open_user = User.find_by_email(self.email,
402 :order => 'created_at DESC')
403 :order => 'created_at DESC')
403 if open_user and open_user.created_at and
404 if open_user and open_user.created_at and
404 (open_user.created_at > Time.now.gmtime - 5.minutes)
405 (open_user.created_at > Time.now.gmtime - 5.minutes)
405 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
406 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
406 end
407 end
407 end
408 end
408
409
409 def email_validation?
410 def email_validation?
410 begin
411 begin
411 return VALIDATE_USER_EMAILS
412 return VALIDATE_USER_EMAILS
412 rescue
413 rescue
413 return false
414 return false
414 end
415 end
415 end
416 end
416 end
417 end
@@ -1,60 +1,60
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
24 %th
25 Submit
25 Submit
26 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?]
26 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?]
27 %th Date added
27 %th Date added
28 %th.text-center
28 %th.text-center
29 Avail?
29 Avail?
30 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
30 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
31 %th.text-center
31 %th.text-center
32 View Data?
32 View Data?
33 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
33 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
34 %th.text-center
34 %th.text-center
35 Test?
35 Test?
36 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
36 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
37 - if GraderConfiguration.multicontests?
37 - if GraderConfiguration.multicontests?
38 %th Contests
38 %th Contests
39 - for problem in @problems
39 - for problem in @problems
40 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
40 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
41 - @problem=problem
41 - @problem=problem
42 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
42 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
43 %td
43 %td
44 = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
44 = 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
45 = 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
46 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
47 - %td= link_to "Submit", direct_edit_problem_submissions_path(problem), class: 'btn btn-xs btn-primary'
47 + %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-xs btn-primary'
48 %td= problem.date_added
48 %td= problem.date_added
49 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
49 %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}")
50 %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}")
51 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
52 - if GraderConfiguration.multicontests?
52 - if GraderConfiguration.multicontests?
53 %td
53 %td
54 = problem.contests.collect { |c| c.name }.join(', ')
54 = 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'
55 %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'
56 %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'
57 %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'
58 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
59 %br/
59 %br/
60 = link_to '[New problem]', :action => 'new'
60 = link_to '[New problem]', :action => 'new'
@@ -1,269 +1,271
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 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
15 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
15
16
16 = hidden_field_tag 'editor_text', @source
17 = hidden_field_tag 'editor_text', @source
17 = hidden_field_tag 'submission[problem_id]', @problem.id
18 = hidden_field_tag 'submission[problem_id]', @problem.id
18 .form-group
19 .form-group
19 = label_tag "Task:"
20 = label_tag "Task:"
20 = 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
21
22
22 .form-group
23 .form-group
23 = label_tag 'Language'
24 = label_tag 'Language'
24 = 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"
25 .form-group
26 .form-group
26 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
27 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
27 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
28 .panel.panel-info
30 .panel.panel-info
29 .panel-heading
31 .panel-heading
30 Latest Submission Status
32 Latest Submission Status
31 = 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
32 .panel-body
34 .panel-body
33 - if @submission
35 - if @submission
34 = render :partial => 'submission_short',
36 = render :partial => 'submission_short',
35 :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
37 :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
36 .row
38 .row
37 .col-md-12
39 .col-md-12
38 %h2 Console
40 %h2 Console
39 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
41 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
40
42
41 :javascript
43 :javascript
42 $(document).ready(function() {
44 $(document).ready(function() {
43 e = ace.edit("editor")
45 e = ace.edit("editor")
44 e.setValue($("#text_sourcecode").val());
46 e.setValue($("#text_sourcecode").val());
45 e.gotoLine(1);
47 e.gotoLine(1);
46 $("#language_id").trigger('change');
48 $("#language_id").trigger('change');
47 brython();
49 brython();
48 });
50 });
49
51
50
52
51 %script#__main__{type:'text/python3'}
53 %script#__main__{type:'text/python3'}
52 :plain
54 :plain
53 import sys
55 import sys
54 import traceback
56 import traceback
55
57
56 from browser import document as doc
58 from browser import document as doc
57 from browser import window, alert, console
59 from browser import window, alert, console
58
60
59 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
61 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
60 for supporting Python development. See www.python.org for more information."""
62 for supporting Python development. See www.python.org for more information."""
61
63
62 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
64 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
63 All Rights Reserved.
65 All Rights Reserved.
64
66
65 Copyright (c) 2001-2013 Python Software Foundation.
67 Copyright (c) 2001-2013 Python Software Foundation.
66 All Rights Reserved.
68 All Rights Reserved.
67
69
68 Copyright (c) 2000 BeOpen.com.
70 Copyright (c) 2000 BeOpen.com.
69 All Rights Reserved.
71 All Rights Reserved.
70
72
71 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
73 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
72 All Rights Reserved.
74 All Rights Reserved.
73
75
74 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
76 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
75 All Rights Reserved."""
77 All Rights Reserved."""
76
78
77 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
79 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
78 All rights reserved.
80 All rights reserved.
79
81
80 Redistribution and use in source and binary forms, with or without
82 Redistribution and use in source and binary forms, with or without
81 modification, are permitted provided that the following conditions are met:
83 modification, are permitted provided that the following conditions are met:
82
84
83 Redistributions of source code must retain the above copyright notice, this
85 Redistributions of source code must retain the above copyright notice, this
84 list of conditions and the following disclaimer. Redistributions in binary
86 list of conditions and the following disclaimer. Redistributions in binary
85 form must reproduce the above copyright notice, this list of conditions and
87 form must reproduce the above copyright notice, this list of conditions and
86 the following disclaimer in the documentation and/or other materials provided
88 the following disclaimer in the documentation and/or other materials provided
87 with the distribution.
89 with the distribution.
88 Neither the name of the <ORGANIZATION> nor the names of its contributors may
90 Neither the name of the <ORGANIZATION> nor the names of its contributors may
89 be used to endorse or promote products derived from this software without
91 be used to endorse or promote products derived from this software without
90 specific prior written permission.
92 specific prior written permission.
91
93
92 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
94 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
93 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
95 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
94 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
96 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
95 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
97 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
96 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
98 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
97 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
99 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
98 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
100 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
99 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
101 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
100 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
102 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
101 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
103 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
102 POSSIBILITY OF SUCH DAMAGE.
104 POSSIBILITY OF SUCH DAMAGE.
103 """
105 """
104
106
105 def credits():
107 def credits():
106 print(_credits)
108 print(_credits)
107 credits.__repr__ = lambda:_credits
109 credits.__repr__ = lambda:_credits
108
110
109 def copyright():
111 def copyright():
110 print(_copyright)
112 print(_copyright)
111 copyright.__repr__ = lambda:_copyright
113 copyright.__repr__ = lambda:_copyright
112
114
113 def license():
115 def license():
114 print(_license)
116 print(_license)
115 license.__repr__ = lambda:_license
117 license.__repr__ = lambda:_license
116
118
117 def write(data):
119 def write(data):
118 doc['console'].value += str(data)
120 doc['console'].value += str(data)
119
121
120
122
121 sys.stdout.write = sys.stderr.write = write
123 sys.stdout.write = sys.stderr.write = write
122 history = []
124 history = []
123 current = 0
125 current = 0
124 _status = "main" # or "block" if typing inside a block
126 _status = "main" # or "block" if typing inside a block
125
127
126 # execution namespace
128 # execution namespace
127 editor_ns = {'credits':credits,
129 editor_ns = {'credits':credits,
128 'copyright':copyright,
130 'copyright':copyright,
129 'license':license,
131 'license':license,
130 '__name__':'__main__'}
132 '__name__':'__main__'}
131
133
132 def cursorToEnd(*args):
134 def cursorToEnd(*args):
133 pos = len(doc['console'].value)
135 pos = len(doc['console'].value)
134 doc['console'].setSelectionRange(pos, pos)
136 doc['console'].setSelectionRange(pos, pos)
135 doc['console'].scrollTop = doc['console'].scrollHeight
137 doc['console'].scrollTop = doc['console'].scrollHeight
136
138
137 def get_col(area):
139 def get_col(area):
138 # returns the column num of cursor
140 # returns the column num of cursor
139 sel = doc['console'].selectionStart
141 sel = doc['console'].selectionStart
140 lines = doc['console'].value.split('\n')
142 lines = doc['console'].value.split('\n')
141 for line in lines[:-1]:
143 for line in lines[:-1]:
142 sel -= len(line) + 1
144 sel -= len(line) + 1
143 return sel
145 return sel
144
146
145
147
146 def myKeyPress(event):
148 def myKeyPress(event):
147 global _status, current
149 global _status, current
148 if event.keyCode == 9: # tab key
150 if event.keyCode == 9: # tab key
149 event.preventDefault()
151 event.preventDefault()
150 doc['console'].value += " "
152 doc['console'].value += " "
151 elif event.keyCode == 13: # return
153 elif event.keyCode == 13: # return
152 src = doc['console'].value
154 src = doc['console'].value
153 if _status == "main":
155 if _status == "main":
154 currentLine = src[src.rfind('>>>') + 4:]
156 currentLine = src[src.rfind('>>>') + 4:]
155 elif _status == "3string":
157 elif _status == "3string":
156 currentLine = src[src.rfind('>>>') + 4:]
158 currentLine = src[src.rfind('>>>') + 4:]
157 currentLine = currentLine.replace('\n... ', '\n')
159 currentLine = currentLine.replace('\n... ', '\n')
158 else:
160 else:
159 currentLine = src[src.rfind('...') + 4:]
161 currentLine = src[src.rfind('...') + 4:]
160 if _status == 'main' and not currentLine.strip():
162 if _status == 'main' and not currentLine.strip():
161 doc['console'].value += '\n>>> '
163 doc['console'].value += '\n>>> '
162 event.preventDefault()
164 event.preventDefault()
163 return
165 return
164 doc['console'].value += '\n'
166 doc['console'].value += '\n'
165 history.append(currentLine)
167 history.append(currentLine)
166 current = len(history)
168 current = len(history)
167 if _status == "main" or _status == "3string":
169 if _status == "main" or _status == "3string":
168 try:
170 try:
169 _ = editor_ns['_'] = eval(currentLine, editor_ns)
171 _ = editor_ns['_'] = eval(currentLine, editor_ns)
170 if _ is not None:
172 if _ is not None:
171 write(repr(_)+'\n')
173 write(repr(_)+'\n')
172 doc['console'].value += '>>> '
174 doc['console'].value += '>>> '
173 _status = "main"
175 _status = "main"
174 except IndentationError:
176 except IndentationError:
175 doc['console'].value += '... '
177 doc['console'].value += '... '
176 _status = "block"
178 _status = "block"
177 except SyntaxError as msg:
179 except SyntaxError as msg:
178 if str(msg) == 'invalid syntax : triple string end not found' or \
180 if str(msg) == 'invalid syntax : triple string end not found' or \
179 str(msg).startswith('Unbalanced bracket'):
181 str(msg).startswith('Unbalanced bracket'):
180 doc['console'].value += '... '
182 doc['console'].value += '... '
181 _status = "3string"
183 _status = "3string"
182 elif str(msg) == 'eval() argument must be an expression':
184 elif str(msg) == 'eval() argument must be an expression':
183 try:
185 try:
184 exec(currentLine, editor_ns)
186 exec(currentLine, editor_ns)
185 except:
187 except:
186 traceback.print_exc()
188 traceback.print_exc()
187 doc['console'].value += '>>> '
189 doc['console'].value += '>>> '
188 _status = "main"
190 _status = "main"
189 elif str(msg) == 'decorator expects function':
191 elif str(msg) == 'decorator expects function':
190 doc['console'].value += '... '
192 doc['console'].value += '... '
191 _status = "block"
193 _status = "block"
192 else:
194 else:
193 traceback.print_exc()
195 traceback.print_exc()
194 doc['console'].value += '>>> '
196 doc['console'].value += '>>> '
195 _status = "main"
197 _status = "main"
196 except:
198 except:
197 traceback.print_exc()
199 traceback.print_exc()
198 doc['console'].value += '>>> '
200 doc['console'].value += '>>> '
199 _status = "main"
201 _status = "main"
200 elif currentLine == "": # end of block
202 elif currentLine == "": # end of block
201 block = src[src.rfind('>>>') + 4:].splitlines()
203 block = src[src.rfind('>>>') + 4:].splitlines()
202 block = [block[0]] + [b[4:] for b in block[1:]]
204 block = [block[0]] + [b[4:] for b in block[1:]]
203 block_src = '\n'.join(block)
205 block_src = '\n'.join(block)
204 # status must be set before executing code in globals()
206 # status must be set before executing code in globals()
205 _status = "main"
207 _status = "main"
206 try:
208 try:
207 _ = exec(block_src, editor_ns)
209 _ = exec(block_src, editor_ns)
208 if _ is not None:
210 if _ is not None:
209 print(repr(_))
211 print(repr(_))
210 except:
212 except:
211 traceback.print_exc()
213 traceback.print_exc()
212 doc['console'].value += '>>> '
214 doc['console'].value += '>>> '
213 else:
215 else:
214 doc['console'].value += '... '
216 doc['console'].value += '... '
215
217
216 cursorToEnd()
218 cursorToEnd()
217 event.preventDefault()
219 event.preventDefault()
218
220
219 def myKeyDown(event):
221 def myKeyDown(event):
220 global _status, current
222 global _status, current
221 if event.keyCode == 37: # left arrow
223 if event.keyCode == 37: # left arrow
222 sel = get_col(doc['console'])
224 sel = get_col(doc['console'])
223 if sel < 5:
225 if sel < 5:
224 event.preventDefault()
226 event.preventDefault()
225 event.stopPropagation()
227 event.stopPropagation()
226 elif event.keyCode == 36: # line start
228 elif event.keyCode == 36: # line start
227 pos = doc['console'].selectionStart
229 pos = doc['console'].selectionStart
228 col = get_col(doc['console'])
230 col = get_col(doc['console'])
229 doc['console'].setSelectionRange(pos - col + 4, pos - col + 4)
231 doc['console'].setSelectionRange(pos - col + 4, pos - col + 4)
230 event.preventDefault()
232 event.preventDefault()
231 elif event.keyCode == 38: # up
233 elif event.keyCode == 38: # up
232 if current > 0:
234 if current > 0:
233 pos = doc['console'].selectionStart
235 pos = doc['console'].selectionStart
234 col = get_col(doc['console'])
236 col = get_col(doc['console'])
235 # remove current line
237 # remove current line
236 doc['console'].value = doc['console'].value[:pos - col + 4]
238 doc['console'].value = doc['console'].value[:pos - col + 4]
237 current -= 1
239 current -= 1
238 doc['console'].value += history[current]
240 doc['console'].value += history[current]
239 event.preventDefault()
241 event.preventDefault()
240 elif event.keyCode == 40: # down
242 elif event.keyCode == 40: # down
241 if current < len(history) - 1:
243 if current < len(history) - 1:
242 pos = doc['console'].selectionStart
244 pos = doc['console'].selectionStart
243 col = get_col(doc['console'])
245 col = get_col(doc['console'])
244 # remove current line
246 # remove current line
245 doc['console'].value = doc['console'].value[:pos - col + 4]
247 doc['console'].value = doc['console'].value[:pos - col + 4]
246 current += 1
248 current += 1
247 doc['console'].value += history[current]
249 doc['console'].value += history[current]
248 event.preventDefault()
250 event.preventDefault()
249 elif event.keyCode == 8: # backspace
251 elif event.keyCode == 8: # backspace
250 src = doc['console'].value
252 src = doc['console'].value
251 lstart = src.rfind('\n')
253 lstart = src.rfind('\n')
252 if (lstart == -1 and len(src) < 5) or (len(src) - lstart < 6):
254 if (lstart == -1 and len(src) < 5) or (len(src) - lstart < 6):
253 event.preventDefault()
255 event.preventDefault()
254 event.stopPropagation()
256 event.stopPropagation()
255
257
256
258
257 doc['console'].bind('keypress', myKeyPress)
259 doc['console'].bind('keypress', myKeyPress)
258 doc['console'].bind('keydown', myKeyDown)
260 doc['console'].bind('keydown', myKeyDown)
259 doc['console'].bind('click', cursorToEnd)
261 doc['console'].bind('click', cursorToEnd)
260 v = sys.implementation.version
262 v = sys.implementation.version
261 doc['console'].value = "Brython %s.%s.%s on %s %s\n>>> " % (
263 doc['console'].value = "Brython %s.%s.%s on %s %s\n>>> " % (
262 v[0], v[1], v[2], window.navigator.appName, window.navigator.appVersion)
264 v[0], v[1], v[2], window.navigator.appName, window.navigator.appVersion)
263 #doc['console'].value += 'Type "copyright", "credits" or "license" for more information.'
265 #doc['console'].value += 'Type "copyright", "credits" or "license" for more information.'
264 doc['console'].focus()
266 doc['console'].focus()
265 cursorToEnd()
267 cursorToEnd()
266
268
267
269
268
270
269
271
@@ -1,114 +1,114
1 CafeGrader::Application.routes.draw do
1 CafeGrader::Application.routes.draw do
2 resources :tags
2 resources :tags
3 get "sources/direct_edit"
3 get "sources/direct_edit"
4
4
5 root :to => 'main#login'
5 root :to => 'main#login'
6
6
7 #logins
7 #logins
8 get 'login/login', to: 'login#login'
8 get 'login/login', to: 'login#login'
9
9
10 resources :contests
10 resources :contests
11
11
12 resources :sites
12 resources :sites
13
13
14 resources :announcements do
14 resources :announcements do
15 member do
15 member do
16 get 'toggle','toggle_front'
16 get 'toggle','toggle_front'
17 end
17 end
18 end
18 end
19
19
20 resources :problems do
20 resources :problems do
21 member do
21 member do
22 get 'toggle'
22 get 'toggle'
23 get 'toggle_test'
23 get 'toggle_test'
24 get 'toggle_view_testcase'
24 get 'toggle_view_testcase'
25 get 'stat'
25 get 'stat'
26 end
26 end
27 collection do
27 collection do
28 get 'turn_all_off'
28 get 'turn_all_off'
29 get 'turn_all_on'
29 get 'turn_all_on'
30 get 'import'
30 get 'import'
31 get 'manage'
31 get 'manage'
32 end
32 end
33 end
33 end
34
34
35 resources :groups do
35 resources :groups do
36 member do
36 member do
37 post 'add_user', to: 'groups#add_user', as: 'add_user'
37 post 'add_user', to: 'groups#add_user', as: 'add_user'
38 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
38 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
39 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
39 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
40 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
40 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
41 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
41 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
42 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
42 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
43 end
43 end
44 collection do
44 collection do
45
45
46 end
46 end
47 end
47 end
48
48
49 resources :testcases, only: [] do
49 resources :testcases, only: [] do
50 member do
50 member do
51 get 'download_input'
51 get 'download_input'
52 get 'download_sol'
52 get 'download_sol'
53 end
53 end
54 collection do
54 collection do
55 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
55 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
56 end
56 end
57 end
57 end
58
58
59 resources :grader_configuration, controller: 'configurations'
59 resources :grader_configuration, controller: 'configurations'
60
60
61 resources :users do
61 resources :users do
62 member do
62 member do
63 get 'toggle_activate', 'toggle_enable'
63 get 'toggle_activate', 'toggle_enable'
64 get 'stat'
64 get 'stat'
65 end
65 end
66 end
66 end
67
67
68 resources :submissions do
68 resources :submissions do
69 member do
69 member do
70 get 'download'
70 get 'download'
71 get 'compiler_msg'
71 get 'compiler_msg'
72 get 'rejudge'
72 get 'rejudge'
73 end
73 end
74 collection do
74 collection do
75 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
75 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
76 - get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
76 + get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
77 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
77 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
78 end
78 end
79 end
79 end
80
80
81
81
82
82
83 #main
83 #main
84 get "main/list"
84 get "main/list"
85 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
85 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
86
86
87 #user admin
87 #user admin
88 get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
88 get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
89 post 'user_admin', to: 'user_admin#create'
89 post 'user_admin', to: 'user_admin#create'
90 delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
90 delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
91
91
92 #report
92 #report
93 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
93 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
94 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
94 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
95 get "report/login"
95 get "report/login"
96 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
96 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
97 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
97 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
98
98
99
99
100 #
100 #
101 get 'tasks/view/:file.:ext' => 'tasks#view'
101 get 'tasks/view/:file.:ext' => 'tasks#view'
102 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
102 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
103 get 'heartbeat/:id/edit' => 'heartbeat#edit'
103 get 'heartbeat/:id/edit' => 'heartbeat#edit'
104
104
105 #grader
105 #grader
106 get 'graders/list', to: 'graders#list', as: 'grader_list'
106 get 'graders/list', to: 'graders#list', as: 'grader_list'
107
107
108
108
109 # See how all your routes lay out with "rake routes"
109 # See how all your routes lay out with "rake routes"
110
110
111 # This is a legacy wild controller route that's not recommended for RESTful applications.
111 # This is a legacy wild controller route that's not recommended for RESTful applications.
112 # Note: This route will make all actions in every controller accessible via GET requests.
112 # Note: This route will make all actions in every controller accessible via GET requests.
113 match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
113 match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
114 end
114 end
You need to be logged in to leave comments. Login now