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: 168 inserted, 83 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,253 +1,264
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
90
101
91 # check if the user is enabled
102 # check if the user is enabled
92 unless @current_user.enabled? || @current_user.admin?
103 unless @current_user.enabled? || @current_user.admin?
93 unauthorized_redirect 'Your account is disabled'
104 unauthorized_redirect 'Your account is disabled'
94 return false
105 return false
95 end
106 end
96
107
97 # check if user ip is allowed
108 # check if user ip is allowed
98 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
109 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
99 unless is_request_ip_allowed?
110 unless is_request_ip_allowed?
100 unauthorized_redirect 'Your IP is not allowed to login at this time.'
111 unauthorized_redirect 'Your IP is not allowed to login at this time.'
101 return false
112 return false
102 end
113 end
103 end
114 end
104
115
105 if GraderConfiguration.multicontests?
116 if GraderConfiguration.multicontests?
106 return true if @current_user.admin?
117 return true if @current_user.admin?
107 begin
118 begin
108 if @current_user.contest_stat(true).forced_logout
119 if @current_user.contest_stat(true).forced_logout
109 flash[:notice] = 'You have been automatically logged out.'
120 flash[:notice] = 'You have been automatically logged out.'
110 redirect_to :controller => 'main', :action => 'index'
121 redirect_to :controller => 'main', :action => 'index'
111 end
122 end
112 rescue
123 rescue
113 end
124 end
114 end
125 end
115 return true
126 return true
116 end
127 end
117
128
118 #redirect to root (and also force logout)
129 #redirect to root (and also force logout)
119 #if the user use different ip from the previous connection
130 #if the user use different ip from the previous connection
120 # only applicable when MULTIPLE_IP_LOGIN options is false only
131 # only applicable when MULTIPLE_IP_LOGIN options is false only
121 def authenticate_by_ip_address
132 def authenticate_by_ip_address
122 #this assume that we have already authenticate normally
133 #this assume that we have already authenticate normally
123 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
134 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
124 user = User.find(session[:user_id])
135 user = User.find(session[:user_id])
125 if (!user.admin? && user.last_ip && user.last_ip != request.remote_ip)
136 if (!user.admin? && user.last_ip && user.last_ip != request.remote_ip)
126 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
137 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
127 redirect_to :controller => 'main', :action => 'login'
138 redirect_to :controller => 'main', :action => 'login'
128 return false
139 return false
129 end
140 end
130 unless user.last_ip
141 unless user.last_ip
131 user.last_ip = request.remote_ip
142 user.last_ip = request.remote_ip
132 user.save
143 user.save
133 end
144 end
134 end
145 end
135 return true
146 return true
136 end
147 end
137
148
138 def authorization
149 def authorization
139 return false unless check_valid_login
150 return false unless check_valid_login
140 user = User.find(session[:user_id])
151 user = User.find(session[:user_id])
141 unless user.roles.detect { |role|
152 unless user.roles.detect { |role|
142 role.rights.detect{ |right|
153 role.rights.detect{ |right|
143 right.controller == self.class.controller_name and
154 right.controller == self.class.controller_name and
144 (right.action == 'all' || right.action == action_name)
155 (right.action == 'all' || right.action == action_name)
145 }
156 }
146 }
157 }
147 flash[:notice] = 'You are not authorized to view the page you requested'
158 flash[:notice] = 'You are not authorized to view the page you requested'
148 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
159 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
149 redirect_to :controller => 'main', :action => 'login'
160 redirect_to :controller => 'main', :action => 'login'
150 return false
161 return false
151 end
162 end
152 end
163 end
153
164
154 def verify_time_limit
165 def verify_time_limit
155 return true if session[:user_id]==nil
166 return true if session[:user_id]==nil
156 user = User.find(session[:user_id], :include => :site)
167 user = User.find(session[:user_id], :include => :site)
157 return true if user==nil || user.site == nil
168 return true if user==nil || user.site == nil
158 if user.contest_finished?
169 if user.contest_finished?
159 flash[:notice] = 'Error: the contest you are participating is over.'
170 flash[:notice] = 'Error: the contest you are participating is over.'
160 redirect_to :back
171 redirect_to :back
161 return false
172 return false
162 end
173 end
163 return true
174 return true
164 end
175 end
165
176
166 def is_request_ip_allowed?
177 def is_request_ip_allowed?
167 unless GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
178 unless GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
168 user_ip = IPAddr.new(request.remote_ip)
179 user_ip = IPAddr.new(request.remote_ip)
169 allowed = GraderConfiguration[WHITELIST_IP_CONF_KEY] || ''
180 allowed = GraderConfiguration[WHITELIST_IP_CONF_KEY] || ''
170
181
171 allowed.delete(' ').split(',').each do |ips|
182 allowed.delete(' ').split(',').each do |ips|
172 allow_ips = IPAddr.new(ips)
183 allow_ips = IPAddr.new(ips)
173 if allow_ips.include?(user_ip)
184 if allow_ips.include?(user_ip)
174 return true
185 return true
175 end
186 end
176 end
187 end
177 return false
188 return false
178 end
189 end
179 return true
190 return true
180 end
191 end
181
192
182 #function for datatable ajax query
193 #function for datatable ajax query
183 #return record,total_count,filter_count
194 #return record,total_count,filter_count
184 def process_query_record(record,
195 def process_query_record(record,
185 total_count: nil,
196 total_count: nil,
186 select: '',
197 select: '',
187 global_search: [],
198 global_search: [],
188 no_search: false,
199 no_search: false,
189 force_order: '',
200 force_order: '',
190 date_filter: '', date_param_since: 'date_since',date_param_until: 'date_until',
201 date_filter: '', date_param_since: 'date_since',date_param_until: 'date_until',
191 hard_limit: nil)
202 hard_limit: nil)
192 arel_table = record.model.arel_table
203 arel_table = record.model.arel_table
193
204
194 if !no_search && params['search']
205 if !no_search && params['search']
195 global_value = record.model.sanitize_sql(params['search']['value'].strip.downcase)
206 global_value = record.model.sanitize_sql(params['search']['value'].strip.downcase)
196 if !global_value.blank?
207 if !global_value.blank?
197 global_value.split.each do |value|
208 global_value.split.each do |value|
198 global_where = global_search.map{|f| "LOWER(#{f}) like '%#{value}%'"}.join(' OR ')
209 global_where = global_search.map{|f| "LOWER(#{f}) like '%#{value}%'"}.join(' OR ')
199 record = record.where(global_where)
210 record = record.where(global_where)
200 end
211 end
201 end
212 end
202
213
203 params['columns'].each do |i, col|
214 params['columns'].each do |i, col|
204 if !col['search']['value'].blank?
215 if !col['search']['value'].blank?
205 record = record.where(arel_table[col['name']].lower.matches("%#{col['search']['value'].strip.downcase}%"))
216 record = record.where(arel_table[col['name']].lower.matches("%#{col['search']['value'].strip.downcase}%"))
206 end
217 end
207 end
218 end
208 end
219 end
209
220
210 if !date_filter.blank?
221 if !date_filter.blank?
211 param_since = params[date_param_since]
222 param_since = params[date_param_since]
212 param_until = params[date_param_until]
223 param_until = params[date_param_until]
213 date_since = Time.zone.parse( param_since ) || Time.new(1,1,1) rescue Time.new(1,1,1)
224 date_since = Time.zone.parse( param_since ) || Time.new(1,1,1) rescue Time.new(1,1,1)
214 date_until = Time.zone.parse( param_until ) || Time.zone.now() rescue Time.zone.now()
225 date_until = Time.zone.parse( param_until ) || Time.zone.now() rescue Time.zone.now()
215 date_range = date_since..(date_until.end_of_day)
226 date_range = date_since..(date_until.end_of_day)
216 record = record.where(date_filter.to_sym => date_range)
227 record = record.where(date_filter.to_sym => date_range)
217 end
228 end
218
229
219 if force_order.blank?
230 if force_order.blank?
220 if params['order']
231 if params['order']
221 params['order'].each do |i, o|
232 params['order'].each do |i, o|
222 colName = params['columns'][o['column']]['name']
233 colName = params['columns'][o['column']]['name']
223 colName = "#{record.model.table_name}.#{colName}" if colName.upcase == 'ID'
234 colName = "#{record.model.table_name}.#{colName}" if colName.upcase == 'ID'
224 record = record.order("#{colName} #{o['dir'].casecmp('desc') != 0 ? 'ASC' : 'DESC'}") unless colName.blank?
235 record = record.order("#{colName} #{o['dir'].casecmp('desc') != 0 ? 'ASC' : 'DESC'}") unless colName.blank?
225 end
236 end
226 end
237 end
227 else
238 else
228 record = record.order(force_order)
239 record = record.order(force_order)
229 end
240 end
230
241
231 filterCount = record.count(record.model.primary_key)
242 filterCount = record.count(record.model.primary_key)
232 # if .group() is used, filterCount might be like {id_1: count_1, id_2: count_2, ...}
243 # if .group() is used, filterCount might be like {id_1: count_1, id_2: count_2, ...}
233 # so we should count the result again..
244 # so we should count the result again..
234 if filterCount.is_a? Hash
245 if filterCount.is_a? Hash
235 filterCount = filterCount.count
246 filterCount = filterCount.count
236 end
247 end
237
248
238
249
239 record = record.offset(params['start'] || 0)
250 record = record.offset(params['start'] || 0)
240 record = record.limit(hard_limit)
251 record = record.limit(hard_limit)
241 if (params['length'])
252 if (params['length'])
242 limit = params['length'].to_i
253 limit = params['length'].to_i
243 limit == hard_limit if (hard_limit && hard_limit < limit)
254 limit == hard_limit if (hard_limit && hard_limit < limit)
244 record = record.limit(limit)
255 record = record.limit(limit)
245 end
256 end
246 if (!select.blank?)
257 if (!select.blank?)
247 record = record.select(select)
258 record = record.select(select)
248 end
259 end
249
260
250 return record, total_count || record.model.count, filterCount
261 return record, total_count || record.model.count, filterCount
251 end
262 end
252
263
253 end
264 end
@@ -1,369 +1,369
1 class MainController < ApplicationController
1 class MainController < ApplicationController
2
2
3 before_action :check_valid_login, :except => [:login]
3 before_action :check_valid_login, :except => [:login]
4 before_action :check_viewability, :except => [:index, :login]
4 before_action :check_viewability, :except => [:index, :login]
5
5
6 append_before_action :confirm_and_update_start_time,
6 append_before_action :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_action :reject_announcement_refresh_when_logged_out,
13 prepend_before_action :reject_announcement_refresh_when_logged_out,
14 :only => [:announcements]
14 :only => [:announcements]
15
15
16 before_action :authenticate_by_ip_address, :only => [:list]
16 before_action :authenticate_by_ip_address, :only => [:list]
17
17
18 #reset login, clear session
18 #reset login, clear session
19 #front page
19 #front page
20 def login
20 def login
21 saved_notice = flash[:notice]
21 saved_notice = flash[:notice]
22 reset_session
22 reset_session
23 flash.now[:notice] = saved_notice
23 flash.now[:notice] = saved_notice
24 @remote_ip = request.remote_ip
24 @remote_ip = request.remote_ip
25
25
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
123 end
123 end
124 @user = User.find(session[:user_id])
124 @user = User.find(session[:user_id])
125 @submission = Submission.find(params[:id])
125 @submission = Submission.find(params[:id])
126 if @submission.user!=@user
126 if @submission.user!=@user
127 flash[:notice] = 'You are not allowed to view result of other users.'
127 flash[:notice] = 'You are not allowed to view result of other users.'
128 redirect_to :action => 'list' and return
128 redirect_to :action => 'list' and return
129 end
129 end
130 prepare_grading_result(@submission)
130 prepare_grading_result(@submission)
131 end
131 end
132
132
133 def load_output
133 def load_output
134 if !GraderConfiguration.show_grading_result or params[:num]==nil
134 if !GraderConfiguration.show_grading_result or params[:num]==nil
135 redirect_to :action => 'list' and return
135 redirect_to :action => 'list' and return
136 end
136 end
137 @user = User.find(session[:user_id])
137 @user = User.find(session[:user_id])
138 @submission = Submission.find(params[:id])
138 @submission = Submission.find(params[:id])
139 if @submission.user!=@user
139 if @submission.user!=@user
140 flash[:notice] = 'You are not allowed to view result of other users.'
140 flash[:notice] = 'You are not allowed to view result of other users.'
141 redirect_to :action => 'list' and return
141 redirect_to :action => 'list' and return
142 end
142 end
143 case_num = params[:num].to_i
143 case_num = params[:num].to_i
144 out_filename = output_filename(@user.login,
144 out_filename = output_filename(@user.login,
145 @submission.problem.name,
145 @submission.problem.name,
146 @submission.id,
146 @submission.id,
147 case_num)
147 case_num)
148 if !FileTest.exists?(out_filename)
148 if !FileTest.exists?(out_filename)
149 flash[:notice] = 'Output not found.'
149 flash[:notice] = 'Output not found.'
150 redirect_to :action => 'list' and return
150 redirect_to :action => 'list' and return
151 end
151 end
152
152
153 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
153 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
154 response.headers['Content-Type'] = "application/force-download"
154 response.headers['Content-Type'] = "application/force-download"
155 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
155 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
156 response.headers["X-Sendfile"] = out_filename
156 response.headers["X-Sendfile"] = out_filename
157 response.headers['Content-length'] = File.size(out_filename)
157 response.headers['Content-length'] = File.size(out_filename)
158 render :nothing => true
158 render :nothing => true
159 else
159 else
160 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
160 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
161 end
161 end
162 end
162 end
163
163
164 def error
164 def error
165 @user = User.find(session[:user_id])
165 @user = User.find(session[:user_id])
166 end
166 end
167
167
168 # announcement refreshing and hiding methods
168 # announcement refreshing and hiding methods
169
169
170 def announcements
170 def announcements
171 if params.has_key? 'recent'
171 if params.has_key? 'recent'
172 prepare_announcements(params[:recent])
172 prepare_announcements(params[:recent])
173 else
173 else
174 prepare_announcements
174 prepare_announcements
175 end
175 end
176 render(:partial => 'announcement',
176 render(:partial => 'announcement',
177 :collection => @announcements,
177 :collection => @announcements,
178 :locals => {:announcement_effect => true})
178 :locals => {:announcement_effect => true})
179 end
179 end
180
180
181 def confirm_contest_start
181 def confirm_contest_start
182 user = User.find(session[:user_id])
182 user = User.find(session[:user_id])
183 if request.method == 'POST'
183 if request.method == 'POST'
184 user.update_start_time
184 user.update_start_time
185 redirect_to :action => 'list'
185 redirect_to :action => 'list'
186 else
186 else
187 @contests = user.contests
187 @contests = user.contests
188 @user = user
188 @user = user
189 end
189 end
190 end
190 end
191
191
192 protected
192 protected
193
193
194 def prepare_announcements(recent=nil)
194 def prepare_announcements(recent=nil)
195 if GraderConfiguration.show_tasks_to?(@user)
195 if GraderConfiguration.show_tasks_to?(@user)
196 @announcements = Announcement.published(true)
196 @announcements = Announcement.published(true)
197 else
197 else
198 @announcements = Announcement.published
198 @announcements = Announcement.published
199 end
199 end
200 if recent!=nil
200 if recent!=nil
201 recent_id = recent.to_i
201 recent_id = recent.to_i
202 @announcements = @announcements.find_all { |a| a.id > recent_id }
202 @announcements = @announcements.find_all { |a| a.id > recent_id }
203 end
203 end
204 end
204 end
205
205
206 def prepare_list_information
206 def prepare_list_information
207 @user = User.find(session[:user_id])
207 @user = User.find(session[:user_id])
208 if not GraderConfiguration.multicontests?
208 if not GraderConfiguration.multicontests?
209 @problems = @user.available_problems
209 @problems = @user.available_problems
210 else
210 else
211 @contest_problems = @user.available_problems_group_by_contests
211 @contest_problems = @user.available_problems_group_by_contests
212 @problems = @user.available_problems
212 @problems = @user.available_problems
213 end
213 end
214 @prob_submissions = {}
214 @prob_submissions = {}
215 @problems.each do |p|
215 @problems.each do |p|
216 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
216 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
217 if sub!=nil
217 if sub!=nil
218 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
218 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
219 else
219 else
220 @prob_submissions[p.id] = { :count => 0, :submission => nil }
220 @prob_submissions[p.id] = { :count => 0, :submission => nil }
221 end
221 end
222 end
222 end
223 prepare_announcements
223 prepare_announcements
224 end
224 end
225
225
226 def check_viewability
226 def check_viewability
227 @user = User.find(session[:user_id])
227 @user = User.find(session[:user_id])
228 if (!GraderConfiguration.show_tasks_to?(@user)) and
228 if (!GraderConfiguration.show_tasks_to?(@user)) and
229 ((action_name=='submission') or (action_name=='submit'))
229 ((action_name=='submission') or (action_name=='submit'))
230 redirect_to :action => 'list' and return
230 redirect_to :action => 'list' and return
231 end
231 end
232 end
232 end
233
233
234 def prepare_grading_result(submission)
234 def prepare_grading_result(submission)
235 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
235 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
236 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
236 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
237 else
237 else
238 # guess task info from problem.full_score
238 # guess task info from problem.full_score
239 cases = submission.problem.full_score / 10
239 cases = submission.problem.full_score / 10
240 grading_info = {
240 grading_info = {
241 'testruns' => cases,
241 'testruns' => cases,
242 'testcases' => cases
242 'testcases' => cases
243 }
243 }
244 end
244 end
245 @test_runs = []
245 @test_runs = []
246 if grading_info['testruns'].is_a? Integer
246 if grading_info['testruns'].is_a? Integer
247 trun_count = grading_info['testruns']
247 trun_count = grading_info['testruns']
248 trun_count.times do |i|
248 trun_count.times do |i|
249 @test_runs << [ read_grading_result(@user.login,
249 @test_runs << [ read_grading_result(@user.login,
250 submission.problem.name,
250 submission.problem.name,
251 submission.id,
251 submission.id,
252 i+1) ]
252 i+1) ]
253 end
253 end
254 else
254 else
255 grading_info['testruns'].keys.sort.each do |num|
255 grading_info['testruns'].keys.sort.each do |num|
256 run = []
256 run = []
257 testrun = grading_info['testruns'][num]
257 testrun = grading_info['testruns'][num]
258 testrun.each do |c|
258 testrun.each do |c|
259 run << read_grading_result(@user.login,
259 run << read_grading_result(@user.login,
260 submission.problem.name,
260 submission.problem.name,
261 submission.id,
261 submission.id,
262 c)
262 c)
263 end
263 end
264 @test_runs << run
264 @test_runs << run
265 end
265 end
266 end
266 end
267 end
267 end
268
268
269 def grading_result_dir(user_name, problem_name, submission_id, case_num)
269 def grading_result_dir(user_name, problem_name, submission_id, case_num)
270 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
270 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
271 end
271 end
272
272
273 def output_filename(user_name, problem_name, submission_id, case_num)
273 def output_filename(user_name, problem_name, submission_id, case_num)
274 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
274 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
275 return "#{dir}/output.txt"
275 return "#{dir}/output.txt"
276 end
276 end
277
277
278 def read_grading_result(user_name, problem_name, submission_id, case_num)
278 def read_grading_result(user_name, problem_name, submission_id, case_num)
279 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
279 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
280 result_file_name = "#{dir}/result"
280 result_file_name = "#{dir}/result"
281 if !FileTest.exists?(result_file_name)
281 if !FileTest.exists?(result_file_name)
282 return {:num => case_num, :msg => 'program did not run'}
282 return {:num => case_num, :msg => 'program did not run'}
283 else
283 else
284 results = File.open(result_file_name).readlines
284 results = File.open(result_file_name).readlines
285 run_stat = extract_running_stat(results)
285 run_stat = extract_running_stat(results)
286 output_filename = "#{dir}/output.txt"
286 output_filename = "#{dir}/output.txt"
287 if FileTest.exists?(output_filename)
287 if FileTest.exists?(output_filename)
288 output_file = true
288 output_file = true
289 output_size = File.size(output_filename)
289 output_size = File.size(output_filename)
290 else
290 else
291 output_file = false
291 output_file = false
292 output_size = 0
292 output_size = 0
293 end
293 end
294
294
295 return {
295 return {
296 :num => case_num,
296 :num => case_num,
297 :msg => results[0],
297 :msg => results[0],
298 :run_stat => run_stat,
298 :run_stat => run_stat,
299 :output => output_file,
299 :output => output_file,
300 :output_size => output_size
300 :output_size => output_size
301 }
301 }
302 end
302 end
303 end
303 end
304
304
305 # copied from grader/script/lib/test_request_helper.rb
305 # copied from grader/script/lib/test_request_helper.rb
306 def extract_running_stat(results)
306 def extract_running_stat(results)
307 running_stat_line = results[-1]
307 running_stat_line = results[-1]
308
308
309 # extract exit status line
309 # extract exit status line
310 run_stat = ""
310 run_stat = ""
311 if !(/[Cc]orrect/.match(results[0]))
311 if !(/[Cc]orrect/.match(results[0]))
312 run_stat = results[0].chomp
312 run_stat = results[0].chomp
313 else
313 else
314 run_stat = 'Program exited normally'
314 run_stat = 'Program exited normally'
315 end
315 end
316
316
317 logger.info "Stat line: #{running_stat_line}"
317 logger.info "Stat line: #{running_stat_line}"
318
318
319 # extract running time
319 # extract running time
320 if res = /r(.*)u(.*)s/.match(running_stat_line)
320 if res = /r(.*)u(.*)s/.match(running_stat_line)
321 seconds = (res[1].to_f + res[2].to_f)
321 seconds = (res[1].to_f + res[2].to_f)
322 time_stat = "Time used: #{seconds} sec."
322 time_stat = "Time used: #{seconds} sec."
323 else
323 else
324 seconds = nil
324 seconds = nil
325 time_stat = "Time used: n/a sec."
325 time_stat = "Time used: n/a sec."
326 end
326 end
327
327
328 # extract memory usage
328 # extract memory usage
329 if res = /s(.*)m/.match(running_stat_line)
329 if res = /s(.*)m/.match(running_stat_line)
330 memory_used = res[1].to_i
330 memory_used = res[1].to_i
331 else
331 else
332 memory_used = -1
332 memory_used = -1
333 end
333 end
334
334
335 return {
335 return {
336 :msg => "#{run_stat}\n#{time_stat}",
336 :msg => "#{run_stat}\n#{time_stat}",
337 :running_time => seconds,
337 :running_time => seconds,
338 :exit_status => run_stat,
338 :exit_status => run_stat,
339 :memory_usage => memory_used
339 :memory_usage => memory_used
340 }
340 }
341 end
341 end
342
342
343 def confirm_and_update_start_time
343 def confirm_and_update_start_time
344 user = User.find(session[:user_id])
344 user = User.find(session[:user_id])
345 if (GraderConfiguration.indv_contest_mode? and
345 if (GraderConfiguration.indv_contest_mode? and
346 GraderConfiguration['contest.confirm_indv_contest_start'] and
346 GraderConfiguration['contest.confirm_indv_contest_start'] and
347 !user.contest_started?)
347 !user.contest_started?)
348 redirect_to :action => 'confirm_contest_start' and return
348 redirect_to :action => 'confirm_contest_start' and return
349 end
349 end
350 if not GraderConfiguration.analysis_mode?
350 if not GraderConfiguration.analysis_mode?
351 user.update_start_time
351 user.update_start_time
352 end
352 end
353 end
353 end
354
354
355 def reject_announcement_refresh_when_logged_out
355 def reject_announcement_refresh_when_logged_out
356 if not session[:user_id]
356 if not session[:user_id]
357 render :text => 'Access forbidden', :status => 403
357 render :text => 'Access forbidden', :status => 403
358 end
358 end
359
359
360 if GraderConfiguration.multicontests?
360 if GraderConfiguration.multicontests?
361 user = User.find(session[:user_id])
361 user = User.find(session[:user_id])
362 if user.contest_stat.forced_logout
362 if user.contest_stat.forced_logout
363 render :text => 'Access forbidden', :status => 403
363 render :text => 'Access forbidden', :status => 403
364 end
364 end
365 end
365 end
366 end
366 end
367
367
368 end
368 end
369
369
@@ -1,566 +1,575
1 require 'csv'
1 require 'csv'
2
2
3 class ReportController < ApplicationController
3 class ReportController < ApplicationController
4
4
5 before_action :check_valid_login
5 before_action :check_valid_login
6
6
7 before_action :admin_authorization, only: [:login_stat,:submission, :submission_query,
7 before_action :admin_authorization, only: [:login_stat,:submission, :submission_query,
8 :login, :login_detail_query, :login_summary_query,
8 :login, :login_detail_query, :login_summary_query,
9 :stuck, :cheat_report, :cheat_scrutinize, :show_max_score, :current_score]
9 :stuck, :cheat_report, :cheat_scrutinize, :show_max_score, :current_score]
10
10
11 before_action(only: [:problem_hof]) { |c|
11 before_action(only: [:problem_hof]) { |c|
12 return false unless check_valid_login
12 return false unless check_valid_login
13
13
14 admin_authorization unless GraderConfiguration["right.user_view_submission"]
14 admin_authorization unless GraderConfiguration["right.user_view_submission"]
15 }
15 }
16
16
17 def max_score
17 def max_score
18 end
18 end
19
19
20 def current_score
20 def current_score
21 @problems = Problem.available_problems
21 @problems = Problem.available_problems
22 if params[:group_id] && params[:users] == 'group'
22 if params[:group_id] && params[:users] == 'group'
23 @group = Group.find(params[:group_id])
23 @group = Group.find(params[:group_id])
24 @users = @group.users.where(enabled: true)
24 @users = @group.users.where(enabled: true)
25 else
25 else
26 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
26 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
27 end
27 end
28 @scorearray = calculate_max_score(@problems, @users,0,0,true)
28 @scorearray = calculate_max_score(@problems, @users,0,0,true)
29
29
30 #rencer accordingly
30 #rencer accordingly
31 if params[:button] == 'download' then
31 if params[:button] == 'download' then
32 csv = gen_csv_from_scorearray(@scorearray,@problems)
32 csv = gen_csv_from_scorearray(@scorearray,@problems)
33 send_data csv, filename: 'max_score.csv'
33 send_data csv, filename: 'max_score.csv'
34 else
34 else
35 #render template: 'user_admin/user_stat'
35 #render template: 'user_admin/user_stat'
36 render 'current_score'
36 render 'current_score'
37 end
37 end
38 end
38 end
39
39
40 def show_max_score
40 def show_max_score
41 #process parameters
41 #process parameters
42 #problems
42 #problems
43 @problems = []
43 @problems = []
44 if params[:problem_id]
44 if params[:problem_id]
45 params[:problem_id].each do |id|
45 params[:problem_id].each do |id|
46 next unless id.strip != ""
46 next unless id.strip != ""
47 pid = Problem.find_by_id(id.to_i)
47 pid = Problem.find_by_id(id.to_i)
48 @problems << pid if pid
48 @problems << pid if pid
49 end
49 end
50 end
50 end
51
51
52 #users
52 #users
53 @users = if params[:users] == "group" then
53 @users = if params[:users] == "group" then
54 Group.find(params[:group_id]).users.all
54 Group.find(params[:group_id]).users.all
55 elsif params[:users] == 'enabled'
55 elsif params[:users] == 'enabled'
56 User.includes(:contests).includes(:contest_stat).where(enabled: true)
56 User.includes(:contests).includes(:contest_stat).where(enabled: true)
57 else
57 else
58 User.includes(:contests).includes(:contest_stat)
58 User.includes(:contests).includes(:contest_stat)
59 end
59 end
60
60
61 #set up range from param
61 #set up range from param
62 @since_id = params.fetch(:from_id, 0).to_i
62 @since_id = params.fetch(:from_id, 0).to_i
63 @until_id = params.fetch(:to_id, 0).to_i
63 @until_id = params.fetch(:to_id, 0).to_i
64 @since_id = nil if @since_id == 0
64 @since_id = nil if @since_id == 0
65 @until_id = nil if @until_id == 0
65 @until_id = nil if @until_id == 0
66
66
67 #calculate the routine
67 #calculate the routine
68 @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
68 @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
69
69
70 #rencer accordingly
70 #rencer accordingly
71 if params[:button] == 'download' then
71 if params[:button] == 'download' then
72 csv = gen_csv_from_scorearray(@scorearray,@problems)
72 csv = gen_csv_from_scorearray(@scorearray,@problems)
73 send_data csv, filename: 'max_score.csv'
73 send_data csv, filename: 'max_score.csv'
74 else
74 else
75 #render template: 'user_admin/user_stat'
75 #render template: 'user_admin/user_stat'
76 render 'max_score'
76 render 'max_score'
77 end
77 end
78
78
79 end
79 end
80
80
81 def score
81 def score
82 if params[:commit] == 'download csv'
82 if params[:commit] == 'download csv'
83 @problems = Problem.all
83 @problems = Problem.all
84 else
84 else
85 @problems = Problem.available_problems
85 @problems = Problem.available_problems
86 end
86 end
87 @users = User.includes(:contests, :contest_stat).where(enabled: true)
87 @users = User.includes(:contests, :contest_stat).where(enabled: true)
88 @scorearray = Array.new
88 @scorearray = Array.new
89 @users.each do |u|
89 @users.each do |u|
90 ustat = Array.new
90 ustat = Array.new
91 ustat[0] = u
91 ustat[0] = u
92 @problems.each do |p|
92 @problems.each do |p|
93 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
93 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
94 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
94 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
95 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
95 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
96 else
96 else
97 ustat << [0,false]
97 ustat << [0,false]
98 end
98 end
99 end
99 end
100 @scorearray << ustat
100 @scorearray << ustat
101 end
101 end
102 if params[:commit] == 'download csv' then
102 if params[:commit] == 'download csv' then
103 csv = gen_csv_from_scorearray(@scorearray,@problems)
103 csv = gen_csv_from_scorearray(@scorearray,@problems)
104 send_data csv, filename: 'last_score.csv'
104 send_data csv, filename: 'last_score.csv'
105 else
105 else
106 render template: 'user_admin/user_stat'
106 render template: 'user_admin/user_stat'
107 end
107 end
108
108
109 end
109 end
110
110
111 def login
111 def login
112 end
112 end
113
113
114 def login_summary_query
114 def login_summary_query
115 @users = Array.new
115 @users = Array.new
116
116
117 date_and_time = '%Y-%m-%d %H:%M'
117 date_and_time = '%Y-%m-%d %H:%M'
118 begin
118 begin
119 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
119 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
120 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
120 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
121 rescue
121 rescue
122 @since_time = DateTime.new(1000,1,1)
122 @since_time = DateTime.new(1000,1,1)
123 end
123 end
124 begin
124 begin
125 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
125 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
126 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
126 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
127 rescue
127 rescue
128 @until_time = DateTime.new(3000,1,1)
128 @until_time = DateTime.new(3000,1,1)
129 end
129 end
130
130
131 record = User
131 record = User
132 .left_outer_joins(:logins).group('users.id')
132 .left_outer_joins(:logins).group('users.id')
133 .where("logins.created_at >= ? AND logins.created_at <= ?",@since_time, @until_time)
133 .where("logins.created_at >= ? AND logins.created_at <= ?",@since_time, @until_time)
134 case params[:users]
134 case params[:users]
135 when 'enabled'
135 when 'enabled'
136 record = record.where(enabled: true)
136 record = record.where(enabled: true)
137 when 'group'
137 when 'group'
138 record = record.joins(:groups).where(groups: {id: params[:groups]}) if params[:groups]
138 record = record.joins(:groups).where(groups: {id: params[:groups]}) if params[:groups]
139 end
139 end
140
140
141 record = record.pluck("users.id,users.login,users.full_name,count(logins.created_at),min(logins.created_at),max(logins.created_at)")
141 record = record.pluck("users.id,users.login,users.full_name,count(logins.created_at),min(logins.created_at),max(logins.created_at)")
142 record.each do |user|
142 record.each do |user|
143 x = Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
143 x = Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
144 user[0],@since_time,@until_time)
144 user[0],@since_time,@until_time)
145 .pluck(:ip_address).uniq
145 .pluck(:ip_address).uniq
146 @users << { id: user[0],
146 @users << { id: user[0],
147 login: user[1],
147 login: user[1],
148 full_name: user[2],
148 full_name: user[2],
149 count: user[3],
149 count: user[3],
150 min: user[4],
150 min: user[4],
151 max: user[5],
151 max: user[5],
152 ip: x
152 ip: x
153 }
153 }
154 end
154 end
155 end
155 end
156
156
157 def login_detail_query
157 def login_detail_query
158 @logins = Array.new
158 @logins = Array.new
159
159
160 date_and_time = '%Y-%m-%d %H:%M'
160 date_and_time = '%Y-%m-%d %H:%M'
161 begin
161 begin
162 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
162 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
163 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
163 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
164 rescue
164 rescue
165 @since_time = DateTime.new(1000,1,1)
165 @since_time = DateTime.new(1000,1,1)
166 end
166 end
167 begin
167 begin
168 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
168 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
169 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
169 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
170 rescue
170 rescue
171 @until_time = DateTime.new(3000,1,1)
171 @until_time = DateTime.new(3000,1,1)
172 end
172 end
173
173
174 @logins = Login.includes(:user).where("logins.created_at >= ? AND logins.created_at <= ?",@since_time, @until_time)
174 @logins = Login.includes(:user).where("logins.created_at >= ? AND logins.created_at <= ?",@since_time, @until_time)
175 case params[:users]
175 case params[:users]
176 when 'enabled'
176 when 'enabled'
177 @logins = @logins.where(users: {enabled: true})
177 @logins = @logins.where(users: {enabled: true})
178 when 'group'
178 when 'group'
179 @logins = @logins.joins(user: :groups).where(user: {groups: {id: params[:groups]}}) if params[:groups]
179 @logins = @logins.joins(user: :groups).where(user: {groups: {id: params[:groups]}}) if params[:groups]
180 end
180 end
181 end
181 end
182
182
183 def submission
183 def submission
184 end
184 end
185
185
186 def submission_query
186 def submission_query
187 @submissions = Submission
187 @submissions = Submission
188 .includes(:problem).includes(:user).includes(:language)
188 .includes(:problem).includes(:user).includes(:language)
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
365 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
374 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
366 last,count = 0,0
375 last,count = 0,0
367 first = 0
376 first = 0
368 @ip = []
377 @ip = []
369 raw.each do |r|
378 raw.each do |r|
370 if last != r.ip_address
379 if last != r.ip_address
371 count = 1
380 count = 1
372 last = r.ip_address
381 last = r.ip_address
373 first = r
382 first = r
374 else
383 else
375 @ip << first if count == 1
384 @ip << first if count == 1
376 @ip << r
385 @ip << r
377 count += 1
386 count += 1
378 end
387 end
379 end
388 end
380 end
389 end
381
390
382 def cheat_report
391 def cheat_report
383 date_and_time = '%Y-%m-%d %H:%M'
392 date_and_time = '%Y-%m-%d %H:%M'
384 begin
393 begin
385 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
394 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
386 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
395 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
387 rescue
396 rescue
388 @since_time = Time.zone.now.ago( 90.minutes)
397 @since_time = Time.zone.now.ago( 90.minutes)
389 end
398 end
390 begin
399 begin
391 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
400 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
392 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
401 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
393 rescue
402 rescue
394 @until_time = Time.zone.now
403 @until_time = Time.zone.now
395 end
404 end
396
405
397 #multi login
406 #multi login
398 @ml = Login.joins(:user).where("logins.created_at >= ? and logins.created_at <= ?",@since_time,@until_time).select('users.login,count(distinct ip_address) as count,users.full_name').group("users.id").having("count > 1")
407 @ml = Login.joins(:user).where("logins.created_at >= ? and logins.created_at <= ?",@since_time,@until_time).select('users.login,count(distinct ip_address) as count,users.full_name').group("users.id").having("count > 1")
399
408
400 st = <<-SQL
409 st = <<-SQL
401 SELECT l2.*
410 SELECT l2.*
402 FROM logins l2 INNER JOIN
411 FROM logins l2 INNER JOIN
403 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
412 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
404 FROM logins l
413 FROM logins l
405 INNER JOIN users u ON l.user_id = u.id
414 INNER JOIN users u ON l.user_id = u.id
406 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
415 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
407 GROUP BY u.id
416 GROUP BY u.id
408 HAVING count > 1
417 HAVING count > 1
409 ) ml ON l2.user_id = ml.id
418 ) ml ON l2.user_id = ml.id
410 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
419 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
411 UNION
420 UNION
412 SELECT l2.*
421 SELECT l2.*
413 FROM logins l2 INNER JOIN
422 FROM logins l2 INNER JOIN
414 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
423 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
415 FROM logins l
424 FROM logins l
416 INNER JOIN users u ON l.user_id = u.id
425 INNER JOIN users u ON l.user_id = u.id
417 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
426 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
418 GROUP BY l.ip_address
427 GROUP BY l.ip_address
419 HAVING count > 1
428 HAVING count > 1
420 ) ml on ml.ip_address = l2.ip_address
429 ) ml on ml.ip_address = l2.ip_address
421 INNER JOIN users u ON l2.user_id = u.id
430 INNER JOIN users u ON l2.user_id = u.id
422 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
431 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
423 ORDER BY ip_address,created_at
432 ORDER BY ip_address,created_at
424 SQL
433 SQL
425 @mld = Login.find_by_sql(st)
434 @mld = Login.find_by_sql(st)
426
435
427 st = <<-SQL
436 st = <<-SQL
428 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
437 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
429 FROM submissions s INNER JOIN
438 FROM submissions s INNER JOIN
430 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
439 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
431 FROM logins l
440 FROM logins l
432 INNER JOIN users u ON l.user_id = u.id
441 INNER JOIN users u ON l.user_id = u.id
433 WHERE l.created_at >= ? and l.created_at <= ?
442 WHERE l.created_at >= ? and l.created_at <= ?
434 GROUP BY u.id
443 GROUP BY u.id
435 HAVING count > 1
444 HAVING count > 1
436 ) ml ON s.user_id = ml.id
445 ) ml ON s.user_id = ml.id
437 WHERE s.submitted_at >= ? and s.submitted_at <= ?
446 WHERE s.submitted_at >= ? and s.submitted_at <= ?
438 UNION
447 UNION
439 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
448 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
440 FROM submissions s INNER JOIN
449 FROM submissions s INNER JOIN
441 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
450 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
442 FROM logins l
451 FROM logins l
443 INNER JOIN users u ON l.user_id = u.id
452 INNER JOIN users u ON l.user_id = u.id
444 WHERE l.created_at >= ? and l.created_at <= ?
453 WHERE l.created_at >= ? and l.created_at <= ?
445 GROUP BY l.ip_address
454 GROUP BY l.ip_address
446 HAVING count > 1
455 HAVING count > 1
447 ) ml on ml.ip_address = s.ip_address
456 ) ml on ml.ip_address = s.ip_address
448 WHERE s.submitted_at >= ? and s.submitted_at <= ?
457 WHERE s.submitted_at >= ? and s.submitted_at <= ?
449 ORDER BY ip_address,submitted_at
458 ORDER BY ip_address,submitted_at
450 SQL
459 SQL
451 @subs = Submission.joins(:problem).find_by_sql([st,@since_time,@until_time,
460 @subs = Submission.joins(:problem).find_by_sql([st,@since_time,@until_time,
452 @since_time,@until_time,
461 @since_time,@until_time,
453 @since_time,@until_time,
462 @since_time,@until_time,
454 @since_time,@until_time])
463 @since_time,@until_time])
455
464
456 end
465 end
457
466
458 def cheat_scrutinize
467 def cheat_scrutinize
459 #convert date & time
468 #convert date & time
460 date_and_time = '%Y-%m-%d %H:%M'
469 date_and_time = '%Y-%m-%d %H:%M'
461 begin
470 begin
462 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
471 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
463 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
472 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
464 rescue
473 rescue
465 @since_time = Time.zone.now.ago( 90.minutes)
474 @since_time = Time.zone.now.ago( 90.minutes)
466 end
475 end
467 begin
476 begin
468 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
477 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
469 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
478 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
470 rescue
479 rescue
471 @until_time = Time.zone.now
480 @until_time = Time.zone.now
472 end
481 end
473
482
474 #convert sid
483 #convert sid
475 @sid = params[:SID].split(/[,\s]/) if params[:SID]
484 @sid = params[:SID].split(/[,\s]/) if params[:SID]
476 unless @sid and @sid.size > 0
485 unless @sid and @sid.size > 0
477 return
486 return
478 redirect_to actoin: :cheat_scrutinize
487 redirect_to actoin: :cheat_scrutinize
479 flash[:notice] = 'Please enter at least 1 student id'
488 flash[:notice] = 'Please enter at least 1 student id'
480 end
489 end
481 mark = Array.new(@sid.size,'?')
490 mark = Array.new(@sid.size,'?')
482 condition = "(u.login = " + mark.join(' OR u.login = ') + ')'
491 condition = "(u.login = " + mark.join(' OR u.login = ') + ')'
483
492
484 @st = <<-SQL
493 @st = <<-SQL
485 SELECT l.created_at as submitted_at ,-1 as id,u.login,u.full_name,l.ip_address,"" as problem_id,"" as points,l.user_id
494 SELECT l.created_at as submitted_at ,-1 as id,u.login,u.full_name,l.ip_address,"" as problem_id,"" as points,l.user_id
486 FROM logins l INNER JOIN users u on l.user_id = u.id
495 FROM logins l INNER JOIN users u on l.user_id = u.id
487 WHERE l.created_at >= ? AND l.created_at <= ? AND #{condition}
496 WHERE l.created_at >= ? AND l.created_at <= ? AND #{condition}
488 UNION
497 UNION
489 SELECT s.submitted_at,s.id,u.login,u.full_name,s.ip_address,s.problem_id,s.points,s.user_id
498 SELECT s.submitted_at,s.id,u.login,u.full_name,s.ip_address,s.problem_id,s.points,s.user_id
490 FROM submissions s INNER JOIN users u ON s.user_id = u.id
499 FROM submissions s INNER JOIN users u ON s.user_id = u.id
491 WHERE s.submitted_at >= ? AND s.submitted_at <= ? AND #{condition}
500 WHERE s.submitted_at >= ? AND s.submitted_at <= ? AND #{condition}
492 ORDER BY submitted_at
501 ORDER BY submitted_at
493 SQL
502 SQL
494
503
495 p = [@st,@since_time,@until_time] + @sid + [@since_time,@until_time] + @sid
504 p = [@st,@since_time,@until_time] + @sid + [@since_time,@until_time] + @sid
496 @logs = Submission.joins(:problem).find_by_sql(p)
505 @logs = Submission.joins(:problem).find_by_sql(p)
497
506
498
507
499
508
500
509
501
510
502 end
511 end
503
512
504 protected
513 protected
505
514
506 def calculate_max_score(problems, users,since_id,until_id, get_last_score = false)
515 def calculate_max_score(problems, users,since_id,until_id, get_last_score = false)
507 #scorearray[i] = user #i's user stat where i is the index (not id)
516 #scorearray[i] = user #i's user stat where i is the index (not id)
508 scorearray = Array.new
517 scorearray = Array.new
509 users.each do |u|
518 users.each do |u|
510 ustat = Array.new
519 ustat = Array.new
511 ustat[0] = u
520 ustat[0] = u
512 problems.each do |p|
521 problems.each do |p|
513 unless get_last_score
522 unless get_last_score
514 #get max score
523 #get max score
515 max_points = 0
524 max_points = 0
516 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
525 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
517 max_points = sub.points if sub and sub.points and (sub.points > max_points)
526 max_points = sub.points if sub and sub.points and (sub.points > max_points)
518 end
527 end
519 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
528 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
520 else
529 else
521 #get latest score
530 #get latest score
522 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
531 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
523 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
532 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
524 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
533 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
525 else
534 else
526 ustat << [0,false]
535 ustat << [0,false]
527 end
536 end
528 end
537 end
529 end
538 end
530 scorearray << ustat
539 scorearray << ustat
531 end
540 end
532 return scorearray
541 return scorearray
533 end
542 end
534
543
535 def gen_csv_from_scorearray(scorearray,problem)
544 def gen_csv_from_scorearray(scorearray,problem)
536 CSV.generate do |csv|
545 CSV.generate do |csv|
537 #add header
546 #add header
538 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
547 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
539 problem.each { |p| header << p.name }
548 problem.each { |p| header << p.name }
540 header += ['Total','Passed']
549 header += ['Total','Passed']
541 csv << header
550 csv << header
542 #add data
551 #add data
543 scorearray.each do |sc|
552 scorearray.each do |sc|
544 total = num_passed = 0
553 total = num_passed = 0
545 row = Array.new
554 row = Array.new
546 sc.each_index do |i|
555 sc.each_index do |i|
547 if i == 0
556 if i == 0
548 row << sc[i].login
557 row << sc[i].login
549 row << sc[i].full_name
558 row << sc[i].full_name
550 row << sc[i].activated
559 row << sc[i].activated
551 row << (sc[i].try(:contest_stat).try(:started_at)!=nil ? 'yes' : 'no')
560 row << (sc[i].try(:contest_stat).try(:started_at)!=nil ? 'yes' : 'no')
552 row << sc[i].contests.collect {|c| c.name}.join(', ')
561 row << sc[i].contests.collect {|c| c.name}.join(', ')
553 else
562 else
554 row << sc[i][0]
563 row << sc[i][0]
555 total += sc[i][0]
564 total += sc[i][0]
556 num_passed += 1 if sc[i][1]
565 num_passed += 1 if sc[i][1]
557 end
566 end
558 end
567 end
559 row << total
568 row << total
560 row << num_passed
569 row << num_passed
561 csv << row
570 csv << row
562 end
571 end
563 end
572 end
564 end
573 end
565
574
566 end
575 end
@@ -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
109
110 + def set_submission
111 + @submission = Submission.find(params[:id])
112 + end
113 +
110
114
111 end
115 end
@@ -1,146 +1,168
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)
75 result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
97 result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
76 return result
98 return result
77 end
99 end
78
100
79 def long_name
101 def long_name
80 "[#{name}] #{full_name}"
102 "[#{name}] #{full_name}"
81 end
103 end
82
104
83 protected
105 protected
84
106
85 def self.to_i_or_default(st, default)
107 def self.to_i_or_default(st, default)
86 if st!=''
108 if st!=''
87 result = st.to_i
109 result = st.to_i
88 end
110 end
89 result ||= default
111 result ||= default
90 end
112 end
91
113
92 def self.to_f_or_default(st, default)
114 def self.to_f_or_default(st, default)
93 if st!=''
115 if st!=''
94 result = st.to_f
116 result = st.to_f
95 end
117 end
96 result ||= default
118 result ||= default
97 end
119 end
98
120
99 def self.extract_params_and_check(params, problem)
121 def self.extract_params_and_check(params, problem)
100 time_limit = Problem.to_f_or_default(params[:time_limit],
122 time_limit = Problem.to_f_or_default(params[:time_limit],
101 DEFAULT_TIME_LIMIT)
123 DEFAULT_TIME_LIMIT)
102 memory_limit = Problem.to_i_or_default(params[:memory_limit],
124 memory_limit = Problem.to_i_or_default(params[:memory_limit],
103 DEFAULT_MEMORY_LIMIT)
125 DEFAULT_MEMORY_LIMIT)
104
126
105 if time_limit<=0 or time_limit >60
127 if time_limit<=0 or time_limit >60
106 problem.errors.add(:base,'Time limit out of range.')
128 problem.errors.add(:base,'Time limit out of range.')
107 end
129 end
108
130
109 if memory_limit==0 and params[:memory_limit]!='0'
131 if memory_limit==0 and params[:memory_limit]!='0'
110 problem.errors.add(:base,'Memory limit format errors.')
132 problem.errors.add(:base,'Memory limit format errors.')
111 elsif memory_limit<=0 or memory_limit >512
133 elsif memory_limit<=0 or memory_limit >512
112 problem.errors.add(:base,'Memory limit out of range.')
134 problem.errors.add(:base,'Memory limit out of range.')
113 end
135 end
114
136
115 if params[:file]==nil or params[:file]==''
137 if params[:file]==nil or params[:file]==''
116 problem.errors.add(:base,'No testdata file.')
138 problem.errors.add(:base,'No testdata file.')
117 end
139 end
118
140
119 checker_name = 'text'
141 checker_name = 'text'
120 if ['text','float'].include? params[:checker]
142 if ['text','float'].include? params[:checker]
121 checker_name = params[:checker]
143 checker_name = params[:checker]
122 end
144 end
123
145
124 file = params[:file]
146 file = params[:file]
125
147
126 if !problem.errors.empty?
148 if !problem.errors.empty?
127 return nil, problem
149 return nil, problem
128 end
150 end
129
151
130 problem.name = params[:name]
152 problem.name = params[:name]
131 if params[:full_name]!=''
153 if params[:full_name]!=''
132 problem.full_name = params[:full_name]
154 problem.full_name = params[:full_name]
133 else
155 else
134 problem.full_name = params[:name]
156 problem.full_name = params[:name]
135 end
157 end
136
158
137 return [{
159 return [{
138 :time_limit => time_limit,
160 :time_limit => time_limit,
139 :memory_limit => memory_limit,
161 :memory_limit => memory_limit,
140 :file => file,
162 :file => file,
141 :checker_name => checker_name
163 :checker_name => checker_name
142 },
164 },
143 problem]
165 problem]
144 end
166 end
145
167
146 end
168 end
@@ -1,168 +1,170
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,
132 self.source_filename)
134 self.source_filename)
133 end
135 end
134 end
136 end
135
137
136 # validation codes
138 # validation codes
137 def must_specify_language
139 def must_specify_language
138 return if self.source==nil
140 return if self.source==nil
139
141
140 # for output_only tasks
142 # for output_only tasks
141 return if self.problem!=nil and self.problem.output_only
143 return if self.problem!=nil and self.problem.output_only
142
144
143 if self.language == nil
145 if self.language == nil
144 errors.add('source',"Cannot detect language. Did you submit a correct source file?")
146 errors.add('source',"Cannot detect language. Did you submit a correct source file?")
145 end
147 end
146 end
148 end
147
149
148 def must_have_valid_problem
150 def must_have_valid_problem
149 return if self.source==nil
151 return if self.source==nil
150 if self.problem==nil
152 if self.problem==nil
151 errors.add('problem',"must be specified.")
153 errors.add('problem',"must be specified.")
152 else
154 else
153 #admin always have right
155 #admin always have right
154 return if self.user.admin?
156 return if self.user.admin?
155
157
156 #check if user has the right to submit the problem
158 #check if user has the right to submit the problem
157 errors[:base] << "Authorization error: you have no right to submit to this problem" if (!self.user.available_problems.include?(self.problem)) and (self.new_record?)
159 errors[:base] << "Authorization error: you have no right to submit to this problem" if (!self.user.available_problems.include?(self.problem)) and (self.new_record?)
158 end
160 end
159 end
161 end
160
162
161 # callbacks
163 # callbacks
162 def assign_latest_number_if_new_recond
164 def assign_latest_number_if_new_recond
163 return if !self.new_record?
165 return if !self.new_record?
164 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
166 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
165 self.number = (latest==nil) ? 1 : latest.number + 1;
167 self.number = (latest==nil) ? 1 : latest.number + 1;
166 end
168 end
167
169
168 end
170 end
@@ -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
52 + .card-body
53 + %h2.card-title Model submission
31 %table.table.table-hover
54 %table.table.table-hover
32 %thead
55 %thead
33 %tr
56 %tr
34 - %th
57 + %th #Sub
35 - %th
58 + %th Author
36 %tbody
59 %tbody
37 - %tr
60 + - @model_subs.each do |sub|
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
61 %tr
57 - %td.info_param
62 + %td= link_to "##{sub.id}", submission_path(sub)
58 - Best Memory Usage
63 + %td= sub.user.full_name
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
64 .col-md-8
101 - if @best
65 - if @best
102 - %h2 By Language
66 + .card
67 + .card-body
68 + %h2.card-title Top Submissions
103 %table.table.table-hover
69 %table.table.table-hover
104 %thead
70 %thead
105 %tr
71 %tr
106 %th Language
72 %th Language
107 %th Best runtime (ms)
73 %th Best runtime (ms)
108 %th Best memory (kbytes)
74 %th Best memory (kbytes)
109 %th Shortest Code (bytes)
75 %th Shortest Code (bytes)
110 %th First solver
76 %th First solver
111 %tbody
77 %tbody
78 + %tr.bg-warning
79 + %td
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])}
90 + %br
91 + using <span class="text-success">#{@best[:memory][:lang]}</span>
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])
102 + %td
103 + - if @best[:first][:user] != '(NULL)'
104 + #{link_to @best[:first][:user], stat_user_path(@best[:first][:user_id])} is the first solver
105 + %br
106 + using <span class="text-success">#{@best[:first][:lang]}</span>
107 + %br
108 + on <span class="text-success">#{@best[:first][:value]}</span>
109 + %br= link_to "#" + @best[:first][:sub_id].to_s, submission_path( @best[:first][:sub_id])
110 + - else
111 + no first solver
112 - @by_lang.each do |lang,value|
112 - @by_lang.each do |lang,value|
113 %tr
113 %tr
114 %td= lang
114 %td= lang
115 %td
115 %td
116 = link_to value[:runtime][:user], stat_user_path(value[:runtime][:user_id])
116 = link_to value[:runtime][:user], stat_user_path(value[:runtime][:user_id])
117 %br
117 %br
118 = "#{(value[:runtime][:value] * 1000).to_i} @"
118 = "#{(value[:runtime][:value] * 1000).to_i} @"
119 = link_to "#" + value[:runtime][:sub_id].to_s, submission_path( value[:runtime][:sub_id])
119 = link_to "#" + value[:runtime][:sub_id].to_s, submission_path( value[:runtime][:sub_id])
120 %td
120 %td
121 = link_to value[:memory][:user], stat_user_path( value[:memory][:user_id])
121 = link_to value[:memory][:user], stat_user_path( value[:memory][:user_id])
122 %br
122 %br
123 = "#{number_with_delimiter(value[:memory][:value])} @"
123 = "#{number_with_delimiter(value[:memory][:value])} @"
124 = link_to "#" + value[:memory][:sub_id].to_s, submission_path(value[:memory][:sub_id])
124 = link_to "#" + value[:memory][:sub_id].to_s, submission_path(value[:memory][:sub_id])
125 %td
125 %td
126 = link_to value[:length][:user], stat_user_path(value[:length][:user_id])
126 = link_to value[:length][:user], stat_user_path(value[:length][:user_id])
127 %br
127 %br
128 = "#{value[:length][:value]} @"
128 = "#{value[:length][:value]} @"
129 = link_to "#" + value[:length][:sub_id].to_s, submission_path(value[:length][:sub_id])
129 = link_to "#" + value[:length][:sub_id].to_s, submission_path(value[:length][:sub_id])
130 %td
130 %td
131 - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong...
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])
132 = link_to value[:first][:user], stat_user_path(value[:first][:user_id])
133 %br
133 %br
134 = "#{value[:first][:value]} @"
134 = "#{value[:first][:value]} @"
135 = link_to "#" + value[:first][:sub_id].to_s, submission_path( value[:first][:sub_id])
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
@@ -1,116 +1,128
1 %h1= "Submission: #{@submission.id}"
1 %h1= "Submission: #{@submission.id}"
2
2
3 %textarea#data{style: "display:none;"}
3 %textarea#data{style: "display:none;"}
4 :preserve
4 :preserve
5 #{@submission.source}
5 #{@submission.source}
6
6
7 //%div.highlight{:style => "border: 1px solid black;"}
7 //%div.highlight{:style => "border: 1px solid black;"}
8 //=@formatted_code.html_safe
8 //=@formatted_code.html_safe
9
9
10
10
11 .containter
11 .containter
12 .row
12 .row
13 .col-md-7
13 .col-md-7
14 %h2 Source Code
14 %h2 Source Code
15 .col-md-5
15 .col-md-5
16 %h2 Stat
16 %h2 Stat
17 .row
17 .row
18 .col-md-7
18 .col-md-7
19 %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" }
19 %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" }
20 :javascript
20 :javascript
21 e = ace.edit("editor")
21 e = ace.edit("editor")
22 e.setOptions({ maxLines: Infinity })
22 e.setOptions({ maxLines: Infinity })
23 e.setValue($("#data").text())
23 e.setValue($("#data").text())
24 e.gotoLine(1)
24 e.gotoLine(1)
25 e.getSession().setMode("#{get_ace_mode(@submission.language)}")
25 e.getSession().setMode("#{get_ace_mode(@submission.language)}")
26 e.setReadOnly(true)
26 e.setReadOnly(true)
27 .col-md-5
27 .col-md-5
28 %table.table.table-striped
28 %table.table.table-striped
29 %tr
29 %tr
30 %td.text-right
30 %td.text-right
31 %strong User
31 %strong User
32 %td
32 %td
33 - if @current_user.admin? ||@current_user == @submission.user
33 - if @current_user.admin? ||@current_user == @submission.user
34 - if @submission.user
34 - if @submission.user
35 = link_to "#{@submission.user.login}", stat_user_path(@submission.user)
35 = link_to "#{@submission.user.login}", stat_user_path(@submission.user)
36 = @submission.user.full_name
36 = @submission.user.full_name
37 - else
37 - else
38 = "(n/a)"
38 = "(n/a)"
39 - else
39 - else
40 = '-- REDACTED --'
40 = '-- REDACTED --'
41 %tr
41 %tr
42 %td.text-right
42 %td.text-right
43 %strong Task
43 %strong Task
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 + %tr
93 + %td.text-right
94 + %strong Grading Task Status
95 + %td
96 + = @task.status_str if @task
97 + - if session[:admin]
98 + = link_to "rejudge", rejudge_submission_path, data: {remote: true}, class: 'btn btn-info btn-xs'
92 - if session[:admin]
99 - if session[:admin]
93 %tr
100 %tr
94 %td.text-right
101 %td.text-right
95 %strong IP
102 %strong IP
96 %td #{@submission.ip_address}
103 %td #{@submission.ip_address}
97 %tr
104 %tr
98 %td.text-right
105 %td.text-right
99 - %strong Grading Task Status
106 + %strong Model solution
100 %td
107 %td
101 - = @task.status_str if @task
108 + - if @submission.tag_model?
109 + YES
102 - if session[:admin]
110 - if session[:admin]
103 - = link_to "rejudge", rejudge_submission_path, data: {remote: true}, class: 'btn btn-info btn-xs'
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
@@ -1,212 +1,213
1 Rails.application.routes.draw do
1 Rails.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 match 'login/login', to: 'login#login', via: [:get,:post]
8 match 'login/login', to: 'login#login', via: [:get,:post]
9
9
10 resources :contests
10 resources :contests
11 resources :sites
11 resources :sites
12 resources :test
12 resources :test
13
13
14 resources :messages do
14 resources :messages do
15 member do
15 member do
16 get 'hide'
16 get 'hide'
17 post 'reply'
17 post 'reply'
18 end
18 end
19 collection do
19 collection do
20 get 'console'
20 get 'console'
21 get 'list_all'
21 get 'list_all'
22 end
22 end
23 end
23 end
24
24
25 resources :announcements do
25 resources :announcements do
26 member do
26 member do
27 get 'toggle','toggle_front'
27 get 'toggle','toggle_front'
28 end
28 end
29 end
29 end
30
30
31 resources :problems do
31 resources :problems do
32 member do
32 member do
33 get 'toggle'
33 get 'toggle'
34 get 'toggle_test'
34 get 'toggle_test'
35 get 'toggle_view_testcase'
35 get 'toggle_view_testcase'
36 get 'stat'
36 get 'stat'
37 end
37 end
38 collection do
38 collection do
39 get 'turn_all_off'
39 get 'turn_all_off'
40 get 'turn_all_on'
40 get 'turn_all_on'
41 get 'import'
41 get 'import'
42 get 'manage'
42 get 'manage'
43 get 'quick_create'
43 get 'quick_create'
44 post 'do_manage'
44 post 'do_manage'
45 post 'do_import'
45 post 'do_import'
46 end
46 end
47 end
47 end
48
48
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'
145 get 'multiple_login'
146 get 'multiple_login'
146 get 'problem_hof(/:id)', action: 'problem_hof', as: 'problem_hof'
147 get 'problem_hof(/:id)', action: 'problem_hof', as: 'problem_hof'
147 get 'current_score(/:group_id)', action: 'current_score', as: 'current_score'
148 get 'current_score(/:group_id)', action: 'current_score', as: 'current_score'
148 get 'max_score'
149 get 'max_score'
149 post 'show_max_score'
150 post 'show_max_score'
150 get 'stuck'
151 get 'stuck'
151 get 'cheat_report'
152 get 'cheat_report'
152 post 'cheat_report'
153 post 'cheat_report'
153 get 'cheat_scrutinize'
154 get 'cheat_scrutinize'
154 post 'cheat_scrutinize'
155 post 'cheat_scrutinize'
155 get 'submission'
156 get 'submission'
156 post 'submission_query'
157 post 'submission_query'
157 get 'login_stat'
158 get 'login_stat'
158 post 'login_stat'
159 post 'login_stat'
159 get 'login'
160 get 'login'
160 post 'login_summary_query'
161 post 'login_summary_query'
161 post 'login_detail_query'
162 post 'login_detail_query'
162 end
163 end
163 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
164 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
164 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
165 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
165 #get "report/login"
166 #get "report/login"
166 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
167 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
167 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
168 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
168
169
169 resource :main, only: [], controller: 'main' do
170 resource :main, only: [], controller: 'main' do
170 get 'login'
171 get 'login'
171 get 'logout'
172 get 'logout'
172 get 'list'
173 get 'list'
173 get 'submission(/:id)', action: 'submission', as: 'main_submission'
174 get 'submission(/:id)', action: 'submission', as: 'main_submission'
174 get 'announcements'
175 get 'announcements'
175 get 'help'
176 get 'help'
176 post 'submit'
177 post 'submit'
177 end
178 end
178 #main
179 #main
179 #get "main/list"
180 #get "main/list"
180 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
181 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
181 #post 'main/submit', to: 'main#submit'
182 #post 'main/submit', to: 'main#submit'
182 #get 'main/announcements', to: 'main#announcements'
183 #get 'main/announcements', to: 'main#announcements'
183
184
184
185
185 #
186 #
186 get 'tasks/view/:file.:ext' => 'tasks#view'
187 get 'tasks/view/:file.:ext' => 'tasks#view'
187 get 'tasks/download/:id/:file.:ext' => 'tasks#download', as: 'download_task'
188 get 'tasks/download/:id/:file.:ext' => 'tasks#download', as: 'download_task'
188 get 'heartbeat/:id/edit' => 'heartbeat#edit'
189 get 'heartbeat/:id/edit' => 'heartbeat#edit'
189
190
190 #grader
191 #grader
191 #get 'graders/list', to: 'graders#list', as: 'grader_list'
192 #get 'graders/list', to: 'graders#list', as: 'grader_list'
192 namespace :graders do
193 namespace :graders do
193 get 'task/:id/:type', action: 'task', as: 'task'
194 get 'task/:id/:type', action: 'task', as: 'task'
194 get 'view/:id/:type', action: 'view', as: 'view'
195 get 'view/:id/:type', action: 'view', as: 'view'
195 get 'clear/:id', action: 'clear', as: 'clear'
196 get 'clear/:id', action: 'clear', as: 'clear'
196 get 'start_grading'
197 get 'start_grading'
197 get 'start_exam'
198 get 'start_exam'
198 get 'clear_all'
199 get 'clear_all'
199 get 'stop_all'
200 get 'stop_all'
200
201
201 get 'stop'
202 get 'stop'
202 get 'clear_terminated'
203 get 'clear_terminated'
203 get 'list'
204 get 'list'
204 end
205 end
205
206
206
207
207 # See how all your routes lay out with "rake routes"
208 # See how all your routes lay out with "rake routes"
208
209
209 # This is a legacy wild controller route that's not recommended for RESTful applications.
210 # This is a legacy wild controller route that's not recommended for RESTful applications.
210 # Note: This route will make all actions in every controller accessible via GET requests.
211 # Note: This route will make all actions in every controller accessible via GET requests.
211 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
212 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
212 end
213 end
You need to be logged in to leave comments. Login now