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

r855:ef5cd5528b8d - - 11 files changed: 210 inserted, 125 deleted

@@ -0,0 +1,6
1 + class AddTypeToSubmission < ActiveRecord::Migration[7.0]
2 + def change
3 + add_column :submissions, :tag, :integer, default: 0
4 + add_column :problems, :difficulty, :integer
5 + end
6 + end
@@ -1,89 +1,100
1 require 'ipaddr'
1 require 'ipaddr'
2 require "securerandom"
2 require "securerandom"
3
3
4 class ApplicationController < ActionController::Base
4 class ApplicationController < ActionController::Base
5 protect_from_forgery
5 protect_from_forgery
6
6
7 before_action :current_user
7 before_action :current_user
8 before_action :nav_announcement
8 before_action :nav_announcement
9 before_action :unique_visitor_id
9 before_action :unique_visitor_id
10
10
11 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
11 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
12 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
12 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
13 WHITELIST_IGNORE_CONF_KEY = 'right.whitelist_ignore'
13 WHITELIST_IGNORE_CONF_KEY = 'right.whitelist_ignore'
14 WHITELIST_IP_CONF_KEY = 'right.whitelist_ip'
14 WHITELIST_IP_CONF_KEY = 'right.whitelist_ip'
15
15
16 #report and redirect for unauthorized activities
16 #report and redirect for unauthorized activities
17 def unauthorized_redirect(notice = 'You are not authorized to view the page you requested')
17 def unauthorized_redirect(notice = 'You are not authorized to view the page you requested')
18 flash[:notice] = notice
18 flash[:notice] = notice
19 redirect_to login_main_path
19 redirect_to login_main_path
20 end
20 end
21
21
22 # Returns the current logged-in user (if any).
22 # Returns the current logged-in user (if any).
23 def current_user
23 def current_user
24 return nil unless session[:user_id]
24 return nil unless session[:user_id]
25 @current_user ||= User.find(session[:user_id])
25 @current_user ||= User.find(session[:user_id])
26 end
26 end
27
27
28 def nav_announcement
28 def nav_announcement
29 @nav_announcement = Announcement.where(on_nav_bar: true)
29 @nav_announcement = Announcement.where(on_nav_bar: true)
30 end
30 end
31
31
32 def admin_authorization
32 def admin_authorization
33 return false unless check_valid_login
33 return false unless check_valid_login
34 user = User.includes(:roles).find(session[:user_id])
34 user = User.includes(:roles).find(session[:user_id])
35 unless user.admin?
35 unless user.admin?
36 unauthorized_redirect
36 unauthorized_redirect
37 return false
37 return false
38 end
38 end
39 return true
39 return true
40 end
40 end
41
41
42 + #admin always count as every roles
43 + def role_authorization(roles)
44 + return false unless check_valid_login
45 + user = User.find(session[:user_id])
46 + return true if user.admin?
47 + roles.each do |r|
48 + return true if user.has_role?(r)
49 + end
50 + unauthorized_redirect
51 + end
52 +
42 def authorization_by_roles(allowed_roles)
53 def authorization_by_roles(allowed_roles)
43 return false unless check_valid_login
54 return false unless check_valid_login
44 unless @current_user.roles.detect { |role| allowed_roles.member?(role.name) }
55 unless @current_user.roles.detect { |role| allowed_roles.member?(role.name) }
45 unauthorized_redirect
56 unauthorized_redirect
46 return false
57 return false
47 end
58 end
48 end
59 end
49
60
50 def testcase_authorization
61 def testcase_authorization
51 #admin always has privileged
62 #admin always has privileged
52 if @current_user.admin?
63 if @current_user.admin?
53 return true
64 return true
54 end
65 end
55
66
56 unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
67 unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
57 end
68 end
58
69
59 def unique_visitor_id
70 def unique_visitor_id
60 unless cookies.encrypted[:uuid]
71 unless cookies.encrypted[:uuid]
61 value = SecureRandom.uuid
72 value = SecureRandom.uuid
62 cookies.encrypted[:uuid] = { value: value, expires: 20.year }
73 cookies.encrypted[:uuid] = { value: value, expires: 20.year }
63 end
74 end
64 puts "encrypt " + cookies.encrypted[:uuid]
75 puts "encrypt " + cookies.encrypted[:uuid]
65 puts cookies[:uuid]
76 puts cookies[:uuid]
66 end
77 end
67
78
68 protected
79 protected
69
80
70 #redirect to root (and also force logout)
81 #redirect to root (and also force logout)
71 #if the user is not logged_in or the system is in "ADMIN ONLY" mode
82 #if the user is not logged_in or the system is in "ADMIN ONLY" mode
72 def check_valid_login
83 def check_valid_login
73 #check if logged in
84 #check if logged in
74 unless session[:user_id]
85 unless session[:user_id]
75 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
86 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
76 unauthorized_redirect('You need to login but you cannot log in at this time')
87 unauthorized_redirect('You need to login but you cannot log in at this time')
77 else
88 else
78 unauthorized_redirect('You need to login')
89 unauthorized_redirect('You need to login')
79 end
90 end
80 return false
91 return false
81 end
92 end
82
93
83 # check if run in single user mode
94 # check if run in single user mode
84 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
95 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
85 if @current_user==nil || (!@current_user.admin?)
96 if @current_user==nil || (!@current_user.admin?)
86 unauthorized_redirect('You cannot log in at this time')
97 unauthorized_redirect('You cannot log in at this time')
87 return false
98 return false
88 end
99 end
89 end
100 end
@@ -26,97 +26,97
26 # EXPERIMENT:
26 # EXPERIMENT:
27 # Hide login if in single user mode and the url does not
27 # Hide login if in single user mode and the url does not
28 # explicitly specify /login
28 # explicitly specify /login
29 #
29 #
30 # logger.info "PATH: #{request.path}"
30 # logger.info "PATH: #{request.path}"
31 # if GraderConfiguration['system.single_user_mode'] and
31 # if GraderConfiguration['system.single_user_mode'] and
32 # request.path!='/main/login'
32 # request.path!='/main/login'
33 # @hidelogin = true
33 # @hidelogin = true
34 # end
34 # end
35
35
36 @announcements = Announcement.frontpage
36 @announcements = Announcement.frontpage
37 render :action => 'login', :layout => 'empty'
37 render :action => 'login', :layout => 'empty'
38 end
38 end
39
39
40 def logout
40 def logout
41 reset_session
41 reset_session
42 redirect_to root_path
42 redirect_to root_path
43 end
43 end
44
44
45 def list
45 def list
46 prepare_list_information
46 prepare_list_information
47 end
47 end
48
48
49 def help
49 def help
50 @user = User.find(session[:user_id])
50 @user = User.find(session[:user_id])
51 end
51 end
52
52
53 def submit
53 def submit
54 user = User.find(session[:user_id])
54 user = User.find(session[:user_id])
55
55
56 @submission = Submission.new
56 @submission = Submission.new
57 @submission.problem_id = params[:submission][:problem_id]
57 @submission.problem_id = params[:submission][:problem_id]
58 @submission.user = user
58 @submission.user = user
59 @submission.language_id = 0
59 @submission.language_id = 0
60 if (params['file']) and (params['file']!='')
60 if (params['file']) and (params['file']!='')
61 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
61 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
62 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
62 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
63 @submission.source_filename = params['file'].original_filename
63 @submission.source_filename = params['file'].original_filename
64 end
64 end
65
65
66 if (params[:editor_text])
66 if (params[:editor_text])
67 language = Language.find_by_id(params[:language_id])
67 language = Language.find_by_id(params[:language_id])
68 @submission.source = params[:editor_text]
68 @submission.source = params[:editor_text]
69 @submission.source_filename = "live_edit.#{language.ext}"
69 @submission.source_filename = "live_edit.#{language.ext}"
70 @submission.language = language
70 @submission.language = language
71 end
71 end
72
72
73 @submission.submitted_at = Time.new.gmtime
73 @submission.submitted_at = Time.new.gmtime
74 - @submission.ip_address = request.remote_ip
74 + @submission.ip_address = cookies.encrypted[:uuid]
75
75
76 if @current_user.admin? == false && GraderConfiguration.time_limit_mode? && @current_user.contest_finished?
76 if @current_user.admin? == false && GraderConfiguration.time_limit_mode? && @current_user.contest_finished?
77 @submission.errors.add(:base,"The contest is over.")
77 @submission.errors.add(:base,"The contest is over.")
78 prepare_list_information
78 prepare_list_information
79 render :action => 'list' and return
79 render :action => 'list' and return
80 end
80 end
81
81
82 if @submission.valid?(@current_user)
82 if @submission.valid?(@current_user)
83 if @submission.save == false
83 if @submission.save == false
84 flash[:notice] = 'Error saving your submission'
84 flash[:notice] = 'Error saving your submission'
85 elsif Task.create(:submission_id => @submission.id,
85 elsif Task.create(:submission_id => @submission.id,
86 :status => Task::STATUS_INQUEUE) == false
86 :status => Task::STATUS_INQUEUE) == false
87 flash[:notice] = 'Error adding your submission to task queue'
87 flash[:notice] = 'Error adding your submission to task queue'
88 end
88 end
89 else
89 else
90 prepare_list_information
90 prepare_list_information
91 render :action => 'list' and return
91 render :action => 'list' and return
92 end
92 end
93 redirect_to edit_submission_path(@submission)
93 redirect_to edit_submission_path(@submission)
94 end
94 end
95
95
96 def source
96 def source
97 submission = Submission.find(params[:id])
97 submission = Submission.find(params[:id])
98 if ((submission.user_id == session[:user_id]) and
98 if ((submission.user_id == session[:user_id]) and
99 (submission.problem != nil) and
99 (submission.problem != nil) and
100 (submission.problem.available))
100 (submission.problem.available))
101 send_data(submission.source,
101 send_data(submission.source,
102 {:filename => submission.download_filename,
102 {:filename => submission.download_filename,
103 :type => 'text/plain'})
103 :type => 'text/plain'})
104 else
104 else
105 flash[:notice] = 'Error viewing source'
105 flash[:notice] = 'Error viewing source'
106 redirect_to :action => 'list'
106 redirect_to :action => 'list'
107 end
107 end
108 end
108 end
109
109
110 def compiler_msg
110 def compiler_msg
111 @submission = Submission.find(params[:id])
111 @submission = Submission.find(params[:id])
112 if @submission.user_id == session[:user_id]
112 if @submission.user_id == session[:user_id]
113 render :action => 'compiler_msg', :layout => 'empty'
113 render :action => 'compiler_msg', :layout => 'empty'
114 else
114 else
115 flash[:notice] = 'Error viewing source'
115 flash[:notice] = 'Error viewing source'
116 redirect_to :action => 'list'
116 redirect_to :action => 'list'
117 end
117 end
118 end
118 end
119
119
120 def result
120 def result
121 if !GraderConfiguration.show_grading_result
121 if !GraderConfiguration.show_grading_result
122 redirect_to :action => 'list' and return
122 redirect_to :action => 'list' and return
@@ -189,176 +189,185
189
189
190 case params[:users]
190 case params[:users]
191 when 'enabled'
191 when 'enabled'
192 @submissions = @submissions.where(users: {enabled: true})
192 @submissions = @submissions.where(users: {enabled: true})
193 when 'group'
193 when 'group'
194 @submissions = @submissions.joins(user: :groups).where(user: {groups: {id: params[:groups]}}) if params[:groups]
194 @submissions = @submissions.joins(user: :groups).where(user: {groups: {id: params[:groups]}}) if params[:groups]
195 end
195 end
196
196
197 case params[:problems]
197 case params[:problems]
198 when 'enabled'
198 when 'enabled'
199 @submissions = @submissions.where(problems: {available: true})
199 @submissions = @submissions.where(problems: {available: true})
200 when 'selected'
200 when 'selected'
201 @submissions = @submissions.where(problem_id: params[:problem_id])
201 @submissions = @submissions.where(problem_id: params[:problem_id])
202 end
202 end
203
203
204 #set default
204 #set default
205 params[:since_datetime] = Date.today.to_s if params[:since_datetime].blank?
205 params[:since_datetime] = Date.today.to_s if params[:since_datetime].blank?
206
206
207 @submissions, @recordsTotal, @recordsFiltered = process_query_record( @submissions,
207 @submissions, @recordsTotal, @recordsFiltered = process_query_record( @submissions,
208 global_search: ['user.login','user.full_name','problem.name','problem.full_name','points'],
208 global_search: ['user.login','user.full_name','problem.name','problem.full_name','points'],
209 date_filter: 'submitted_at',
209 date_filter: 'submitted_at',
210 date_param_since: 'since_datetime',
210 date_param_since: 'since_datetime',
211 date_param_until: 'until_datetime',
211 date_param_until: 'until_datetime',
212 hard_limit: 100_000
212 hard_limit: 100_000
213 )
213 )
214 end
214 end
215
215
216 def login
216 def login
217 end
217 end
218
218
219 def problem_hof
219 def problem_hof
220 # gen problem list
220 # gen problem list
221 @user = User.find(session[:user_id])
221 @user = User.find(session[:user_id])
222 @problems = @user.available_problems
222 @problems = @user.available_problems
223
223
224 # get selected problems or the default
224 # get selected problems or the default
225 if params[:id]
225 if params[:id]
226 begin
226 begin
227 @problem = Problem.available.find(params[:id])
227 @problem = Problem.available.find(params[:id])
228 rescue
228 rescue
229 redirect_to action: :problem_hof
229 redirect_to action: :problem_hof
230 flash[:notice] = 'Error: submissions for that problem are not viewable.'
230 flash[:notice] = 'Error: submissions for that problem are not viewable.'
231 return
231 return
232 end
232 end
233 end
233 end
234
234
235 return unless @problem
235 return unless @problem
236
236
237 + #model submisssion
238 + @model_subs = Submission.where(problem: @problem,tag: Submission.tags[:model])
239 +
240 +
241 + #calculate best submission
237 @by_lang = {} #aggregrate by language
242 @by_lang = {} #aggregrate by language
238
243
239 range =65
244 range =65
240 - @histogram = { data: Array.new(range,0), summary: {} }
245 + #@histogram = { data: Array.new(range,0), summary: {} }
241 @summary = {count: 0, solve: 0, attempt: 0}
246 @summary = {count: 0, solve: 0, attempt: 0}
242 user = Hash.new(0)
247 user = Hash.new(0)
243 Submission.where(problem_id: @problem.id).find_each do |sub|
248 Submission.where(problem_id: @problem.id).find_each do |sub|
244 #histogram
249 #histogram
245 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
250 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
246 - @histogram[:data][d.to_i] += 1 if d < range
251 + #@histogram[:data][d.to_i] += 1 if d < range
247
252
248 next unless sub.points
253 next unless sub.points
249 @summary[:count] += 1
254 @summary[:count] += 1
250 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
255 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
251
256
252 lang = Language.find_by_id(sub.language_id)
257 lang = Language.find_by_id(sub.language_id)
253 next unless lang
258 next unless lang
254 next unless sub.points >= @problem.full_score
259 next unless sub.points >= @problem.full_score
255
260
256 #initialize
261 #initialize
257 unless @by_lang.has_key?(lang.pretty_name)
262 unless @by_lang.has_key?(lang.pretty_name)
258 @by_lang[lang.pretty_name] = {
263 @by_lang[lang.pretty_name] = {
259 runtime: { avail: false, value: 2**30-1 },
264 runtime: { avail: false, value: 2**30-1 },
260 memory: { avail: false, value: 2**30-1 },
265 memory: { avail: false, value: 2**30-1 },
261 length: { avail: false, value: 2**30-1 },
266 length: { avail: false, value: 2**30-1 },
262 first: { avail: false, value: DateTime.new(3000,1,1) }
267 first: { avail: false, value: DateTime.new(3000,1,1) }
263 }
268 }
264 end
269 end
265
270
266 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
271 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
267 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
272 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
268 end
273 end
269
274
270 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
275 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
271 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
276 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
272 end
277 end
273
278
274 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and sub.user and
279 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and sub.user and
275 !sub.user.admin?
280 !sub.user.admin?
276 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
281 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
277 end
282 end
278
283
279 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
284 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
280 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
285 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
281 end
286 end
282 end
287 end
283
288
284 #process user_id
289 #process user_id
285 @by_lang.each do |lang,prop|
290 @by_lang.each do |lang,prop|
286 prop.each do |k,v|
291 prop.each do |k,v|
287 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
292 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
288 end
293 end
289 end
294 end
290
295
291 #sum into best
296 #sum into best
292 if @by_lang and @by_lang.first
297 if @by_lang and @by_lang.first
293 @best = @by_lang.first[1].clone
298 @best = @by_lang.first[1].clone
294 @by_lang.each do |lang,prop|
299 @by_lang.each do |lang,prop|
295 if @best[:runtime][:value] >= prop[:runtime][:value]
300 if @best[:runtime][:value] >= prop[:runtime][:value]
296 @best[:runtime] = prop[:runtime]
301 @best[:runtime] = prop[:runtime]
297 @best[:runtime][:lang] = lang
302 @best[:runtime][:lang] = lang
298 end
303 end
299 if @best[:memory][:value] >= prop[:memory][:value]
304 if @best[:memory][:value] >= prop[:memory][:value]
300 @best[:memory] = prop[:memory]
305 @best[:memory] = prop[:memory]
301 @best[:memory][:lang] = lang
306 @best[:memory][:lang] = lang
302 end
307 end
303 if @best[:length][:value] >= prop[:length][:value]
308 if @best[:length][:value] >= prop[:length][:value]
304 @best[:length] = prop[:length]
309 @best[:length] = prop[:length]
305 @best[:length][:lang] = lang
310 @best[:length][:lang] = lang
306 end
311 end
307 if @best[:first][:value] >= prop[:first][:value]
312 if @best[:first][:value] >= prop[:first][:value]
308 @best[:first] = prop[:first]
313 @best[:first] = prop[:first]
309 @best[:first][:lang] = lang
314 @best[:first][:lang] = lang
310 end
315 end
311 end
316 end
312 end
317 end
313
318
314 - @histogram[:summary][:max] = [@histogram[:data].max,1].max
319 + #@histogram[:summary][:max] = [@histogram[:data].max,1].max
315 @summary[:attempt] = user.count
320 @summary[:attempt] = user.count
316 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
321 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
322 +
323 +
324 + #for new graph
325 + @chart_dataset = @problem.get_jschart_history.to_json.html_safe
317 end
326 end
318
327
319 def stuck #report struggling user,problem
328 def stuck #report struggling user,problem
320 # init
329 # init
321 user,problem = nil
330 user,problem = nil
322 solve = true
331 solve = true
323 tries = 0
332 tries = 0
324 @struggle = Array.new
333 @struggle = Array.new
325 record = {}
334 record = {}
326 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
335 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
327 next unless sub.problem and sub.user
336 next unless sub.problem and sub.user
328 if user != sub.user_id or problem != sub.problem_id
337 if user != sub.user_id or problem != sub.problem_id
329 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
338 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
330 record = {user: sub.user, problem: sub.problem}
339 record = {user: sub.user, problem: sub.problem}
331 user,problem = sub.user_id, sub.problem_id
340 user,problem = sub.user_id, sub.problem_id
332 solve = false
341 solve = false
333 tries = 0
342 tries = 0
334 end
343 end
335 if sub.points >= sub.problem.full_score
344 if sub.points >= sub.problem.full_score
336 solve = true
345 solve = true
337 else
346 else
338 tries += 1
347 tries += 1
339 end
348 end
340 end
349 end
341 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
350 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
342 @struggle = @struggle[0..50]
351 @struggle = @struggle[0..50]
343 end
352 end
344
353
345
354
346 def multiple_login
355 def multiple_login
347 #user with multiple IP
356 #user with multiple IP
348 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
357 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
349 last,count = 0,0
358 last,count = 0,0
350 first = 0
359 first = 0
351 @users = []
360 @users = []
352 raw.each do |r|
361 raw.each do |r|
353 if last != r.user.login
362 if last != r.user.login
354 count = 1
363 count = 1
355 last = r.user.login
364 last = r.user.login
356 first = r
365 first = r
357 else
366 else
358 @users << first if count == 1
367 @users << first if count == 1
359 @users << r
368 @users << r
360 count += 1
369 count += 1
361 end
370 end
362 end
371 end
363
372
364 #IP with multiple user
373 #IP with multiple user
@@ -1,111 +1,115
1 class SubmissionsController < ApplicationController
1 class SubmissionsController < ApplicationController
2 + before_action :set_submission, only: [:show,:download,:compiler_msg,:rejudge,:set_tag, :edit]
2 before_action :check_valid_login
3 before_action :check_valid_login
3 before_action :submission_authorization, only: [:show, :download, :edit]
4 before_action :submission_authorization, only: [:show, :download, :edit]
4 - before_action :admin_authorization, only: [:rejudge]
5 + before_action only: [:rejudge, :set_tag] do role_authorization([:ta]) end
5
6
6 # GET /submissions
7 # GET /submissions
7 # GET /submissions.json
8 # GET /submissions.json
8 # Show problem selection and user's submission of that problem
9 # Show problem selection and user's submission of that problem
9 def index
10 def index
10 @user = @current_user
11 @user = @current_user
11 @problems = @user.available_problems
12 @problems = @user.available_problems
12
13
13 if params[:problem_id]==nil
14 if params[:problem_id]==nil
14 @problem = nil
15 @problem = nil
15 @submissions = nil
16 @submissions = nil
16 else
17 else
17 @problem = Problem.find_by_id(params[:problem_id])
18 @problem = Problem.find_by_id(params[:problem_id])
18 if (@problem == nil) or (not @problem.available)
19 if (@problem == nil) or (not @problem.available)
19 redirect_to list_main_path
20 redirect_to list_main_path
20 flash[:error] = 'Authorization error: You have no right to view submissions for this problem'
21 flash[:error] = 'Authorization error: You have no right to view submissions for this problem'
21 return
22 return
22 end
23 end
23 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc)
24 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc)
24 end
25 end
25 end
26 end
26
27
27 # GET /submissions/1
28 # GET /submissions/1
28 # GET /submissions/1.json
29 # GET /submissions/1.json
29 def show
30 def show
30 - @submission = Submission.find(params[:id])
31 -
32 #log the viewing
31 #log the viewing
33 user = User.find(session[:user_id])
32 user = User.find(session[:user_id])
34 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
33 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
35
34
36 @task = @submission.task
35 @task = @submission.task
37 end
36 end
38
37
39 def download
38 def download
40 - @submission = Submission.find(params[:id])
41 send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
39 send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
42 end
40 end
43
41
44 def compiler_msg
42 def compiler_msg
45 - @submission = Submission.find(params[:id])
46 respond_to do |format|
43 respond_to do |format|
47 format.js
44 format.js
48 end
45 end
49 end
46 end
50
47
51 #on-site new submission on specific problem
48 #on-site new submission on specific problem
52 def direct_edit_problem
49 def direct_edit_problem
53 @problem = Problem.find(params[:problem_id])
50 @problem = Problem.find(params[:problem_id])
54 unless @current_user.can_view_problem?(@problem)
51 unless @current_user.can_view_problem?(@problem)
55 unauthorized_redirect
52 unauthorized_redirect
56 return
53 return
57 end
54 end
58 @source = ''
55 @source = ''
59 if (params[:view_latest])
56 if (params[:view_latest])
60 sub = Submission.find_last_by_user_and_problem(@current_user.id,@problem.id)
57 sub = Submission.find_last_by_user_and_problem(@current_user.id,@problem.id)
61 @source = @submission.source.to_s if @submission and @submission.source
58 @source = @submission.source.to_s if @submission and @submission.source
62 end
59 end
63 render 'edit'
60 render 'edit'
64 end
61 end
65
62
66 # GET /submissions/1/edit
63 # GET /submissions/1/edit
67 def edit
64 def edit
68 - @submission = Submission.find(params[:id])
69 @source = @submission.source.to_s
65 @source = @submission.source.to_s
70 @problem = @submission.problem
66 @problem = @submission.problem
71 @lang_id = @submission.language.id
67 @lang_id = @submission.language.id
72 end
68 end
73
69
74
70
75 def get_latest_submission_status
71 def get_latest_submission_status
76 @problem = Problem.find(params[:pid])
72 @problem = Problem.find(params[:pid])
77 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
73 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
78 respond_to do |format|
74 respond_to do |format|
79 format.js
75 format.js
80 end
76 end
81 end
77 end
82
78
83 # GET /submissions/:id/rejudge
79 # GET /submissions/:id/rejudge
84 def rejudge
80 def rejudge
85 - @submission = Submission.find(params[:id])
86 @task = @submission.task
81 @task = @submission.task
87 @task.status_inqueue! if @task
82 @task.status_inqueue! if @task
88 respond_to do |format|
83 respond_to do |format|
89 format.js
84 format.js
90 end
85 end
91 end
86 end
92
87
88 + def set_tag
89 + @submission.update(tag: params[:tag])
90 + redirect_to @submission
91 + end
92 +
93 protected
93 protected
94
94
95 def submission_authorization
95 def submission_authorization
96 #admin always has privileged
96 #admin always has privileged
97 return true if @current_user.admin?
97 return true if @current_user.admin?
98 return true if @current_user.has_role?('ta') && (['show','download'].include? action_name)
98 return true if @current_user.has_role?('ta') && (['show','download'].include? action_name)
99
99
100 sub = Submission.find(params[:id])
100 sub = Submission.find(params[:id])
101 if @current_user.available_problems.include? sub.problem
101 if @current_user.available_problems.include? sub.problem
102 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
102 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
103 end
103 end
104
104
105 #default to NO
105 #default to NO
106 unauthorized_redirect
106 unauthorized_redirect
107 return false
107 return false
108 end
108 end
109 +
110 + def set_submission
111 + @submission = Submission.find(params[:id])
112 + end
109
113
110
114
111 end
115 end
@@ -1,74 +1,96
1 class Problem < ActiveRecord::Base
1 class Problem < ActiveRecord::Base
2
2
3 belongs_to :description
3 belongs_to :description
4 has_and_belongs_to_many :contests, :uniq => true
4 has_and_belongs_to_many :contests, :uniq => true
5
5
6 #has_and_belongs_to_many :groups
6 #has_and_belongs_to_many :groups
7 has_many :groups_problems, class_name: 'GroupProblem'
7 has_many :groups_problems, class_name: 'GroupProblem'
8 has_many :groups, :through => :groups_problems
8 has_many :groups, :through => :groups_problems
9
9
10 has_many :problems_tags, class_name: 'ProblemTag'
10 has_many :problems_tags, class_name: 'ProblemTag'
11 has_many :tags, through: :problems_tags
11 has_many :tags, through: :problems_tags
12
12
13 has_many :test_pairs, :dependent => :delete_all
13 has_many :test_pairs, :dependent => :delete_all
14 has_many :testcases, :dependent => :destroy
14 has_many :testcases, :dependent => :destroy
15
15
16 has_many :submissions
16 has_many :submissions
17
17
18 validates_presence_of :name
18 validates_presence_of :name
19 validates_format_of :name, :with => /\A\w+\z/
19 validates_format_of :name, :with => /\A\w+\z/
20 validates_presence_of :full_name
20 validates_presence_of :full_name
21
21
22 scope :available, -> { where(available: true) }
22 scope :available, -> { where(available: true) }
23
23
24 DEFAULT_TIME_LIMIT = 1
24 DEFAULT_TIME_LIMIT = 1
25 DEFAULT_MEMORY_LIMIT = 32
25 DEFAULT_MEMORY_LIMIT = 32
26
26
27 + def get_jschart_history
28 + start = 4.month.ago.beginning_of_day
29 + start_date = start.to_date
30 + count = Submission.where(problem: self).where('submitted_at >= ?', start).group('DATE(submitted_at)').count
31 + i = 0
32 + label = []
33 + value = []
34 + while (start_date + i < Time.zone.now.to_date)
35 + if (start_date+i).day == 1
36 + #label << (start_date+i).strftime("%d %b %Y")
37 + #label << (start_date+i).strftime("%d")
38 + else
39 + #label << ' '
40 + #label << (start_date+i).strftime("%d")
41 + end
42 + label << (start_date+i).strftime("%d-%b")
43 + value << (count[start_date+i] || 0)
44 + i+=1
45 + end
46 + return {labels: label,datasets: [label:'sub',data: value, backgroundColor: 'rgba(54, 162, 235, 0.2)', borderColor: 'rgb(75, 192, 192)']}
47 + end
48 +
27 def self.available_problems
49 def self.available_problems
28 available.order(date_added: :desc).order(:name)
50 available.order(date_added: :desc).order(:name)
29 #Problem.available.all(:order => "date_added DESC, name ASC")
51 #Problem.available.all(:order => "date_added DESC, name ASC")
30 end
52 end
31
53
32 def self.create_from_import_form_params(params, old_problem=nil)
54 def self.create_from_import_form_params(params, old_problem=nil)
33 org_problem = old_problem || Problem.new
55 org_problem = old_problem || Problem.new
34 import_params, problem = Problem.extract_params_and_check(params,
56 import_params, problem = Problem.extract_params_and_check(params,
35 org_problem)
57 org_problem)
36
58
37 if !problem.errors.empty?
59 if !problem.errors.empty?
38 return problem, 'Error importing'
60 return problem, 'Error importing'
39 end
61 end
40
62
41 problem.full_score = 100
63 problem.full_score = 100
42 problem.date_added = Time.new
64 problem.date_added = Time.new
43 problem.test_allowed = true
65 problem.test_allowed = true
44 problem.output_only = false
66 problem.output_only = false
45 problem.available = false
67 problem.available = false
46
68
47 if not problem.save
69 if not problem.save
48 return problem, 'Error importing'
70 return problem, 'Error importing'
49 end
71 end
50
72
51 import_to_db = params.has_key? :import_to_db
73 import_to_db = params.has_key? :import_to_db
52
74
53 importer = TestdataImporter.new(problem)
75 importer = TestdataImporter.new(problem)
54
76
55 if not importer.import_from_file(import_params[:file],
77 if not importer.import_from_file(import_params[:file],
56 import_params[:time_limit],
78 import_params[:time_limit],
57 import_params[:memory_limit],
79 import_params[:memory_limit],
58 import_params[:checker_name],
80 import_params[:checker_name],
59 import_to_db)
81 import_to_db)
60 problem.errors.add(:base,'Import error.')
82 problem.errors.add(:base,'Import error.')
61 end
83 end
62
84
63 return problem, importer.log_msg
85 return problem, importer.log_msg
64 end
86 end
65
87
66 def self.download_file_basedir
88 def self.download_file_basedir
67 return "#{Rails.root}/data/tasks"
89 return "#{Rails.root}/data/tasks"
68 end
90 end
69
91
70 def get_submission_stat
92 def get_submission_stat
71 result = Hash.new
93 result = Hash.new
72 #total number of submission
94 #total number of submission
73 result[:total_sub] = Submission.where(problem_id: self.id).count
95 result[:total_sub] = Submission.where(problem_id: self.id).count
74 result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
96 result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
@@ -1,131 +1,133
1 class Submission < ActiveRecord::Base
1 class Submission < ActiveRecord::Base
2
2
3 + enum tag: {default: 0, model: 1}, _prefix: true
4 +
3 belongs_to :language
5 belongs_to :language
4 belongs_to :problem
6 belongs_to :problem
5 belongs_to :user
7 belongs_to :user
6
8
7 before_validation :assign_problem
9 before_validation :assign_problem
8 before_validation :assign_language
10 before_validation :assign_language
9
11
10 validates_presence_of :source
12 validates_presence_of :source
11 validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'code too long, the limit is 100,000 bytes'
13 validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'code too long, the limit is 100,000 bytes'
12 validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short'
14 validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short'
13 validate :must_have_valid_problem
15 validate :must_have_valid_problem
14 validate :must_specify_language
16 validate :must_specify_language
15
17
16 has_one :task
18 has_one :task
17
19
18 before_save :assign_latest_number_if_new_recond
20 before_save :assign_latest_number_if_new_recond
19
21
20 def self.find_last_by_user_and_problem(user_id, problem_id)
22 def self.find_last_by_user_and_problem(user_id, problem_id)
21 where("user_id = ? AND problem_id = ?",user_id,problem_id).last
23 where("user_id = ? AND problem_id = ?",user_id,problem_id).last
22 end
24 end
23
25
24 def self.find_all_last_by_problem(problem_id)
26 def self.find_all_last_by_problem(problem_id)
25 # need to put in SQL command, maybe there's a better way
27 # need to put in SQL command, maybe there's a better way
26 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
28 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
27 - "WHERE id = " +
29 + "WHERE id = " +
28 - "(SELECT MAX(id) FROM submissions AS subs " +
30 + "(SELECT MAX(id) FROM submissions AS subs " +
29 - "WHERE subs.user_id = submissions.user_id AND " +
31 + "WHERE subs.user_id = submissions.user_id AND " +
30 - "problem_id = " + problem_id.to_s + " " +
32 + "problem_id = " + problem_id.to_s + " " +
31 - "GROUP BY user_id) " +
33 + "GROUP BY user_id) " +
32 - "ORDER BY user_id")
34 + "ORDER BY user_id")
33 end
35 end
34
36
35 def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id)
37 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)
38 records = Submission.where(problem_id: problem_id,user_id: user_id)
37 records = records.where('id >= ?',since_id) if since_id and since_id > 0
39 records = records.where('id >= ?',since_id) if since_id and since_id > 0
38 records = records.where('id <= ?',until_id) if until_id and until_id > 0
40 records = records.where('id <= ?',until_id) if until_id and until_id > 0
39 records.all
41 records.all
40 end
42 end
41
43
42 def self.find_last_for_all_available_problems(user_id)
44 def self.find_last_for_all_available_problems(user_id)
43 submissions = Array.new
45 submissions = Array.new
44 problems = Problem.available_problems
46 problems = Problem.available_problems
45 problems.each do |problem|
47 problems.each do |problem|
46 sub = Submission.find_last_by_user_and_problem(user_id, problem.id)
48 sub = Submission.find_last_by_user_and_problem(user_id, problem.id)
47 submissions << sub if sub!=nil
49 submissions << sub if sub!=nil
48 end
50 end
49 submissions
51 submissions
50 end
52 end
51
53
52 def self.find_by_user_problem_number(user_id, problem_id, number)
54 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
55 where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first
54 end
56 end
55
57
56 def self.find_all_by_user_problem(user_id, problem_id)
58 def self.find_all_by_user_problem(user_id, problem_id)
57 where("user_id = ? AND problem_id = ?",user_id,problem_id)
59 where("user_id = ? AND problem_id = ?",user_id,problem_id)
58 end
60 end
59
61
60 def download_filename
62 def download_filename
61 if self.problem.output_only
63 if self.problem.output_only
62 return self.source_filename
64 return self.source_filename
63 else
65 else
64 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
66 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
65 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
67 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
66 end
68 end
67 end
69 end
68
70
69 protected
71 protected
70
72
71 def self.find_option_in_source(option, source)
73 def self.find_option_in_source(option, source)
72 if source==nil
74 if source==nil
73 return nil
75 return nil
74 end
76 end
75 i = 0
77 i = 0
76 source.each_line do |s|
78 source.each_line do |s|
77 if s =~ option
79 if s =~ option
78 - words = s.split
80 + words = s.split
79 - return words[1]
81 + return words[1]
80 end
82 end
81 i = i + 1
83 i = i + 1
82 if i==10
84 if i==10
83 - return nil
85 + return nil
84 end
86 end
85 end
87 end
86 return nil
88 return nil
87 end
89 end
88
90
89 def self.find_language_in_source(source, source_filename="")
91 def self.find_language_in_source(source, source_filename="")
90 langopt = find_option_in_source(/^LANG:/,source)
92 langopt = find_option_in_source(/^LANG:/,source)
91 if langopt
93 if langopt
92 return (Language.find_by_name(langopt) ||
94 return (Language.find_by_name(langopt) ||
93 Language.find_by_pretty_name(langopt))
95 Language.find_by_pretty_name(langopt))
94 else
96 else
95 if source_filename
97 if source_filename
96 return Language.find_by_extension(source_filename.split('.').last)
98 return Language.find_by_extension(source_filename.split('.').last)
97 else
99 else
98 return nil
100 return nil
99 end
101 end
100 end
102 end
101 end
103 end
102
104
103 def self.find_problem_in_source(source, source_filename="")
105 def self.find_problem_in_source(source, source_filename="")
104 prob_opt = find_option_in_source(/^TASK:/,source)
106 prob_opt = find_option_in_source(/^TASK:/,source)
105 if problem = Problem.find_by_name(prob_opt)
107 if problem = Problem.find_by_name(prob_opt)
106 return problem
108 return problem
107 else
109 else
108 if source_filename
110 if source_filename
109 return Problem.find_by_name(source_filename.split('.').first)
111 return Problem.find_by_name(source_filename.split('.').first)
110 else
112 else
111 return nil
113 return nil
112 end
114 end
113 end
115 end
114 end
116 end
115
117
116 def assign_problem
118 def assign_problem
117 if self.problem_id!=-1
119 if self.problem_id!=-1
118 begin
120 begin
119 self.problem = Problem.find(self.problem_id)
121 self.problem = Problem.find(self.problem_id)
120 rescue ActiveRecord::RecordNotFound
122 rescue ActiveRecord::RecordNotFound
121 self.problem = nil
123 self.problem = nil
122 end
124 end
123 else
125 else
124 self.problem = Submission.find_problem_in_source(self.source,
126 self.problem = Submission.find_problem_in_source(self.source,
125 self.source_filename)
127 self.source_filename)
126 end
128 end
127 end
129 end
128
130
129 def assign_language
131 def assign_language
130 if self.language == nil
132 if self.language == nil
131 self.language = Submission.find_language_in_source(self.source,
133 self.language = Submission.find_language_in_source(self.source,
@@ -1,136 +1,154
1 :css
1 :css
2 .hof_user { color: orangered; font-style: italic; }
2 .hof_user { color: orangered; font-style: italic; }
3 .hof_language { color: green; font-style: italic; }
3 .hof_language { color: green; font-style: italic; }
4 .hof_value { color: deeppink;font-style: italic; }
4 .hof_value { color: deeppink;font-style: italic; }
5 .info_param { font-weight: bold;text-align: right; }
5 .info_param { font-weight: bold;text-align: right; }
6 .tooltip {
6 .tooltip {
7 font-family: Verdana,sans-serif;
7 font-family: Verdana,sans-serif;
8 font-weight: normal;
8 font-weight: normal;
9 text-align: left;
9 text-align: left;
10 font-size: 1.0em;
10 font-size: 1.0em;
11 color: black;
11 color: black;
12 line-height: 1.1;
12 line-height: 1.1;
13 display: none;
13 display: none;
14 min-width: 20em;
14 min-width: 20em;
15 position: absolute;
15 position: absolute;
16 left: 25px;
16 left: 25px;
17 bottom: 5px;
17 bottom: 5px;
18 border: 1px solid;
18 border: 1px solid;
19 padding: 5px;
19 padding: 5px;
20 background-color: #FFF;
20 background-color: #FFF;
21 word-wrap: break-word;
21 word-wrap: break-word;
22 z-index: 9999;
22 z-index: 9999;
23 overflow: auto;
23 overflow: auto;
24 }
24 }
25
25
26
26
27 - .container
27 + .container-fluid
28 + .row
29 + .col-md-8
30 + .card
31 + .card-body
32 + %h2.card-title Submission History
33 + %canvas#chart{height: '50px'}
34 +
35 + .col-md-4
36 + .card
37 + .card-body
38 + %h2.card-title General Info
39 + .row
40 + .col-sm-6
41 + Subs
42 + .col-sm-6
43 + = @summary[:count]
44 + .row
45 + .col-sm-6
46 + Solved/Attempted User
47 + .col-sm-6
48 + #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
28 .row
49 .row
29 .col-md-4
50 .col-md-4
30 - %h2 Overall Stat
51 + .card
31 - %table.table.table-hover
52 + .card-body
32 - %thead
53 + %h2.card-title Model submission
33 - %tr
34 - %th
35 - %th
36 - %tbody
37 - %tr
38 - %td.info_param Submissions
39 - %td= @summary[:count]
40 - %tr
41 - %td.info_param Solved/Attempted User
42 - %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
43 - - if @best
44 - %tr
45 - %td.info_param Best Runtime
46 - %td
47 - by #{link_to @best[:runtime][:user], stat_user_path(@best[:runtime][:user_id])}
48 - %br
49 - using <span class="text-success">#{@best[:runtime][:lang]}</span>
50 - %br
51 - with <span class="text-success">#{@best[:runtime][:value] * 1000} milliseconds</span>
52 - %br
53 - at submission
54 - = link_to "#" + @best[:runtime][:sub_id].to_s, submission_path(@best[:runtime][:sub_id])
55 -
56 - %tr
57 - %td.info_param
58 - Best Memory Usage
59 - %sup{ id: "xmem_remark",
60 - style: "position:relative; color: blue;",
61 - data: {toggle: 'tooltip', placement: 'top', animation: 'false', delay: 20},
62 - title: "This counts only for submission with 100% score. Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...)"}
63 - [?]
64 - %td
65 - by #{link_to @best[:memory][:user], stat_user_path(@best[:memory][:user_id])}
66 - %br
67 - using <span class="text-success">#{@best[:memory][:lang]}</span>
68 - %br
69 - with <span class="text-success">#{number_with_delimiter(@best[:memory][:value])} kbytes </span>
70 - %br
71 - at submission
72 - = link_to "#" + @best[:memory][:sub_id].to_s, submission_path(@best[:memory][:sub_id])
73 -
74 - %tr
75 - %td.info_param Shortest Code
76 - %td
77 - by #{link_to @best[:length][:user], stat_user_path(@best[:length][:user_id])}
78 - %br
79 - using <span class="text-success">#{@best[:length][:lang]}</span>
80 - %br
81 - with <span class="text-success">#{@best[:length][:value]} bytes</span>
82 - %br
83 - at submission
84 - = link_to "#" + @best[:length][:sub_id].to_s, submission_path(@best[:length][:sub_id])
85 -
86 - %tr
87 - %td.info_param First solver
88 - %td
89 - - if @best[:first][:user] != '(NULL)'
90 - #{link_to @best[:first][:user], stat_user_path(@best[:first][:user_id])} is the first solver
91 - %br
92 - using <span class="text-success">#{@best[:first][:lang]}</span>
93 - %br
94 - on <span class="text-success">#{@best[:first][:value]}</span>
95 - %br
96 - at submission
97 - = link_to "#" + @best[:first][:sub_id].to_s, submission_path( @best[:first][:sub_id])
98 - - else
99 - no first solver
100 - .col-md-8
101 - - if @best
102 - %h2 By Language
103 %table.table.table-hover
54 %table.table.table-hover
104 %thead
55 %thead
105 %tr
56 %tr
106 - %th Language
57 + %th #Sub
107 - %th Best runtime (ms)
58 + %th Author
108 - %th Best memory (kbytes)
109 - %th Shortest Code (bytes)
110 - %th First solver
111 %tbody
59 %tbody
112 - - @by_lang.each do |lang,value|
60 + - @model_subs.each do |sub|
113 %tr
61 %tr
114 - %td= lang
62 + %td= link_to "##{sub.id}", submission_path(sub)
63 + %td= sub.user.full_name
64 + .col-md-8
65 + - if @best
66 + .card
67 + .card-body
68 + %h2.card-title Top Submissions
69 + %table.table.table-hover
70 + %thead
71 + %tr
72 + %th Language
73 + %th Best runtime (ms)
74 + %th Best memory (kbytes)
75 + %th Shortest Code (bytes)
76 + %th First solver
77 + %tbody
78 + %tr.bg-warning
115 %td
79 %td
116 - = link_to value[:runtime][:user], stat_user_path(value[:runtime][:user_id])
80 + Overall
81 + %td
82 + by #{link_to @best[:runtime][:user], stat_user_path(@best[:runtime][:user_id])}
83 + %br
84 + using <span class="text-success">#{@best[:runtime][:lang]}</span>
85 + %br
86 + with <span class="text-success">#{@best[:runtime][:value] * 1000} milliseconds</span>
87 + %br= link_to "#" + @best[:runtime][:sub_id].to_s, submission_path(@best[:runtime][:sub_id])
88 + %td
89 + by #{link_to @best[:memory][:user], stat_user_path(@best[:memory][:user_id])}
117 %br
90 %br
118 - = "#{(value[:runtime][:value] * 1000).to_i} @"
91 + using <span class="text-success">#{@best[:memory][:lang]}</span>
119 - = link_to "#" + value[:runtime][:sub_id].to_s, submission_path( value[:runtime][:sub_id])
92 + %br
93 + with <span class="text-success">#{number_with_delimiter(@best[:memory][:value])} kbytes </span>
94 + %br= link_to "#" + @best[:memory][:sub_id].to_s, submission_path(@best[:memory][:sub_id])
95 + %td
96 + by #{link_to @best[:length][:user], stat_user_path(@best[:length][:user_id])}
97 + %br
98 + using <span class="text-success">#{@best[:length][:lang]}</span>
99 + %br
100 + with <span class="text-success">#{@best[:length][:value]} bytes</span>
101 + %br= link_to "#" + @best[:length][:sub_id].to_s, submission_path(@best[:length][:sub_id])
120 %td
102 %td
121 - = link_to value[:memory][:user], stat_user_path( value[:memory][:user_id])
103 + - if @best[:first][:user] != '(NULL)'
122 - %br
104 + #{link_to @best[:first][:user], stat_user_path(@best[:first][:user_id])} is the first solver
123 - = "#{number_with_delimiter(value[:memory][:value])} @"
105 + %br
124 - = link_to "#" + value[:memory][:sub_id].to_s, submission_path(value[:memory][:sub_id])
106 + using <span class="text-success">#{@best[:first][:lang]}</span>
125 - %td
107 + %br
126 - = link_to value[:length][:user], stat_user_path(value[:length][:user_id])
108 + on <span class="text-success">#{@best[:first][:value]}</span>
127 - %br
109 + %br= link_to "#" + @best[:first][:sub_id].to_s, submission_path( @best[:first][:sub_id])
128 - = "#{value[:length][:value]} @"
110 + - else
129 - = link_to "#" + value[:length][:sub_id].to_s, submission_path(value[:length][:sub_id])
111 + no first solver
130 - %td
112 + - @by_lang.each do |lang,value|
131 - - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong...
113 + %tr
132 - = link_to value[:first][:user], stat_user_path(value[:first][:user_id])
114 + %td= lang
115 + %td
116 + = link_to value[:runtime][:user], stat_user_path(value[:runtime][:user_id])
117 + %br
118 + = "#{(value[:runtime][:value] * 1000).to_i} @"
119 + = link_to "#" + value[:runtime][:sub_id].to_s, submission_path( value[:runtime][:sub_id])
120 + %td
121 + = link_to value[:memory][:user], stat_user_path( value[:memory][:user_id])
122 + %br
123 + = "#{number_with_delimiter(value[:memory][:value])} @"
124 + = link_to "#" + value[:memory][:sub_id].to_s, submission_path(value[:memory][:sub_id])
125 + %td
126 + = link_to value[:length][:user], stat_user_path(value[:length][:user_id])
133 %br
127 %br
134 - = "#{value[:first][:value]} @"
128 + = "#{value[:length][:value]} @"
135 - = link_to "#" + value[:first][:sub_id].to_s, submission_path( value[:first][:sub_id])
129 + = link_to "#" + value[:length][:sub_id].to_s, submission_path(value[:length][:sub_id])
130 + %td
131 + - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong...
132 + = link_to value[:first][:user], stat_user_path(value[:first][:user_id])
133 + %br
134 + = "#{value[:first][:value]} @"
135 + = link_to "#" + value[:first][:sub_id].to_s, submission_path( value[:first][:sub_id])
136
136
137 + %script{src:"https://cdn.jsdelivr.net/npm/chart.js"}
138 + :javascript
139 + data = #{@chart_dataset}
140 + config = {
141 + type: 'bar',
142 + data: data,
143 + options: {
144 + plugins: {
145 + legend: {
146 + display: false
147 + },
148 + },
149 + }
150 + }
151 + Chart.defaults.font.size = 15
152 + //Chart.defaults.font.family = 'Sarabun Light'
153 + chart = new Chart($('#chart'),config)
154 +
@@ -1,29 +1,29
1
1
2 /- if params[:id]
2 /- if params[:id]
3 / %h1 Tasks Hall of Fame
3 / %h1 Tasks Hall of Fame
4 / = link_to('[back to All-Time Hall of Fame]', action: 'problem_hof', id: nil )
4 / = link_to('[back to All-Time Hall of Fame]', action: 'problem_hof', id: nil )
5 /- else
5 /- else
6 / %h1 All-Time Hall of Fame
6 / %h1 All-Time Hall of Fame
7
7
8 .panel.panel-info
8 .panel.panel-info
9 .panel-heading
9 .panel-heading
10 Select Task
10 Select Task
11 .panel-body
11 .panel-body
12 .form-inline
12 .form-inline
13 = select 'report',
13 = select 'report',
14 'problem_id',
14 'problem_id',
15 @problems.collect {|p| ["[#{p.name}] #{p.full_name}", problem_hof_report_path(p)]},
15 @problems.collect {|p| ["[#{p.name}] #{p.full_name}", problem_hof_report_path(p)]},
16 {:selected => problem_hof_report_path(@problem)},
16 {:selected => problem_hof_report_path(@problem)},
17 { class: 'select2 form-control' }
17 { class: 'select2 form-control' }
18 %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: "#report_problem_id"}} Go
18 %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: "#report_problem_id"}} Go
19
19
20
20
21 - unless params[:id]
21 - unless params[:id]
22 /=render partial: 'all_time_hof'
22 /=render partial: 'all_time_hof'
23 Please select a problem.
23 Please select a problem.
24 - else
24 - else
25 %h1 [#{Problem.find(params[:id]).name}] #{Problem.find(params[:id]).full_name}
25 %h1 [#{Problem.find(params[:id]).name}] #{Problem.find(params[:id]).full_name}
26 - %h2 Submission History
26 + -# %h2 Submission History
27 - =render partial: 'application/bar_graph', locals: { histogram: @histogram }
27 + -# =render partial: 'application/bar_graph', locals: { histogram: @histogram }
28 =render partial: 'task_hof'
28 =render partial: 'task_hof'
29
29
@@ -44,73 +44,85
44 %td
44 %td
45 - if @submission.problem!=nil
45 - if @submission.problem!=nil
46 = link_to "[#{@submission.problem.name}]", stat_problem_path(@submission.problem)
46 = link_to "[#{@submission.problem.name}]", stat_problem_path(@submission.problem)
47 = @submission.problem.full_name
47 = @submission.problem.full_name
48 = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @submission.problem
48 = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @submission.problem
49 - else
49 - else
50 = "(n/a)"
50 = "(n/a)"
51 %tr
51 %tr
52 %td.text-right
52 %td.text-right
53 %strong Tries
53 %strong Tries
54 %td= @submission.number
54 %td= @submission.number
55 %tr
55 %tr
56 %td.text-right
56 %td.text-right
57 %strong Language
57 %strong Language
58 %td= @submission.language.pretty_name
58 %td= @submission.language.pretty_name
59 %tr
59 %tr
60 %td.text-right
60 %td.text-right
61 %strong Submitted
61 %strong Submitted
62 %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
62 %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
63 %tr
63 %tr
64 %td.text-right
64 %td.text-right
65 %strong Graded
65 %strong Graded
66 - if @submission.graded_at
66 - if @submission.graded_at
67 %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
67 %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
68 - else
68 - else
69 %td -
69 %td -
70 %tr
70 %tr
71 %td.text-right
71 %td.text-right
72 %strong Points
72 %strong Points
73 %td #{@submission.points}/#{@submission.try(:problem).try(:full_score)}
73 %td #{@submission.points}/#{@submission.try(:problem).try(:full_score)}
74 %tr
74 %tr
75 %td.text-right
75 %td.text-right
76 %strong Comment
76 %strong Comment
77 %td #{@submission.grader_comment}
77 %td #{@submission.grader_comment}
78 %tr
78 %tr
79 %td.text-right
79 %td.text-right
80 %strong Runtime (s)
80 %strong Runtime (s)
81 %td #{@submission.max_runtime}
81 %td #{@submission.max_runtime}
82 %tr
82 %tr
83 %td.text-right
83 %td.text-right
84 %strong Memory (kb)
84 %strong Memory (kb)
85 %td #{@submission.peak_memory}
85 %td #{@submission.peak_memory}
86 %tr
86 %tr
87 %td.text-right
87 %td.text-right
88 %strong Compiler result
88 %strong Compiler result
89 %td
89 %td
90 %button.btn.btn-info.btn-xs{type: 'button', data: {toggle: 'modal', target: '#compiler'}}
90 %button.btn.btn-info.btn-xs{type: 'button', data: {toggle: 'modal', target: '#compiler'}}
91 view
91 view
92 - - if session[:admin]
93 - %tr
94 - %td.text-right
95 - %strong IP
96 - %td #{@submission.ip_address}
97 %tr
92 %tr
98 %td.text-right
93 %td.text-right
99 %strong Grading Task Status
94 %strong Grading Task Status
100 %td
95 %td
101 = @task.status_str if @task
96 = @task.status_str if @task
102 - if session[:admin]
97 - if session[:admin]
103 = link_to "rejudge", rejudge_submission_path, data: {remote: true}, class: 'btn btn-info btn-xs'
98 = link_to "rejudge", rejudge_submission_path, data: {remote: true}, class: 'btn btn-info btn-xs'
99 + - if session[:admin]
100 + %tr
101 + %td.text-right
102 + %strong IP
103 + %td #{@submission.ip_address}
104 + %tr
105 + %td.text-right
106 + %strong Model solution
107 + %td
108 + - if @submission.tag_model?
109 + YES
110 + - if session[:admin]
111 + = link_to "remove model status", set_tag_submission_path(@submission, tag: :default), class: 'btn btn-warning btn-xs'
112 + - else
113 + No
114 + - if session[:admin]
115 + = link_to "set as model solution", set_tag_submission_path(@submission, tag: :model), class: 'btn btn-success btn-xs'
104
116
105
117
106 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
118 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
107 .modal-dialog.modal-lg{role:'document'}
119 .modal-dialog.modal-lg{role:'document'}
108 .modal-content
120 .modal-content
109 .modal-header
121 .modal-header
110 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
122 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
111 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
123 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
112 %h4 Compiler message
124 %h4 Compiler message
113 .modal-body
125 .modal-body
114 %pre#compiler_msg= @submission.compiler_message
126 %pre#compiler_msg= @submission.compiler_message
115 .modal-footer
127 .modal-footer
116 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
128 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
@@ -49,96 +49,97
49 resources :groups do
49 resources :groups do
50 member do
50 member do
51 post 'add_user', to: 'groups#add_user', as: 'add_user'
51 post 'add_user', to: 'groups#add_user', as: 'add_user'
52 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
52 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
53 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
53 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
54 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
54 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
55 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
55 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
56 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
56 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
57 get 'toggle'
57 get 'toggle'
58 end
58 end
59 collection do
59 collection do
60
60
61 end
61 end
62 end
62 end
63
63
64 resources :testcases, only: [] do
64 resources :testcases, only: [] do
65 member do
65 member do
66 get 'download_input'
66 get 'download_input'
67 get 'download_sol'
67 get 'download_sol'
68 end
68 end
69 collection do
69 collection do
70 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
70 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
71 end
71 end
72 end
72 end
73
73
74 resources :grader_configuration, controller: 'configurations' do
74 resources :grader_configuration, controller: 'configurations' do
75 collection do
75 collection do
76 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
76 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
77 end
77 end
78 end
78 end
79
79
80 resources :users do
80 resources :users do
81 member do
81 member do
82 get 'toggle_activate', 'toggle_enable'
82 get 'toggle_activate', 'toggle_enable'
83 get 'stat'
83 get 'stat'
84 end
84 end
85 collection do
85 collection do
86 get 'profile'
86 get 'profile'
87 post 'chg_passwd'
87 post 'chg_passwd'
88 post 'chg_default_language'
88 post 'chg_default_language'
89 end
89 end
90 end
90 end
91
91
92 resources :submissions do
92 resources :submissions do
93 member do
93 member do
94 get 'download'
94 get 'download'
95 get 'compiler_msg'
95 get 'compiler_msg'
96 get 'rejudge'
96 get 'rejudge'
97 + get 'set_tag'
97 end
98 end
98 collection do
99 collection do
99 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
100 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
100 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
101 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
101 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
102 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
102 end
103 end
103 end
104 end
104
105
105
106
106 #user admin
107 #user admin
107 resources :user_admin do
108 resources :user_admin do
108 collection do
109 collection do
109 match 'bulk_manage', via: [:get, :post]
110 match 'bulk_manage', via: [:get, :post]
110 get 'bulk_mail'
111 get 'bulk_mail'
111 get 'user_stat'
112 get 'user_stat'
112 get 'import'
113 get 'import'
113 get 'new_list'
114 get 'new_list'
114 get 'admin'
115 get 'admin'
115 get 'active'
116 get 'active'
116 get 'mass_mailing'
117 get 'mass_mailing'
117 match 'modify_role', via: [:get, :post]
118 match 'modify_role', via: [:get, :post]
118 match 'create_from_list', via: [:get, :post]
119 match 'create_from_list', via: [:get, :post]
119 match 'random_all_passwords', via: [:get, :post]
120 match 'random_all_passwords', via: [:get, :post]
120 end
121 end
121 member do
122 member do
122 get 'clear_last_ip'
123 get 'clear_last_ip'
123 end
124 end
124 end
125 end
125
126
126 resources :contest_management, only: [:index] do
127 resources :contest_management, only: [:index] do
127 collection do
128 collection do
128 get 'user_stat'
129 get 'user_stat'
129 get 'clear_stat'
130 get 'clear_stat'
130 get 'clear_all_stat'
131 get 'clear_all_stat'
131 get 'change_contest_mode'
132 get 'change_contest_mode'
132 end
133 end
133 end
134 end
134
135
135 #get 'user_admin', to: 'user_admin#index'
136 #get 'user_admin', to: 'user_admin#index'
136 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
137 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
137 #post 'user_admin', to: 'user_admin#create'
138 #post 'user_admin', to: 'user_admin#create'
138 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
139 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
139
140
140 #singular resource
141 #singular resource
141 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
142 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
142 #report
143 #report
143 resource :report, only: [], controller: 'report' do
144 resource :report, only: [], controller: 'report' do
144 get 'login'
145 get 'login'
You need to be logged in to leave comments. Login now