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

r854:14d6c4fb29b0 - - 12 files changed: 225 inserted, 138 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,137 +1,148
1 1 require 'ipaddr'
2 2 require "securerandom"
3 3
4 4 class ApplicationController < ActionController::Base
5 5 protect_from_forgery
6 6
7 7 before_action :current_user
8 8 before_action :nav_announcement
9 9 before_action :unique_visitor_id
10 10
11 11 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
12 12 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
13 13 WHITELIST_IGNORE_CONF_KEY = 'right.whitelist_ignore'
14 14 WHITELIST_IP_CONF_KEY = 'right.whitelist_ip'
15 15
16 16 #report and redirect for unauthorized activities
17 17 def unauthorized_redirect(notice = 'You are not authorized to view the page you requested')
18 18 flash[:notice] = notice
19 19 redirect_to login_main_path
20 20 end
21 21
22 22 # Returns the current logged-in user (if any).
23 23 def current_user
24 24 return nil unless session[:user_id]
25 25 @current_user ||= User.find(session[:user_id])
26 26 end
27 27
28 28 def nav_announcement
29 29 @nav_announcement = Announcement.where(on_nav_bar: true)
30 30 end
31 31
32 32 def admin_authorization
33 33 return false unless check_valid_login
34 34 user = User.includes(:roles).find(session[:user_id])
35 35 unless user.admin?
36 36 unauthorized_redirect
37 37 return false
38 38 end
39 39 return true
40 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 53 def authorization_by_roles(allowed_roles)
43 54 return false unless check_valid_login
44 55 unless @current_user.roles.detect { |role| allowed_roles.member?(role.name) }
45 56 unauthorized_redirect
46 57 return false
47 58 end
48 59 end
49 60
50 61 def testcase_authorization
51 62 #admin always has privileged
52 63 if @current_user.admin?
53 64 return true
54 65 end
55 66
56 67 unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
57 68 end
58 69
59 70 def unique_visitor_id
60 71 unless cookies.encrypted[:uuid]
61 72 value = SecureRandom.uuid
62 73 cookies.encrypted[:uuid] = { value: value, expires: 20.year }
63 74 end
64 75 puts "encrypt " + cookies.encrypted[:uuid]
65 76 puts cookies[:uuid]
66 77 end
67 78
68 79 protected
69 80
70 81 #redirect to root (and also force logout)
71 82 #if the user is not logged_in or the system is in "ADMIN ONLY" mode
72 83 def check_valid_login
73 84 #check if logged in
74 85 unless session[:user_id]
75 86 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
76 87 unauthorized_redirect('You need to login but you cannot log in at this time')
77 88 else
78 89 unauthorized_redirect('You need to login')
79 90 end
80 91 return false
81 92 end
82 93
83 94 # check if run in single user mode
84 95 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
85 96 if @current_user==nil || (!@current_user.admin?)
86 97 unauthorized_redirect('You cannot log in at this time')
87 98 return false
88 99 end
89 100 end
90 101
91 102 # check if the user is enabled
92 103 unless @current_user.enabled? || @current_user.admin?
93 104 unauthorized_redirect 'Your account is disabled'
94 105 return false
95 106 end
96 107
97 108 # check if user ip is allowed
98 109 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
99 110 unless is_request_ip_allowed?
100 111 unauthorized_redirect 'Your IP is not allowed to login at this time.'
101 112 return false
102 113 end
103 114 end
104 115
105 116 if GraderConfiguration.multicontests?
106 117 return true if @current_user.admin?
107 118 begin
108 119 if @current_user.contest_stat(true).forced_logout
109 120 flash[:notice] = 'You have been automatically logged out.'
110 121 redirect_to :controller => 'main', :action => 'index'
111 122 end
112 123 rescue
113 124 end
114 125 end
115 126 return true
116 127 end
117 128
118 129 #redirect to root (and also force logout)
119 130 #if the user use different ip from the previous connection
120 131 # only applicable when MULTIPLE_IP_LOGIN options is false only
121 132 def authenticate_by_ip_address
122 133 #this assume that we have already authenticate normally
123 134 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
124 135 user = User.find(session[:user_id])
125 136 if (!user.admin? && user.last_ip && user.last_ip != request.remote_ip)
126 137 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
127 138 redirect_to :controller => 'main', :action => 'login'
128 139 return false
129 140 end
130 141 unless user.last_ip
131 142 user.last_ip = request.remote_ip
132 143 user.save
133 144 end
134 145 end
135 146 return true
136 147 end
137 148
@@ -1,170 +1,170
1 1 class MainController < ApplicationController
2 2
3 3 before_action :check_valid_login, :except => [:login]
4 4 before_action :check_viewability, :except => [:index, :login]
5 5
6 6 append_before_action :confirm_and_update_start_time,
7 7 :except => [:index,
8 8 :login,
9 9 :confirm_contest_start]
10 10
11 11 # to prevent log in box to be shown when user logged out of the
12 12 # system only in some tab
13 13 prepend_before_action :reject_announcement_refresh_when_logged_out,
14 14 :only => [:announcements]
15 15
16 16 before_action :authenticate_by_ip_address, :only => [:list]
17 17
18 18 #reset login, clear session
19 19 #front page
20 20 def login
21 21 saved_notice = flash[:notice]
22 22 reset_session
23 23 flash.now[:notice] = saved_notice
24 24 @remote_ip = request.remote_ip
25 25
26 26 # EXPERIMENT:
27 27 # Hide login if in single user mode and the url does not
28 28 # explicitly specify /login
29 29 #
30 30 # logger.info "PATH: #{request.path}"
31 31 # if GraderConfiguration['system.single_user_mode'] and
32 32 # request.path!='/main/login'
33 33 # @hidelogin = true
34 34 # end
35 35
36 36 @announcements = Announcement.frontpage
37 37 render :action => 'login', :layout => 'empty'
38 38 end
39 39
40 40 def logout
41 41 reset_session
42 42 redirect_to root_path
43 43 end
44 44
45 45 def list
46 46 prepare_list_information
47 47 end
48 48
49 49 def help
50 50 @user = User.find(session[:user_id])
51 51 end
52 52
53 53 def submit
54 54 user = User.find(session[:user_id])
55 55
56 56 @submission = Submission.new
57 57 @submission.problem_id = params[:submission][:problem_id]
58 58 @submission.user = user
59 59 @submission.language_id = 0
60 60 if (params['file']) and (params['file']!='')
61 61 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
62 62 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
63 63 @submission.source_filename = params['file'].original_filename
64 64 end
65 65
66 66 if (params[:editor_text])
67 67 language = Language.find_by_id(params[:language_id])
68 68 @submission.source = params[:editor_text]
69 69 @submission.source_filename = "live_edit.#{language.ext}"
70 70 @submission.language = language
71 71 end
72 72
73 73 @submission.submitted_at = Time.new.gmtime
74 - @submission.ip_address = request.remote_ip
74 + @submission.ip_address = cookies.encrypted[:uuid]
75 75
76 76 if @current_user.admin? == false && GraderConfiguration.time_limit_mode? && @current_user.contest_finished?
77 77 @submission.errors.add(:base,"The contest is over.")
78 78 prepare_list_information
79 79 render :action => 'list' and return
80 80 end
81 81
82 82 if @submission.valid?(@current_user)
83 83 if @submission.save == false
84 84 flash[:notice] = 'Error saving your submission'
85 85 elsif Task.create(:submission_id => @submission.id,
86 86 :status => Task::STATUS_INQUEUE) == false
87 87 flash[:notice] = 'Error adding your submission to task queue'
88 88 end
89 89 else
90 90 prepare_list_information
91 91 render :action => 'list' and return
92 92 end
93 93 redirect_to edit_submission_path(@submission)
94 94 end
95 95
96 96 def source
97 97 submission = Submission.find(params[:id])
98 98 if ((submission.user_id == session[:user_id]) and
99 99 (submission.problem != nil) and
100 100 (submission.problem.available))
101 101 send_data(submission.source,
102 102 {:filename => submission.download_filename,
103 103 :type => 'text/plain'})
104 104 else
105 105 flash[:notice] = 'Error viewing source'
106 106 redirect_to :action => 'list'
107 107 end
108 108 end
109 109
110 110 def compiler_msg
111 111 @submission = Submission.find(params[:id])
112 112 if @submission.user_id == session[:user_id]
113 113 render :action => 'compiler_msg', :layout => 'empty'
114 114 else
115 115 flash[:notice] = 'Error viewing source'
116 116 redirect_to :action => 'list'
117 117 end
118 118 end
119 119
120 120 def result
121 121 if !GraderConfiguration.show_grading_result
122 122 redirect_to :action => 'list' and return
123 123 end
124 124 @user = User.find(session[:user_id])
125 125 @submission = Submission.find(params[:id])
126 126 if @submission.user!=@user
127 127 flash[:notice] = 'You are not allowed to view result of other users.'
128 128 redirect_to :action => 'list' and return
129 129 end
130 130 prepare_grading_result(@submission)
131 131 end
132 132
133 133 def load_output
134 134 if !GraderConfiguration.show_grading_result or params[:num]==nil
135 135 redirect_to :action => 'list' and return
136 136 end
137 137 @user = User.find(session[:user_id])
138 138 @submission = Submission.find(params[:id])
139 139 if @submission.user!=@user
140 140 flash[:notice] = 'You are not allowed to view result of other users.'
141 141 redirect_to :action => 'list' and return
142 142 end
143 143 case_num = params[:num].to_i
144 144 out_filename = output_filename(@user.login,
145 145 @submission.problem.name,
146 146 @submission.id,
147 147 case_num)
148 148 if !FileTest.exists?(out_filename)
149 149 flash[:notice] = 'Output not found.'
150 150 redirect_to :action => 'list' and return
151 151 end
152 152
153 153 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
154 154 response.headers['Content-Type'] = "application/force-download"
155 155 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
156 156 response.headers["X-Sendfile"] = out_filename
157 157 response.headers['Content-length'] = File.size(out_filename)
158 158 render :nothing => true
159 159 else
160 160 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
161 161 end
162 162 end
163 163
164 164 def error
165 165 @user = User.find(session[:user_id])
166 166 end
167 167
168 168 # announcement refreshing and hiding methods
169 169
170 170 def announcements
@@ -141,272 +141,281
141 141 record = record.pluck("users.id,users.login,users.full_name,count(logins.created_at),min(logins.created_at),max(logins.created_at)")
142 142 record.each do |user|
143 143 x = Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
144 144 user[0],@since_time,@until_time)
145 145 .pluck(:ip_address).uniq
146 146 @users << { id: user[0],
147 147 login: user[1],
148 148 full_name: user[2],
149 149 count: user[3],
150 150 min: user[4],
151 151 max: user[5],
152 152 ip: x
153 153 }
154 154 end
155 155 end
156 156
157 157 def login_detail_query
158 158 @logins = Array.new
159 159
160 160 date_and_time = '%Y-%m-%d %H:%M'
161 161 begin
162 162 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
163 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 164 rescue
165 165 @since_time = DateTime.new(1000,1,1)
166 166 end
167 167 begin
168 168 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
169 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 170 rescue
171 171 @until_time = DateTime.new(3000,1,1)
172 172 end
173 173
174 174 @logins = Login.includes(:user).where("logins.created_at >= ? AND logins.created_at <= ?",@since_time, @until_time)
175 175 case params[:users]
176 176 when 'enabled'
177 177 @logins = @logins.where(users: {enabled: true})
178 178 when 'group'
179 179 @logins = @logins.joins(user: :groups).where(user: {groups: {id: params[:groups]}}) if params[:groups]
180 180 end
181 181 end
182 182
183 183 def submission
184 184 end
185 185
186 186 def submission_query
187 187 @submissions = Submission
188 188 .includes(:problem).includes(:user).includes(:language)
189 189
190 190 case params[:users]
191 191 when 'enabled'
192 192 @submissions = @submissions.where(users: {enabled: true})
193 193 when 'group'
194 194 @submissions = @submissions.joins(user: :groups).where(user: {groups: {id: params[:groups]}}) if params[:groups]
195 195 end
196 196
197 197 case params[:problems]
198 198 when 'enabled'
199 199 @submissions = @submissions.where(problems: {available: true})
200 200 when 'selected'
201 201 @submissions = @submissions.where(problem_id: params[:problem_id])
202 202 end
203 203
204 204 #set default
205 205 params[:since_datetime] = Date.today.to_s if params[:since_datetime].blank?
206 206
207 207 @submissions, @recordsTotal, @recordsFiltered = process_query_record( @submissions,
208 208 global_search: ['user.login','user.full_name','problem.name','problem.full_name','points'],
209 209 date_filter: 'submitted_at',
210 210 date_param_since: 'since_datetime',
211 211 date_param_until: 'until_datetime',
212 212 hard_limit: 100_000
213 213 )
214 214 end
215 215
216 216 def login
217 217 end
218 218
219 219 def problem_hof
220 220 # gen problem list
221 221 @user = User.find(session[:user_id])
222 222 @problems = @user.available_problems
223 223
224 224 # get selected problems or the default
225 225 if params[:id]
226 226 begin
227 227 @problem = Problem.available.find(params[:id])
228 228 rescue
229 229 redirect_to action: :problem_hof
230 230 flash[:notice] = 'Error: submissions for that problem are not viewable.'
231 231 return
232 232 end
233 233 end
234 234
235 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 242 @by_lang = {} #aggregrate by language
238 243
239 244 range =65
240 - @histogram = { data: Array.new(range,0), summary: {} }
245 + #@histogram = { data: Array.new(range,0), summary: {} }
241 246 @summary = {count: 0, solve: 0, attempt: 0}
242 247 user = Hash.new(0)
243 248 Submission.where(problem_id: @problem.id).find_each do |sub|
244 249 #histogram
245 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 253 next unless sub.points
249 254 @summary[:count] += 1
250 255 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
251 256
252 257 lang = Language.find_by_id(sub.language_id)
253 258 next unless lang
254 259 next unless sub.points >= @problem.full_score
255 260
256 261 #initialize
257 262 unless @by_lang.has_key?(lang.pretty_name)
258 263 @by_lang[lang.pretty_name] = {
259 264 runtime: { avail: false, value: 2**30-1 },
260 265 memory: { avail: false, value: 2**30-1 },
261 266 length: { avail: false, value: 2**30-1 },
262 267 first: { avail: false, value: DateTime.new(3000,1,1) }
263 268 }
264 269 end
265 270
266 271 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
267 272 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
268 273 end
269 274
270 275 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
271 276 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
272 277 end
273 278
274 279 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and sub.user and
275 280 !sub.user.admin?
276 281 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
277 282 end
278 283
279 284 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
280 285 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
281 286 end
282 287 end
283 288
284 289 #process user_id
285 290 @by_lang.each do |lang,prop|
286 291 prop.each do |k,v|
287 292 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
288 293 end
289 294 end
290 295
291 296 #sum into best
292 297 if @by_lang and @by_lang.first
293 298 @best = @by_lang.first[1].clone
294 299 @by_lang.each do |lang,prop|
295 300 if @best[:runtime][:value] >= prop[:runtime][:value]
296 301 @best[:runtime] = prop[:runtime]
297 302 @best[:runtime][:lang] = lang
298 303 end
299 304 if @best[:memory][:value] >= prop[:memory][:value]
300 305 @best[:memory] = prop[:memory]
301 306 @best[:memory][:lang] = lang
302 307 end
303 308 if @best[:length][:value] >= prop[:length][:value]
304 309 @best[:length] = prop[:length]
305 310 @best[:length][:lang] = lang
306 311 end
307 312 if @best[:first][:value] >= prop[:first][:value]
308 313 @best[:first] = prop[:first]
309 314 @best[:first][:lang] = lang
310 315 end
311 316 end
312 317 end
313 318
314 - @histogram[:summary][:max] = [@histogram[:data].max,1].max
319 + #@histogram[:summary][:max] = [@histogram[:data].max,1].max
315 320 @summary[:attempt] = user.count
316 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 326 end
318 327
319 328 def stuck #report struggling user,problem
320 329 # init
321 330 user,problem = nil
322 331 solve = true
323 332 tries = 0
324 333 @struggle = Array.new
325 334 record = {}
326 335 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
327 336 next unless sub.problem and sub.user
328 337 if user != sub.user_id or problem != sub.problem_id
329 338 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
330 339 record = {user: sub.user, problem: sub.problem}
331 340 user,problem = sub.user_id, sub.problem_id
332 341 solve = false
333 342 tries = 0
334 343 end
335 344 if sub.points >= sub.problem.full_score
336 345 solve = true
337 346 else
338 347 tries += 1
339 348 end
340 349 end
341 350 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
342 351 @struggle = @struggle[0..50]
343 352 end
344 353
345 354
346 355 def multiple_login
347 356 #user with multiple IP
348 357 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
349 358 last,count = 0,0
350 359 first = 0
351 360 @users = []
352 361 raw.each do |r|
353 362 if last != r.user.login
354 363 count = 1
355 364 last = r.user.login
356 365 first = r
357 366 else
358 367 @users << first if count == 1
359 368 @users << r
360 369 count += 1
361 370 end
362 371 end
363 372
364 373 #IP with multiple user
365 374 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
366 375 last,count = 0,0
367 376 first = 0
368 377 @ip = []
369 378 raw.each do |r|
370 379 if last != r.ip_address
371 380 count = 1
372 381 last = r.ip_address
373 382 first = r
374 383 else
375 384 @ip << first if count == 1
376 385 @ip << r
377 386 count += 1
378 387 end
379 388 end
380 389 end
381 390
382 391 def cheat_report
383 392 date_and_time = '%Y-%m-%d %H:%M'
384 393 begin
385 394 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
386 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 396 rescue
388 397 @since_time = Time.zone.now.ago( 90.minutes)
389 398 end
390 399 begin
391 400 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
392 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 402 rescue
394 403 @until_time = Time.zone.now
395 404 end
396 405
397 406 #multi login
398 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 409 st = <<-SQL
401 410 SELECT l2.*
402 411 FROM logins l2 INNER JOIN
403 412 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
404 413 FROM logins l
405 414 INNER JOIN users u ON l.user_id = u.id
406 415 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
407 416 GROUP BY u.id
408 417 HAVING count > 1
409 418 ) ml ON l2.user_id = ml.id
410 419 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
411 420 UNION
412 421 SELECT l2.*
@@ -1,111 +1,115
1 1 class SubmissionsController < ApplicationController
2 + before_action :set_submission, only: [:show,:download,:compiler_msg,:rejudge,:set_tag, :edit]
2 3 before_action :check_valid_login
3 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 7 # GET /submissions
7 8 # GET /submissions.json
8 9 # Show problem selection and user's submission of that problem
9 10 def index
10 11 @user = @current_user
11 12 @problems = @user.available_problems
12 13
13 14 if params[:problem_id]==nil
14 15 @problem = nil
15 16 @submissions = nil
16 17 else
17 18 @problem = Problem.find_by_id(params[:problem_id])
18 19 if (@problem == nil) or (not @problem.available)
19 20 redirect_to list_main_path
20 21 flash[:error] = 'Authorization error: You have no right to view submissions for this problem'
21 22 return
22 23 end
23 24 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc)
24 25 end
25 26 end
26 27
27 28 # GET /submissions/1
28 29 # GET /submissions/1.json
29 30 def show
30 - @submission = Submission.find(params[:id])
31 -
32 31 #log the viewing
33 32 user = User.find(session[:user_id])
34 33 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
35 34
36 35 @task = @submission.task
37 36 end
38 37
39 38 def download
40 - @submission = Submission.find(params[:id])
41 39 send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
42 40 end
43 41
44 42 def compiler_msg
45 - @submission = Submission.find(params[:id])
46 43 respond_to do |format|
47 44 format.js
48 45 end
49 46 end
50 47
51 48 #on-site new submission on specific problem
52 49 def direct_edit_problem
53 50 @problem = Problem.find(params[:problem_id])
54 51 unless @current_user.can_view_problem?(@problem)
55 52 unauthorized_redirect
56 53 return
57 54 end
58 55 @source = ''
59 56 if (params[:view_latest])
60 57 sub = Submission.find_last_by_user_and_problem(@current_user.id,@problem.id)
61 58 @source = @submission.source.to_s if @submission and @submission.source
62 59 end
63 60 render 'edit'
64 61 end
65 62
66 63 # GET /submissions/1/edit
67 64 def edit
68 - @submission = Submission.find(params[:id])
69 65 @source = @submission.source.to_s
70 66 @problem = @submission.problem
71 67 @lang_id = @submission.language.id
72 68 end
73 69
74 70
75 71 def get_latest_submission_status
76 72 @problem = Problem.find(params[:pid])
77 73 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
78 74 respond_to do |format|
79 75 format.js
80 76 end
81 77 end
82 78
83 79 # GET /submissions/:id/rejudge
84 80 def rejudge
85 - @submission = Submission.find(params[:id])
86 81 @task = @submission.task
87 82 @task.status_inqueue! if @task
88 83 respond_to do |format|
89 84 format.js
90 85 end
91 86 end
92 87
88 + def set_tag
89 + @submission.update(tag: params[:tag])
90 + redirect_to @submission
91 + end
92 +
93 93 protected
94 94
95 95 def submission_authorization
96 96 #admin always has privileged
97 97 return true if @current_user.admin?
98 98 return true if @current_user.has_role?('ta') && (['show','download'].include? action_name)
99 99
100 100 sub = Submission.find(params[:id])
101 101 if @current_user.available_problems.include? sub.problem
102 102 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
103 103 end
104 104
105 105 #default to NO
106 106 unauthorized_redirect
107 107 return false
108 108 end
109 109
110 + def set_submission
111 + @submission = Submission.find(params[:id])
112 + end
113 +
110 114
111 115 end
@@ -1,122 +1,144
1 1 class Problem < ActiveRecord::Base
2 2
3 3 belongs_to :description
4 4 has_and_belongs_to_many :contests, :uniq => true
5 5
6 6 #has_and_belongs_to_many :groups
7 7 has_many :groups_problems, class_name: 'GroupProblem'
8 8 has_many :groups, :through => :groups_problems
9 9
10 10 has_many :problems_tags, class_name: 'ProblemTag'
11 11 has_many :tags, through: :problems_tags
12 12
13 13 has_many :test_pairs, :dependent => :delete_all
14 14 has_many :testcases, :dependent => :destroy
15 15
16 16 has_many :submissions
17 17
18 18 validates_presence_of :name
19 19 validates_format_of :name, :with => /\A\w+\z/
20 20 validates_presence_of :full_name
21 21
22 22 scope :available, -> { where(available: true) }
23 23
24 24 DEFAULT_TIME_LIMIT = 1
25 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 49 def self.available_problems
28 50 available.order(date_added: :desc).order(:name)
29 51 #Problem.available.all(:order => "date_added DESC, name ASC")
30 52 end
31 53
32 54 def self.create_from_import_form_params(params, old_problem=nil)
33 55 org_problem = old_problem || Problem.new
34 56 import_params, problem = Problem.extract_params_and_check(params,
35 57 org_problem)
36 58
37 59 if !problem.errors.empty?
38 60 return problem, 'Error importing'
39 61 end
40 62
41 63 problem.full_score = 100
42 64 problem.date_added = Time.new
43 65 problem.test_allowed = true
44 66 problem.output_only = false
45 67 problem.available = false
46 68
47 69 if not problem.save
48 70 return problem, 'Error importing'
49 71 end
50 72
51 73 import_to_db = params.has_key? :import_to_db
52 74
53 75 importer = TestdataImporter.new(problem)
54 76
55 77 if not importer.import_from_file(import_params[:file],
56 78 import_params[:time_limit],
57 79 import_params[:memory_limit],
58 80 import_params[:checker_name],
59 81 import_to_db)
60 82 problem.errors.add(:base,'Import error.')
61 83 end
62 84
63 85 return problem, importer.log_msg
64 86 end
65 87
66 88 def self.download_file_basedir
67 89 return "#{Rails.root}/data/tasks"
68 90 end
69 91
70 92 def get_submission_stat
71 93 result = Hash.new
72 94 #total number of submission
73 95 result[:total_sub] = Submission.where(problem_id: self.id).count
74 96 result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
75 97 result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
76 98 return result
77 99 end
78 100
79 101 def long_name
80 102 "[#{name}] #{full_name}"
81 103 end
82 104
83 105 protected
84 106
85 107 def self.to_i_or_default(st, default)
86 108 if st!=''
87 109 result = st.to_i
88 110 end
89 111 result ||= default
90 112 end
91 113
92 114 def self.to_f_or_default(st, default)
93 115 if st!=''
94 116 result = st.to_f
95 117 end
96 118 result ||= default
97 119 end
98 120
99 121 def self.extract_params_and_check(params, problem)
100 122 time_limit = Problem.to_f_or_default(params[:time_limit],
101 123 DEFAULT_TIME_LIMIT)
102 124 memory_limit = Problem.to_i_or_default(params[:memory_limit],
103 125 DEFAULT_MEMORY_LIMIT)
104 126
105 127 if time_limit<=0 or time_limit >60
106 128 problem.errors.add(:base,'Time limit out of range.')
107 129 end
108 130
109 131 if memory_limit==0 and params[:memory_limit]!='0'
110 132 problem.errors.add(:base,'Memory limit format errors.')
111 133 elsif memory_limit<=0 or memory_limit >512
112 134 problem.errors.add(:base,'Memory limit out of range.')
113 135 end
114 136
115 137 if params[:file]==nil or params[:file]==''
116 138 problem.errors.add(:base,'No testdata file.')
117 139 end
118 140
119 141 checker_name = 'text'
120 142 if ['text','float'].include? params[:checker]
121 143 checker_name = params[:checker]
122 144 end
@@ -1,98 +1,100
1 1 class Submission < ActiveRecord::Base
2 2
3 + enum tag: {default: 0, model: 1}, _prefix: true
4 +
3 5 belongs_to :language
4 6 belongs_to :problem
5 7 belongs_to :user
6 8
7 9 before_validation :assign_problem
8 10 before_validation :assign_language
9 11
10 12 validates_presence_of :source
11 13 validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'code too long, the limit is 100,000 bytes'
12 14 validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short'
13 15 validate :must_have_valid_problem
14 16 validate :must_specify_language
15 17
16 18 has_one :task
17 19
18 20 before_save :assign_latest_number_if_new_recond
19 21
20 22 def self.find_last_by_user_and_problem(user_id, problem_id)
21 23 where("user_id = ? AND problem_id = ?",user_id,problem_id).last
22 24 end
23 25
24 26 def self.find_all_last_by_problem(problem_id)
25 27 # need to put in SQL command, maybe there's a better way
26 28 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
27 29 "WHERE id = " +
28 30 "(SELECT MAX(id) FROM submissions AS subs " +
29 31 "WHERE subs.user_id = submissions.user_id AND " +
30 32 "problem_id = " + problem_id.to_s + " " +
31 33 "GROUP BY user_id) " +
32 34 "ORDER BY user_id")
33 35 end
34 36
35 37 def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id)
36 38 records = Submission.where(problem_id: problem_id,user_id: user_id)
37 39 records = records.where('id >= ?',since_id) if since_id and since_id > 0
38 40 records = records.where('id <= ?',until_id) if until_id and until_id > 0
39 41 records.all
40 42 end
41 43
42 44 def self.find_last_for_all_available_problems(user_id)
43 45 submissions = Array.new
44 46 problems = Problem.available_problems
45 47 problems.each do |problem|
46 48 sub = Submission.find_last_by_user_and_problem(user_id, problem.id)
47 49 submissions << sub if sub!=nil
48 50 end
49 51 submissions
50 52 end
51 53
52 54 def self.find_by_user_problem_number(user_id, problem_id, number)
53 55 where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first
54 56 end
55 57
56 58 def self.find_all_by_user_problem(user_id, problem_id)
57 59 where("user_id = ? AND problem_id = ?",user_id,problem_id)
58 60 end
59 61
60 62 def download_filename
61 63 if self.problem.output_only
62 64 return self.source_filename
63 65 else
64 66 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
65 67 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
66 68 end
67 69 end
68 70
69 71 protected
70 72
71 73 def self.find_option_in_source(option, source)
72 74 if source==nil
73 75 return nil
74 76 end
75 77 i = 0
76 78 source.each_line do |s|
77 79 if s =~ option
78 80 words = s.split
79 81 return words[1]
80 82 end
81 83 i = i + 1
82 84 if i==10
83 85 return nil
84 86 end
85 87 end
86 88 return nil
87 89 end
88 90
89 91 def self.find_language_in_source(source, source_filename="")
90 92 langopt = find_option_in_source(/^LANG:/,source)
91 93 if langopt
92 94 return (Language.find_by_name(langopt) ||
93 95 Language.find_by_pretty_name(langopt))
94 96 else
95 97 if source_filename
96 98 return Language.find_by_extension(source_filename.split('.').last)
97 99 else
98 100 return nil
@@ -1,136 +1,154
1 1 :css
2 2 .hof_user { color: orangered; font-style: italic; }
3 3 .hof_language { color: green; font-style: italic; }
4 4 .hof_value { color: deeppink;font-style: italic; }
5 5 .info_param { font-weight: bold;text-align: right; }
6 6 .tooltip {
7 7 font-family: Verdana,sans-serif;
8 8 font-weight: normal;
9 9 text-align: left;
10 10 font-size: 1.0em;
11 11 color: black;
12 12 line-height: 1.1;
13 13 display: none;
14 14 min-width: 20em;
15 15 position: absolute;
16 16 left: 25px;
17 17 bottom: 5px;
18 18 border: 1px solid;
19 19 padding: 5px;
20 20 background-color: #FFF;
21 21 word-wrap: break-word;
22 22 z-index: 9999;
23 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 49 .row
29 50 .col-md-4
30 - %h2 Overall Stat
51 + .card
52 + .card-body
53 + %h2.card-title Model submission
31 54 %table.table.table-hover
32 55 %thead
33 56 %tr
34 - %th
35 - %th
57 + %th #Sub
58 + %th Author
36 59 %tbody
37 - %tr
38 - %td.info_param Submissions
39 - %td= @summary[:count]
40 - %tr
41 - %td.info_param Solved/Attempted User
42 - %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
43 - - if @best
44 - %tr
45 - %td.info_param Best Runtime
46 - %td
47 - by #{link_to @best[:runtime][:user], stat_user_path(@best[:runtime][:user_id])}
48 - %br
49 - using <span class="text-success">#{@best[:runtime][:lang]}</span>
50 - %br
51 - with <span class="text-success">#{@best[:runtime][:value] * 1000} milliseconds</span>
52 - %br
53 - at submission
54 - = link_to "#" + @best[:runtime][:sub_id].to_s, submission_path(@best[:runtime][:sub_id])
55 -
60 + - @model_subs.each do |sub|
56 61 %tr
57 - %td.info_param
58 - Best Memory Usage
59 - %sup{ id: "xmem_remark",
60 - style: "position:relative; color: blue;",
61 - data: {toggle: 'tooltip', placement: 'top', animation: 'false', delay: 20},
62 - title: "This counts only for submission with 100% score. Right now, java is excluded from memory usage competition. (Because it always uses 2GB memory...)"}
63 - [?]
64 - %td
65 - by #{link_to @best[:memory][:user], stat_user_path(@best[:memory][:user_id])}
66 - %br
67 - using <span class="text-success">#{@best[:memory][:lang]}</span>
68 - %br
69 - with <span class="text-success">#{number_with_delimiter(@best[:memory][:value])} kbytes </span>
70 - %br
71 - at submission
72 - = link_to "#" + @best[:memory][:sub_id].to_s, submission_path(@best[:memory][:sub_id])
73 -
74 - %tr
75 - %td.info_param Shortest Code
76 - %td
77 - by #{link_to @best[:length][:user], stat_user_path(@best[:length][:user_id])}
78 - %br
79 - using <span class="text-success">#{@best[:length][:lang]}</span>
80 - %br
81 - with <span class="text-success">#{@best[:length][:value]} bytes</span>
82 - %br
83 - at submission
84 - = link_to "#" + @best[:length][:sub_id].to_s, submission_path(@best[:length][:sub_id])
85 -
86 - %tr
87 - %td.info_param First solver
88 - %td
89 - - if @best[:first][:user] != '(NULL)'
90 - #{link_to @best[:first][:user], stat_user_path(@best[:first][:user_id])} is the first solver
91 - %br
92 - using <span class="text-success">#{@best[:first][:lang]}</span>
93 - %br
94 - on <span class="text-success">#{@best[:first][:value]}</span>
95 - %br
96 - at submission
97 - = link_to "#" + @best[:first][:sub_id].to_s, submission_path( @best[:first][:sub_id])
98 - - else
99 - no first solver
62 + %td= link_to "##{sub.id}", submission_path(sub)
63 + %td= sub.user.full_name
100 64 .col-md-8
101 65 - if @best
102 - %h2 By Language
66 + .card
67 + .card-body
68 + %h2.card-title Top Submissions
103 69 %table.table.table-hover
104 70 %thead
105 71 %tr
106 72 %th Language
107 73 %th Best runtime (ms)
108 74 %th Best memory (kbytes)
109 75 %th Shortest Code (bytes)
110 76 %th First solver
111 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 112 - @by_lang.each do |lang,value|
113 113 %tr
114 114 %td= lang
115 115 %td
116 116 = link_to value[:runtime][:user], stat_user_path(value[:runtime][:user_id])
117 117 %br
118 118 = "#{(value[:runtime][:value] * 1000).to_i} @"
119 119 = link_to "#" + value[:runtime][:sub_id].to_s, submission_path( value[:runtime][:sub_id])
120 120 %td
121 121 = link_to value[:memory][:user], stat_user_path( value[:memory][:user_id])
122 122 %br
123 123 = "#{number_with_delimiter(value[:memory][:value])} @"
124 124 = link_to "#" + value[:memory][:sub_id].to_s, submission_path(value[:memory][:sub_id])
125 125 %td
126 126 = link_to value[:length][:user], stat_user_path(value[:length][:user_id])
127 127 %br
128 128 = "#{value[:length][:value]} @"
129 129 = link_to "#" + value[:length][:sub_id].to_s, submission_path(value[:length][:sub_id])
130 130 %td
131 131 - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong...
132 132 = link_to value[:first][:user], stat_user_path(value[:first][:user_id])
133 133 %br
134 134 = "#{value[:first][:value]} @"
135 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 2 /- if params[:id]
3 3 / %h1 Tasks Hall of Fame
4 4 / = link_to('[back to All-Time Hall of Fame]', action: 'problem_hof', id: nil )
5 5 /- else
6 6 / %h1 All-Time Hall of Fame
7 7
8 8 .panel.panel-info
9 9 .panel-heading
10 10 Select Task
11 11 .panel-body
12 12 .form-inline
13 13 = select 'report',
14 14 'problem_id',
15 15 @problems.collect {|p| ["[#{p.name}] #{p.full_name}", problem_hof_report_path(p)]},
16 16 {:selected => problem_hof_report_path(@problem)},
17 17 { class: 'select2 form-control' }
18 18 %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: "#report_problem_id"}} Go
19 19
20 20
21 21 - unless params[:id]
22 22 /=render partial: 'all_time_hof'
23 23 Please select a problem.
24 24 - else
25 25 %h1 [#{Problem.find(params[:id]).name}] #{Problem.find(params[:id]).full_name}
26 - %h2 Submission History
27 - =render partial: 'application/bar_graph', locals: { histogram: @histogram }
26 + -# %h2 Submission History
27 + -# =render partial: 'application/bar_graph', locals: { histogram: @histogram }
28 28 =render partial: 'task_hof'
29 29
@@ -1,116 +1,128
1 1 %h1= "Submission: #{@submission.id}"
2 2
3 3 %textarea#data{style: "display:none;"}
4 4 :preserve
5 5 #{@submission.source}
6 6
7 7 //%div.highlight{:style => "border: 1px solid black;"}
8 8 //=@formatted_code.html_safe
9 9
10 10
11 11 .containter
12 12 .row
13 13 .col-md-7
14 14 %h2 Source Code
15 15 .col-md-5
16 16 %h2 Stat
17 17 .row
18 18 .col-md-7
19 19 %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" }
20 20 :javascript
21 21 e = ace.edit("editor")
22 22 e.setOptions({ maxLines: Infinity })
23 23 e.setValue($("#data").text())
24 24 e.gotoLine(1)
25 25 e.getSession().setMode("#{get_ace_mode(@submission.language)}")
26 26 e.setReadOnly(true)
27 27 .col-md-5
28 28 %table.table.table-striped
29 29 %tr
30 30 %td.text-right
31 31 %strong User
32 32 %td
33 33 - if @current_user.admin? ||@current_user == @submission.user
34 34 - if @submission.user
35 35 = link_to "#{@submission.user.login}", stat_user_path(@submission.user)
36 36 = @submission.user.full_name
37 37 - else
38 38 = "(n/a)"
39 39 - else
40 40 = '-- REDACTED --'
41 41 %tr
42 42 %td.text-right
43 43 %strong Task
44 44 %td
45 45 - if @submission.problem!=nil
46 46 = link_to "[#{@submission.problem.name}]", stat_problem_path(@submission.problem)
47 47 = @submission.problem.full_name
48 48 = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @submission.problem
49 49 - else
50 50 = "(n/a)"
51 51 %tr
52 52 %td.text-right
53 53 %strong Tries
54 54 %td= @submission.number
55 55 %tr
56 56 %td.text-right
57 57 %strong Language
58 58 %td= @submission.language.pretty_name
59 59 %tr
60 60 %td.text-right
61 61 %strong Submitted
62 62 %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
63 63 %tr
64 64 %td.text-right
65 65 %strong Graded
66 66 - if @submission.graded_at
67 67 %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
68 68 - else
69 69 %td -
70 70 %tr
71 71 %td.text-right
72 72 %strong Points
73 73 %td #{@submission.points}/#{@submission.try(:problem).try(:full_score)}
74 74 %tr
75 75 %td.text-right
76 76 %strong Comment
77 77 %td #{@submission.grader_comment}
78 78 %tr
79 79 %td.text-right
80 80 %strong Runtime (s)
81 81 %td #{@submission.max_runtime}
82 82 %tr
83 83 %td.text-right
84 84 %strong Memory (kb)
85 85 %td #{@submission.peak_memory}
86 86 %tr
87 87 %td.text-right
88 88 %strong Compiler result
89 89 %td
90 90 %button.btn.btn-info.btn-xs{type: 'button', data: {toggle: 'modal', target: '#compiler'}}
91 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 99 - if session[:admin]
93 100 %tr
94 101 %td.text-right
95 102 %strong IP
96 103 %td #{@submission.ip_address}
97 104 %tr
98 105 %td.text-right
99 - %strong Grading Task Status
106 + %strong Model solution
100 107 %td
101 - = @task.status_str if @task
108 + - if @submission.tag_model?
109 + YES
102 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 118 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
107 119 .modal-dialog.modal-lg{role:'document'}
108 120 .modal-content
109 121 .modal-header
110 122 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
111 123 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
112 124 %h4 Compiler message
113 125 .modal-body
114 126 %pre#compiler_msg= @submission.compiler_message
115 127 .modal-footer
116 128 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
@@ -1,192 +1,193
1 1 Rails.application.routes.draw do
2 2 resources :tags
3 3 get "sources/direct_edit"
4 4
5 5 root :to => 'main#login'
6 6
7 7 #logins
8 8 match 'login/login', to: 'login#login', via: [:get,:post]
9 9
10 10 resources :contests
11 11 resources :sites
12 12 resources :test
13 13
14 14 resources :messages do
15 15 member do
16 16 get 'hide'
17 17 post 'reply'
18 18 end
19 19 collection do
20 20 get 'console'
21 21 get 'list_all'
22 22 end
23 23 end
24 24
25 25 resources :announcements do
26 26 member do
27 27 get 'toggle','toggle_front'
28 28 end
29 29 end
30 30
31 31 resources :problems do
32 32 member do
33 33 get 'toggle'
34 34 get 'toggle_test'
35 35 get 'toggle_view_testcase'
36 36 get 'stat'
37 37 end
38 38 collection do
39 39 get 'turn_all_off'
40 40 get 'turn_all_on'
41 41 get 'import'
42 42 get 'manage'
43 43 get 'quick_create'
44 44 post 'do_manage'
45 45 post 'do_import'
46 46 end
47 47 end
48 48
49 49 resources :groups do
50 50 member do
51 51 post 'add_user', to: 'groups#add_user', as: 'add_user'
52 52 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
53 53 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
54 54 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
55 55 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
56 56 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
57 57 get 'toggle'
58 58 end
59 59 collection do
60 60
61 61 end
62 62 end
63 63
64 64 resources :testcases, only: [] do
65 65 member do
66 66 get 'download_input'
67 67 get 'download_sol'
68 68 end
69 69 collection do
70 70 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
71 71 end
72 72 end
73 73
74 74 resources :grader_configuration, controller: 'configurations' do
75 75 collection do
76 76 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
77 77 end
78 78 end
79 79
80 80 resources :users do
81 81 member do
82 82 get 'toggle_activate', 'toggle_enable'
83 83 get 'stat'
84 84 end
85 85 collection do
86 86 get 'profile'
87 87 post 'chg_passwd'
88 88 post 'chg_default_language'
89 89 end
90 90 end
91 91
92 92 resources :submissions do
93 93 member do
94 94 get 'download'
95 95 get 'compiler_msg'
96 96 get 'rejudge'
97 + get 'set_tag'
97 98 end
98 99 collection do
99 100 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
100 101 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
101 102 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
102 103 end
103 104 end
104 105
105 106
106 107 #user admin
107 108 resources :user_admin do
108 109 collection do
109 110 match 'bulk_manage', via: [:get, :post]
110 111 get 'bulk_mail'
111 112 get 'user_stat'
112 113 get 'import'
113 114 get 'new_list'
114 115 get 'admin'
115 116 get 'active'
116 117 get 'mass_mailing'
117 118 match 'modify_role', via: [:get, :post]
118 119 match 'create_from_list', via: [:get, :post]
119 120 match 'random_all_passwords', via: [:get, :post]
120 121 end
121 122 member do
122 123 get 'clear_last_ip'
123 124 end
124 125 end
125 126
126 127 resources :contest_management, only: [:index] do
127 128 collection do
128 129 get 'user_stat'
129 130 get 'clear_stat'
130 131 get 'clear_all_stat'
131 132 get 'change_contest_mode'
132 133 end
133 134 end
134 135
135 136 #get 'user_admin', to: 'user_admin#index'
136 137 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
137 138 #post 'user_admin', to: 'user_admin#create'
138 139 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
139 140
140 141 #singular resource
141 142 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
142 143 #report
143 144 resource :report, only: [], controller: 'report' do
144 145 get 'login'
145 146 get 'multiple_login'
146 147 get 'problem_hof(/:id)', action: 'problem_hof', as: 'problem_hof'
147 148 get 'current_score(/:group_id)', action: 'current_score', as: 'current_score'
148 149 get 'max_score'
149 150 post 'show_max_score'
150 151 get 'stuck'
151 152 get 'cheat_report'
152 153 post 'cheat_report'
153 154 get 'cheat_scrutinize'
154 155 post 'cheat_scrutinize'
155 156 get 'submission'
156 157 post 'submission_query'
157 158 get 'login_stat'
158 159 post 'login_stat'
159 160 get 'login'
160 161 post 'login_summary_query'
161 162 post 'login_detail_query'
162 163 end
163 164 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
164 165 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
165 166 #get "report/login"
166 167 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
167 168 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
168 169
169 170 resource :main, only: [], controller: 'main' do
170 171 get 'login'
171 172 get 'logout'
172 173 get 'list'
173 174 get 'submission(/:id)', action: 'submission', as: 'main_submission'
174 175 get 'announcements'
175 176 get 'help'
176 177 post 'submit'
177 178 end
178 179 #main
179 180 #get "main/list"
180 181 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
181 182 #post 'main/submit', to: 'main#submit'
182 183 #get 'main/announcements', to: 'main#announcements'
183 184
184 185
185 186 #
186 187 get 'tasks/view/:file.:ext' => 'tasks#view'
187 188 get 'tasks/download/:id/:file.:ext' => 'tasks#download', as: 'download_task'
188 189 get 'heartbeat/:id/edit' => 'heartbeat#edit'
189 190
190 191 #grader
191 192 #get 'graders/list', to: 'graders#list', as: 'grader_list'
192 193 namespace :graders do
@@ -1,313 +1,315
1 1 # This file is auto-generated from the current state of the database. Instead
2 2 # of editing this file, please use the migrations feature of Active Record to
3 3 # incrementally modify your database, and then regenerate this schema definition.
4 4 #
5 - # Note that this schema.rb definition is the authoritative source for your
6 - # database schema. If you need to create the application database on another
7 - # system, you should be using db:schema:load, not running all the migrations
8 - # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 - # you'll amass, the slower it'll run and the greater likelihood for issues).
5 + # This file is the source Rails uses to define your schema when running `bin/rails
6 + # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7 + # be faster and is potentially less error prone than running all of your
8 + # migrations from scratch. Old migrations may fail to apply correctly if those
9 + # migrations use external dependencies or application code.
10 10 #
11 11 # It's strongly recommended that you check this file into your version control system.
12 12
13 - ActiveRecord::Schema.define(version: 2021_08_09_105935) do
13 + ActiveRecord::Schema.define(version: 2022_02_04_080936) do
14 14
15 - create_table "announcements", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
15 + create_table "announcements", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
16 16 t.string "author"
17 - t.text "body", limit: 16777215
17 + t.text "body"
18 18 t.boolean "published"
19 19 t.datetime "created_at", null: false
20 20 t.datetime "updated_at", null: false
21 21 t.boolean "frontpage", default: false
22 22 t.boolean "contest_only", default: false
23 23 t.string "title"
24 24 t.string "notes"
25 25 t.boolean "on_nav_bar", default: false
26 26 end
27 27
28 - create_table "contests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
28 + create_table "contests", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
29 29 t.string "title"
30 30 t.boolean "enabled"
31 31 t.datetime "created_at", null: false
32 32 t.datetime "updated_at", null: false
33 33 t.string "name"
34 34 end
35 35
36 - create_table "contests_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
36 + create_table "contests_problems", id: false, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
37 37 t.integer "contest_id"
38 38 t.integer "problem_id"
39 39 end
40 40
41 - create_table "contests_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
41 + create_table "contests_users", id: false, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
42 42 t.integer "contest_id"
43 43 t.integer "user_id"
44 44 end
45 45
46 - create_table "countries", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
46 + create_table "countries", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
47 47 t.string "name"
48 48 t.datetime "created_at", null: false
49 49 t.datetime "updated_at", null: false
50 50 end
51 51
52 - create_table "descriptions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
53 - t.text "body", limit: 16777215
52 + create_table "descriptions", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
53 + t.text "body"
54 54 t.boolean "markdowned"
55 55 t.datetime "created_at", null: false
56 56 t.datetime "updated_at", null: false
57 57 end
58 58
59 - create_table "grader_configurations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
59 + create_table "grader_configurations", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
60 60 t.string "key"
61 61 t.string "value_type"
62 62 t.string "value"
63 63 t.datetime "created_at", null: false
64 64 t.datetime "updated_at", null: false
65 - t.text "description", limit: 16777215
65 + t.text "description"
66 66 end
67 67
68 - create_table "grader_processes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
68 + create_table "grader_processes", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
69 69 t.string "host"
70 70 t.integer "pid"
71 71 t.string "mode"
72 72 t.boolean "active"
73 73 t.datetime "created_at", null: false
74 74 t.datetime "updated_at", null: false
75 75 t.integer "task_id"
76 76 t.string "task_type"
77 77 t.boolean "terminated"
78 78 t.index ["host", "pid"], name: "index_grader_processes_on_ip_and_pid"
79 79 end
80 80
81 - create_table "groups", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
81 + create_table "groups", id: :integer, charset: "latin1", force: :cascade do |t|
82 82 t.string "name"
83 83 t.string "description"
84 84 t.boolean "enabled", default: true
85 85 end
86 86
87 - create_table "groups_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
87 + create_table "groups_problems", id: false, charset: "latin1", force: :cascade do |t|
88 88 t.integer "problem_id", null: false
89 89 t.integer "group_id", null: false
90 90 t.index ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id"
91 91 end
92 92
93 - create_table "groups_users", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
93 + create_table "groups_users", charset: "latin1", force: :cascade do |t|
94 94 t.integer "group_id", null: false
95 95 t.integer "user_id", null: false
96 96 t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
97 97 end
98 98
99 - create_table "heart_beats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
99 + create_table "heart_beats", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
100 100 t.integer "user_id"
101 101 t.string "ip_address"
102 102 t.datetime "created_at", null: false
103 103 t.datetime "updated_at", null: false
104 104 t.string "status"
105 105 t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
106 106 end
107 107
108 - create_table "languages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
108 + create_table "languages", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
109 109 t.string "name", limit: 10
110 110 t.string "pretty_name"
111 111 t.string "ext", limit: 10
112 112 t.string "common_ext"
113 113 end
114 114
115 - create_table "logins", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
115 + create_table "logins", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
116 116 t.integer "user_id"
117 117 t.string "ip_address"
118 118 t.datetime "created_at", null: false
119 119 t.datetime "updated_at", null: false
120 120 t.index ["user_id"], name: "index_logins_on_user_id"
121 121 end
122 122
123 - create_table "messages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
123 + create_table "messages", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
124 124 t.integer "sender_id"
125 125 t.integer "receiver_id"
126 126 t.integer "replying_message_id"
127 - t.text "body", limit: 16777215
127 + t.text "body"
128 128 t.boolean "replied"
129 129 t.datetime "created_at", null: false
130 130 t.datetime "updated_at", null: false
131 131 end
132 132
133 - create_table "problems", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
134 - t.string "name", limit: 100
133 + create_table "problems", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
134 + t.string "name", limit: 30
135 135 t.string "full_name"
136 136 t.integer "full_score"
137 137 t.date "date_added"
138 138 t.boolean "available"
139 139 t.string "url"
140 140 t.integer "description_id"
141 141 t.boolean "test_allowed"
142 142 t.boolean "output_only"
143 143 t.string "description_filename"
144 144 t.boolean "view_testcase"
145 + t.integer "difficulty"
145 146 end
146 147
147 - create_table "problems_tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
148 + create_table "problems_tags", id: :integer, charset: "latin1", force: :cascade do |t|
148 149 t.integer "problem_id"
149 150 t.integer "tag_id"
150 151 t.index ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true
151 152 t.index ["problem_id"], name: "index_problems_tags_on_problem_id"
152 153 t.index ["tag_id"], name: "index_problems_tags_on_tag_id"
153 154 end
154 155
155 - create_table "rights", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
156 + create_table "rights", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
156 157 t.string "name"
157 158 t.string "controller"
158 159 t.string "action"
159 160 end
160 161
161 - create_table "rights_roles", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
162 + create_table "rights_roles", id: false, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
162 163 t.integer "right_id"
163 164 t.integer "role_id"
164 165 t.index ["role_id"], name: "index_rights_roles_on_role_id"
165 166 end
166 167
167 - create_table "roles", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
168 + create_table "roles", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
168 169 t.string "name"
169 170 end
170 171
171 - create_table "roles_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
172 + create_table "roles_users", id: false, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
172 173 t.integer "role_id"
173 174 t.integer "user_id"
174 175 t.index ["user_id"], name: "index_roles_users_on_user_id"
175 176 end
176 177
177 - create_table "sessions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
178 + create_table "sessions", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
178 179 t.string "session_id"
179 - t.text "data", limit: 16777215
180 + t.text "data"
180 181 t.datetime "updated_at"
181 182 t.index ["session_id"], name: "index_sessions_on_session_id"
182 183 t.index ["updated_at"], name: "index_sessions_on_updated_at"
183 184 end
184 185
185 - create_table "sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
186 + create_table "sites", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
186 187 t.string "name"
187 188 t.boolean "started"
188 189 t.datetime "start_time"
189 190 t.datetime "created_at", null: false
190 191 t.datetime "updated_at", null: false
191 192 t.integer "country_id"
192 193 t.string "password"
193 194 end
194 195
195 - create_table "submission_view_logs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
196 + create_table "submission_view_logs", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
196 197 t.integer "user_id"
197 198 t.integer "submission_id"
198 199 t.datetime "created_at", null: false
199 200 t.datetime "updated_at", null: false
200 201 end
201 202
202 - create_table "submissions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
203 + create_table "submissions", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
203 204 t.integer "user_id"
204 205 t.integer "problem_id"
205 206 t.integer "language_id"
206 - t.text "source", limit: 16777215
207 + t.text "source", size: :medium
207 208 t.binary "binary"
208 209 t.datetime "submitted_at"
209 210 t.datetime "compiled_at"
210 - t.text "compiler_message", limit: 16777215
211 + t.text "compiler_message"
211 212 t.datetime "graded_at"
212 213 t.integer "points"
213 - t.text "grader_comment", limit: 16777215
214 + t.text "grader_comment"
214 215 t.integer "number"
215 216 t.string "source_filename"
216 217 t.float "max_runtime"
217 218 t.integer "peak_memory"
218 219 t.integer "effective_code_length"
219 220 t.string "ip_address"
221 + t.integer "tag", default: 0
220 222 t.index ["submitted_at"], name: "index_submissions_on_submitted_at"
221 223 t.index ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true
222 224 t.index ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id"
223 225 end
224 226
225 - create_table "tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
227 + create_table "tags", id: :integer, charset: "latin1", force: :cascade do |t|
226 228 t.string "name", null: false
227 229 t.text "description"
228 230 t.boolean "public"
229 231 t.datetime "created_at", null: false
230 232 t.datetime "updated_at", null: false
231 233 end
232 234
233 - create_table "tasks", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
235 + create_table "tasks", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
234 236 t.integer "submission_id"
235 237 t.datetime "created_at"
236 238 t.integer "status"
237 239 t.datetime "updated_at"
238 240 t.index ["status"], name: "index_tasks_on_status"
239 241 t.index ["submission_id"], name: "index_tasks_on_submission_id"
240 242 end
241 243
242 - create_table "test_pairs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
244 + create_table "test_pairs", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
243 245 t.integer "problem_id"
244 - t.text "input", limit: 4294967295
245 - t.text "solution", limit: 4294967295
246 + t.text "input", size: :medium
247 + t.text "solution", size: :medium
246 248 t.datetime "created_at", null: false
247 249 t.datetime "updated_at", null: false
248 250 end
249 251
250 - create_table "test_requests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
252 + create_table "test_requests", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
251 253 t.integer "user_id"
252 254 t.integer "problem_id"
253 255 t.integer "submission_id"
254 256 t.string "input_file_name"
255 257 t.string "output_file_name"
256 258 t.string "running_stat"
257 259 t.integer "status"
258 260 t.datetime "updated_at", null: false
259 261 t.datetime "submitted_at"
260 262 t.datetime "compiled_at"
261 - t.text "compiler_message", limit: 16777215
263 + t.text "compiler_message"
262 264 t.datetime "graded_at"
263 265 t.string "grader_comment"
264 266 t.datetime "created_at", null: false
265 267 t.float "running_time"
266 268 t.string "exit_status"
267 269 t.integer "memory_usage"
268 270 t.index ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id"
269 271 end
270 272
271 - create_table "testcases", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
273 + create_table "testcases", id: :integer, charset: "latin1", force: :cascade do |t|
272 274 t.integer "problem_id"
273 275 t.integer "num"
274 276 t.integer "group"
275 277 t.integer "score"
276 - t.text "input", limit: 4294967295
277 - t.text "sol", limit: 4294967295
278 - t.datetime "created_at", null: false
279 - t.datetime "updated_at", null: false
278 + t.text "input", size: :long
279 + t.text "sol", size: :long
280 + t.datetime "created_at"
281 + t.datetime "updated_at"
280 282 t.index ["problem_id"], name: "index_testcases_on_problem_id"
281 283 end
282 284
283 - create_table "user_contest_stats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
285 + create_table "user_contest_stats", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
284 286 t.integer "user_id"
285 287 t.datetime "started_at"
286 288 t.datetime "created_at", null: false
287 289 t.datetime "updated_at", null: false
288 290 t.boolean "forced_logout"
289 291 end
290 292
291 - create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
293 + create_table "users", id: :integer, charset: "utf8", collation: "utf8_unicode_ci", force: :cascade do |t|
292 294 t.string "login", limit: 50
293 295 t.string "full_name"
294 296 t.string "hashed_password"
295 297 t.string "salt", limit: 5
296 298 t.string "alias"
297 299 t.string "email"
298 300 t.integer "site_id"
299 301 t.integer "country_id"
300 302 t.boolean "activated", default: false
301 303 t.datetime "created_at"
302 304 t.datetime "updated_at"
303 - t.string "section"
304 305 t.boolean "enabled", default: true
305 306 t.string "remark"
306 307 t.string "last_ip"
308 + t.string "section"
307 309 t.integer "default_language"
308 310 t.index ["login"], name: "index_users_on_login", unique: true
309 311 end
310 312
311 313 add_foreign_key "problems_tags", "problems"
312 314 add_foreign_key "problems_tags", "tags"
313 315 end
You need to be logged in to leave comments. Login now