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,89 +1,100
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
@@ -26,97 +26,97
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
@@ -189,176 +189,185
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
@@ -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,74 +1,96
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)
@@ -1,50 +1,52
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
@@ -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
@@ -44,73 +44,85
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
@@ -49,96 +49,97
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'
@@ -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