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

r834:825a2986c8ea - - 19 files changed: 140 inserted, 89 deleted

@@ -0,0 +1,8
1 + = render partial: 'toggle_button',
2 + locals: {button_id: "#group-enabled-#{@group.id}",button_on: @group.enabled }
3 + :plain
4 + r = $("#group-#{@group.id}");
5 + r.removeClass('success');
6 + r.removeClass('danger');
7 + r.addClass("#{@group.enabled? ? 'success' : 'danger'}");
8 +
@@ -0,0 +1,5
1 + class AddEnabledToGroup < ActiveRecord::Migration[5.2]
2 + def change
3 + add_column :groups, :enabled, :boolean, default: true
4 + end
5 + end
@@ -59,109 +59,110
59 59 #check if logged in
60 60 unless session[:user_id]
61 61 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
62 62 unauthorized_redirect('You need to login but you cannot log in at this time')
63 63 else
64 64 unauthorized_redirect('You need to login')
65 65 end
66 66 return false
67 67 end
68 68
69 69 # check if run in single user mode
70 70 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
71 71 if @current_user==nil || (!@current_user.admin?)
72 72 unauthorized_redirect('You cannot log in at this time')
73 73 return false
74 74 end
75 75 end
76 76
77 77 # check if the user is enabled
78 78 unless @current_user.enabled? || @current_user.admin?
79 79 unauthorized_redirect 'Your account is disabled'
80 80 return false
81 81 end
82 82
83 83 # check if user ip is allowed
84 84 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
85 85 unless is_request_ip_allowed?
86 86 unauthorized_redirect 'Your IP is not allowed to login at this time.'
87 87 return false
88 88 end
89 89 end
90 90
91 91 if GraderConfiguration.multicontests?
92 92 return true if @current_user.admin?
93 93 begin
94 94 if @current_user.contest_stat(true).forced_logout
95 95 flash[:notice] = 'You have been automatically logged out.'
96 96 redirect_to :controller => 'main', :action => 'index'
97 97 end
98 98 rescue
99 99 end
100 100 end
101 101 return true
102 102 end
103 103
104 104 #redirect to root (and also force logout)
105 105 #if the user use different ip from the previous connection
106 106 # only applicable when MULTIPLE_IP_LOGIN options is false only
107 107 def authenticate_by_ip_address
108 108 #this assume that we have already authenticate normally
109 109 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
110 110 user = User.find(session[:user_id])
111 111 if (!user.admin? && user.last_ip && user.last_ip != request.remote_ip)
112 112 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
113 113 redirect_to :controller => 'main', :action => 'login'
114 114 return false
115 115 end
116 116 unless user.last_ip
117 117 user.last_ip = request.remote_ip
118 118 user.save
119 119 end
120 120 end
121 121 return true
122 122 end
123 123
124 124 def authorization
125 125 return false unless check_valid_login
126 126 user = User.find(session[:user_id])
127 127 unless user.roles.detect { |role|
128 128 role.rights.detect{ |right|
129 129 right.controller == self.class.controller_name and
130 130 (right.action == 'all' || right.action == action_name)
131 131 }
132 132 }
133 133 flash[:notice] = 'You are not authorized to view the page you requested'
134 134 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
135 135 redirect_to :controller => 'main', :action => 'login'
136 136 return false
137 137 end
138 138 end
139 139
140 140 def verify_time_limit
141 141 return true if session[:user_id]==nil
142 142 user = User.find(session[:user_id], :include => :site)
143 143 return true if user==nil || user.site == nil
144 144 if user.contest_finished?
145 145 flash[:notice] = 'Error: the contest you are participating is over.'
146 146 redirect_to :back
147 147 return false
148 148 end
149 149 return true
150 150 end
151 151
152 152 def is_request_ip_allowed?
153 153 unless GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
154 154 user_ip = IPAddr.new(request.remote_ip)
155 + allowed = GraderConfiguration[WHITELIST_IP_CONF_KEY] || ''
155 156
156 - GraderConfiguration[WHITELIST_IP_CONF_KEY].delete(' ').split(',').each do |ips|
157 + allowed.delete(' ').split(',').each do |ips|
157 158 allow_ips = IPAddr.new(ips)
158 159 if allow_ips.include?(user_ip)
159 160 return true
160 161 end
161 162 end
162 163 return false
163 164 end
164 165 return true
165 166 end
166 167
167 168 end
@@ -1,104 +1,110
1 1 class GroupsController < ApplicationController
2 2 before_action :set_group, only: [:show, :edit, :update, :destroy,
3 3 :add_user, :remove_user,:remove_all_user,
4 4 :add_problem, :remove_problem,:remove_all_problem,
5 + :toggle,
5 6 ]
6 7 before_action :admin_authorization
7 8
8 9 # GET /groups
9 10 def index
10 11 @groups = Group.all
11 12 end
12 13
13 14 # GET /groups/1
14 15 def show
15 16 end
16 17
17 18 # GET /groups/new
18 19 def new
19 20 @group = Group.new
20 21 end
21 22
22 23 # GET /groups/1/edit
23 24 def edit
24 25 end
25 26
26 27 # POST /groups
27 28 def create
28 29 @group = Group.new(group_params)
29 30
30 31 if @group.save
31 32 redirect_to @group, notice: 'Group was successfully created.'
32 33 else
33 34 render :new
34 35 end
35 36 end
36 37
37 38 # PATCH/PUT /groups/1
38 39 def update
39 40 if @group.update(group_params)
40 41 redirect_to @group, notice: 'Group was successfully updated.'
41 42 else
42 43 render :edit
43 44 end
44 45 end
45 46
46 47 # DELETE /groups/1
47 48 def destroy
48 49 @group.destroy
49 50 redirect_to groups_url, notice: 'Group was successfully destroyed.'
50 51 end
51 52
53 + def toggle
54 + @group.enabled = @group.enabled? ? false : true
55 + @group.save
56 + end
57 +
52 58 def remove_user
53 59 user = User.find(params[:user_id])
54 60 @group.users.delete(user)
55 61 redirect_to group_path(@group), flash: {success: "User #{user.login} was removed from the group #{@group.name}"}
56 62 end
57 63
58 64 def remove_all_user
59 65 @group.users.clear
60 66 redirect_to group_path(@group), alert: 'All users removed'
61 67 end
62 68
63 69 def remove_all_problem
64 70 @group.problems.clear
65 71 redirect_to group_path(@group), alert: 'All problems removed'
66 72 end
67 73
68 74 def add_user
69 75 user = User.find(params[:user_id])
70 76 begin
71 77 @group.users << user
72 78 redirect_to group_path(@group), flash: { success: "User #{user.login} was add to the group #{@group.name}"}
73 79 rescue => e
74 80 redirect_to group_path(@group), alert: e.message
75 81 end
76 82 end
77 83
78 84 def remove_problem
79 85 problem = Problem.find(params[:problem_id])
80 86 @group.problems.delete(problem)
81 87 redirect_to group_path(@group), flash: {success: "Problem #{problem.name} was removed from the group #{@group.name}" }
82 88 end
83 89
84 90 def add_problem
85 91 problem = Problem.find(params[:problem_id])
86 92 begin
87 93 @group.problems << problem
88 94 redirect_to group_path(@group), flash: {success: "Problem #{problem.name} was add to the group #{@group.name}" }
89 95 rescue => e
90 96 redirect_to group_path(@group), alert: e.message
91 97 end
92 98 end
93 99
94 100 private
95 101 # Use callbacks to share common setup or constraints between actions.
96 102 def set_group
97 103 @group = Group.find(params[:id])
98 104 end
99 105
100 106 # Only allow a trusted parameter "white list" through.
101 107 def group_params
102 - params.require(:group).permit(:name, :description)
108 + params.require(:group).permit(:name, :description, :enabled)
103 109 end
104 110 end
@@ -1,112 +1,112
1 1 class SubmissionsController < ApplicationController
2 2 before_action :check_valid_login
3 3 before_action :submission_authorization, only: [:show, :download, :edit]
4 4 before_action :admin_authorization, only: [:rejudge]
5 5
6 6 # GET /submissions
7 7 # GET /submissions.json
8 8 # Show problem selection and user's submission of that problem
9 9 def index
10 10 @user = @current_user
11 11 @problems = @user.available_problems
12 12
13 13 if params[:problem_id]==nil
14 14 @problem = nil
15 15 @submissions = nil
16 16 else
17 17 @problem = Problem.find_by_id(params[:problem_id])
18 18 if (@problem == nil) or (not @problem.available)
19 - redirect_to main_list_path
20 - flash[:notice] = 'Error: submissions for that problem are not viewable.'
19 + redirect_to list_main_path
20 + flash[:error] = 'Authorization error: You have no right to view submissions for this problem'
21 21 return
22 22 end
23 23 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id).order(id: :desc)
24 24 end
25 25 end
26 26
27 27 # GET /submissions/1
28 28 # GET /submissions/1.json
29 29 def show
30 30 @submission = Submission.find(params[:id])
31 31
32 32 #log the viewing
33 33 user = User.find(session[:user_id])
34 34 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
35 35
36 36 @task = @submission.task
37 37 end
38 38
39 39 def download
40 40 @submission = Submission.find(params[:id])
41 41 send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
42 42 end
43 43
44 44 def compiler_msg
45 45 @submission = Submission.find(params[:id])
46 46 respond_to do |format|
47 47 format.js
48 48 end
49 49 end
50 50
51 51 #on-site new submission on specific problem
52 52 def direct_edit_problem
53 53 @problem = Problem.find(params[:problem_id])
54 54 unless @current_user.can_view_problem?(@problem)
55 55 unauthorized_redirect
56 56 return
57 57 end
58 58 @source = ''
59 59 if (params[:view_latest])
60 60 sub = Submission.find_last_by_user_and_problem(@current_user.id,@problem.id)
61 61 @source = @submission.source.to_s if @submission and @submission.source
62 62 end
63 63 render 'edit'
64 64 end
65 65
66 66 # GET /submissions/1/edit
67 67 def edit
68 68 @submission = Submission.find(params[:id])
69 69 @source = @submission.source.to_s
70 70 @problem = @submission.problem
71 71 @lang_id = @submission.language.id
72 72 end
73 73
74 74
75 75 def get_latest_submission_status
76 76 @problem = Problem.find(params[:pid])
77 77 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
78 78 respond_to do |format|
79 79 format.js
80 80 end
81 81 end
82 82
83 83 # GET /submissions/:id/rejudge
84 84 def rejudge
85 85 @submission = Submission.find(params[:id])
86 86 @task = @submission.task
87 87 @task.status_inqueue! if @task
88 88 respond_to do |format|
89 89 format.js
90 90 end
91 91 end
92 92
93 93 protected
94 94
95 95 def submission_authorization
96 96 #admin always has privileged
97 97 if @current_user.admin?
98 98 return true
99 99 end
100 100
101 101 sub = Submission.find(params[:id])
102 102 if @current_user.available_problems.include? sub.problem
103 103 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
104 104 end
105 105
106 106 #default to NO
107 107 unauthorized_redirect
108 108 return false
109 109 end
110 110
111 111
112 112 end
@@ -1,74 +1,76
1 1 class TasksController < ApplicationController
2 2
3 3 before_action :check_valid_login, :check_viewability
4 4
5 5 def index
6 6 redirect_to :action => 'list'
7 7 end
8 8
9 9 def list
10 10 @problems = @user.available_problems
11 11 end
12 12
13 13 # this has contest-wide access control
14 14 def view
15 15 base_name = params[:file]
16 16 base_filename = File.basename("#{base_name}.#{params[:ext]}")
17 17 filename = "#{Problem.download_file_basedir}/#{base_filename}"
18 18
19 19 if !FileTest.exists?(filename)
20 20 redirect_to :action => 'index' and return
21 21 end
22 22
23 23 send_file_to_user(filename, base_filename)
24 24 end
25 25
26 26 # this has problem-level access control
27 27 def download
28 28 problem = Problem.find(params[:id])
29 29 unless @current_user.can_view_problem? problem
30 - flash[:notice] = 'You are not authorized to access this file'
31 - redirect_to :action => 'index' and return
30 + flash[:error] = 'You are not authorized to access this file'
31 + redirect_to list_main_path
32 + return
32 33 end
33 34
34 35 base_name = params[:file]
35 36 base_filename = File.basename("#{base_name}.#{params[:ext]}")
36 37 filename = "#{Problem.download_file_basedir}/#{params[:id]}/#{base_filename}"
37 38
38 39 if !FileTest.exists?(filename)
39 40 flash[:notice] = 'File does not exists'
40 - redirect_to :action => 'index' and return
41 + redirect_to list_main_path
42 + return
41 43 end
42 44
43 45 send_file_to_user(filename, base_filename)
44 46 end
45 47
46 48 protected
47 49
48 50 def send_file_to_user(filename, base_filename)
49 51 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
50 52 response.headers['Content-Type'] = "application/force-download"
51 53 response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filename)}\""
52 54 response.headers["X-Sendfile"] = filename
53 55 response.headers['Content-length'] = File.size(filename)
54 56 render :nothing => true
55 57 else
56 58 if params[:ext]=='pdf'
57 59 content_type = 'application/pdf'
58 60 else
59 61 content_type = 'application/octet-stream'
60 62 end
61 63
62 64 send_file filename, :stream => false, :disposition => 'inline', :filename => base_filename, :type => content_type
63 65 end
64 66 end
65 67
66 68 def check_viewability
67 69 @user = User.find(session[:user_id])
68 70 if @user==nil or !GraderConfiguration.show_tasks_to?(@user)
69 71 redirect_to :controller => 'main', :action => 'list'
70 72 return false
71 73 end
72 74 end
73 75
74 76 end
@@ -1,194 +1,204
1 1 require 'csv'
2 2
3 3 class UserAdminController < ApplicationController
4 4
5 5 include MailHelperMethods
6 6
7 7 before_action :admin_authorization
8 8
9 9 def index
10 10 @user_count = User.count
11 11 if params[:page] == 'all'
12 12 @users = User.all
13 13 @paginated = false
14 14 else
15 15 @users = User.paginate :page => params[:page]
16 16 @paginated = true
17 17 end
18 18 @users = User.all
19 19 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
20 20 @contests = Contest.enabled
21 21 end
22 22
23 23 def active
24 24 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
25 25 @users = []
26 26 sessions.each do |session|
27 27 if session.data[:user_id]
28 28 @users << User.find(session.data[:user_id])
29 29 end
30 30 end
31 31 end
32 32
33 33 def show
34 34 @user = User.find(params[:id])
35 35 end
36 36
37 37 def new
38 38 @user = User.new
39 39 end
40 40
41 41 def create
42 42 @user = User.new(user_params)
43 43 @user.activated = true
44 44 if @user.save
45 45 flash[:notice] = 'User was successfully created.'
46 46 redirect_to :action => 'index'
47 47 else
48 48 render :action => 'new'
49 49 end
50 50 end
51 51
52 52 def clear_last_ip
53 53 @user = User.find(params[:id])
54 54 @user.last_ip = nil
55 55 @user.save
56 56 redirect_to action: 'index', page: params[:page]
57 57 end
58 58
59 59 def create_from_list
60 60 lines = params[:user_list]
61 61
62 62 note = []
63 63 error_note = []
64 64 error_msg = nil
65 65 ok_user = []
66 66
67 67 lines.split("\n").each do |line|
68 - items = line.chomp.split(',')
68 + #split with large limit, this will cause consecutive ',' to be result in a blank
69 + items = line.chomp.split(',',1000)
69 70 if items.length>=2
70 71 login = items[0]
71 72 full_name = items[1]
72 73 remark =''
73 74 user_alias = ''
74 75
75 76 added_random_password = false
76 - if items.length >= 3 and items[2].chomp(" ").length > 0;
77 + added_password = false
78 + if items.length >= 3
79 + if items[2].chomp(" ").length > 0
77 80 password = items[2].chomp(" ")
81 + added_password = true
82 + end
78 83 else
79 84 password = random_password
80 85 added_random_password=true;
81 86 end
82 87
83 88 if items.length>= 4 and items[3].chomp(" ").length > 0;
84 89 user_alias = items[3].chomp(" ")
85 90 else
86 91 user_alias = login
87 92 end
88 93
94 +
95 + has_remark = false
89 96 if items.length>=5
90 97 remark = items[4].strip;
98 + has_remark = true
91 99 end
92 100
93 101 user = User.find_by_login(login)
94 102 if (user)
95 103 user.full_name = full_name
96 - user.password = password
97 - user.remark = remark
104 + user.remark = remark if has_remark
105 + user.password = password if added_password || added_random_password
98 106 else
107 + #create a random password if none are given
108 + password = random_password unless password
99 109 user = User.new({:login => login,
100 110 :full_name => full_name,
101 111 :password => password,
102 112 :password_confirmation => password,
103 113 :alias => user_alias,
104 114 :remark => remark})
105 115 end
106 116 user.activated = true
107 117
108 118 if user.save
109 119 if added_random_password
110 120 note << "'#{login}' (+)"
111 121 else
112 122 note << login
113 123 end
114 124 ok_user << user
115 125 else
116 126 error_note << "'#{login}'"
117 127 error_msg = user.errors.full_messages.to_sentence unless error_msg
118 128 end
119 129
120 130 end
121 131 end
122 132
123 133 #add to group
124 134 if params[:add_to_group]
125 135 group = Group.where(id: params[:group_id]).first
126 136 if group
127 137 group.users << ok_user
128 138 end
129 139 end
130 140
131 141 # show flash
132 142 if note.size > 0
133 143 flash[:success] = 'User(s) ' + note.join(', ') +
134 144 ' were successfully created. ' +
135 145 '( (+) - created with random passwords.)'
136 146 end
137 147 if error_note.size > 0
138 148 flash[:error] = "Following user(s) failed to be created: " + error_note.join(', ') + ". The error of the first failed one are: " + error_msg;
139 149 end
140 150 redirect_to :action => 'index'
141 151 end
142 152
143 153 def edit
144 154 @user = User.find(params[:id])
145 155 end
146 156
147 157 def update
148 158 @user = User.find(params[:id])
149 159 if @user.update_attributes(user_params)
150 160 flash[:notice] = 'User was successfully updated.'
151 161 redirect_to :action => 'show', :id => @user
152 162 else
153 163 render :action => 'edit'
154 164 end
155 165 end
156 166
157 167 def destroy
158 168 User.find(params[:id]).destroy
159 169 redirect_to :action => 'index'
160 170 end
161 171
162 172 def user_stat
163 173 if params[:commit] == 'download csv'
164 174 @problems = Problem.all
165 175 else
166 176 @problems = Problem.available_problems
167 177 end
168 178 @users = User.includes(:contests, :contest_stat).where(enabled: true)
169 179 @scorearray = Array.new
170 180 @users.each do |u|
171 181 ustat = Array.new
172 182 ustat[0] = u
173 183 @problems.each do |p|
174 184 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
175 185 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
176 186 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
177 187 else
178 188 ustat << [0,false]
179 189 end
180 190 end
181 191 @scorearray << ustat
182 192 end
183 193 if params[:commit] == 'download csv' then
184 194 csv = gen_csv_from_scorearray(@scorearray,@problems)
185 195 send_data csv, filename: 'last_score.csv'
186 196 else
187 197 render template: 'user_admin/user_stat'
188 198 end
189 199 end
190 200
191 201 def user_stat_max
192 202 if params[:commit] == 'download csv'
193 203 @problems = Problem.all
194 204 else
@@ -20,205 +20,203
20 20 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
21 21 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
22 22 if GraderConfiguration['system.user_setting_enabled']
23 23 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
24 24 end
25 25 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
26 26
27 27
28 28 result = content_tag(:ul,left_menu.html_safe,class: 'nav navbar-nav') + content_tag(:ul,right_menu.html_safe,class: 'nav navbar-nav navbar-right')
29 29 end
30 30
31 31 def add_menu(title, controller, action, html_option = {})
32 32 link_option = {controller: controller, action: action}
33 33 html_option[:class] = (html_option[:class] || '') + " active" if current_page?(link_option)
34 34 content_tag(:li, link_to(title,link_option),html_option)
35 35 end
36 36
37 37 def user_header
38 38 menu_items = ''
39 39 user = User.find(session[:user_id])
40 40
41 41 if (user!=nil) and (session[:admin])
42 42 # admin menu
43 43 menu_items << "<b>Administrative task:</b> "
44 44 append_to menu_items, '[Announcements]', 'announcements', 'index'
45 45 append_to menu_items, '[Msg console]', 'messages', 'console'
46 46 append_to menu_items, '[Problems]', 'problems', 'index'
47 47 append_to menu_items, '[Users]', 'user_admin', 'index'
48 48 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
49 49 append_to menu_items, '[Report]', 'report', 'multiple_login'
50 50 append_to menu_items, '[Graders]', 'graders', 'list'
51 51 append_to menu_items, '[Contests]', 'contest_management', 'index'
52 52 append_to menu_items, '[Sites]', 'sites', 'index'
53 53 append_to menu_items, '[System config]', 'configurations', 'index'
54 54 menu_items << "<br/>"
55 55 end
56 56
57 57 # main page
58 58 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
59 59 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
60 60
61 61 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
62 62 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
63 63 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
64 64 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
65 65 end
66 66
67 67 if GraderConfiguration['right.user_hall_of_fame']
68 68 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
69 69 end
70 70 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
71 71
72 72 if GraderConfiguration['system.user_setting_enabled']
73 73 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
74 74 end
75 75 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
76 76
77 77 menu_items.html_safe
78 78 end
79 79
80 80 def append_to(option,label, controller, action)
81 81 option << ' ' if option!=''
82 82 option << link_to_unless_current(label,
83 83 :controller => controller,
84 84 :action => action)
85 85 end
86 86
87 87 def format_short_time(time)
88 88 now = Time.zone.now
89 89 st = ''
90 90 if (time.yday != now.yday) or (time.year != now.year)
91 91 st = time.strftime("%d/%m/%y ")
92 92 end
93 93 st + time.strftime("%X")
94 94 end
95 95
96 96 def format_short_duration(duration)
97 97 return '' if duration==nil
98 98 d = duration.to_f
99 99 return Time.at(d).gmtime.strftime("%X")
100 100 end
101 101
102 102 def format_full_time_ago(time)
103 103 st = time_ago_in_words(time) + ' ago (' + format_short_time(time) + ')'
104 104 end
105 105
106 106 def read_textfile(fname,max_size=2048)
107 107 begin
108 108 File.open(fname).read(max_size)
109 109 rescue
110 110 nil
111 111 end
112 112 end
113 113
114 114 def toggle_button(on,toggle_url,id, option={})
115 115 btn_size = option[:size] || 'btn-xs'
116 + btn_block = option[:block] || 'btn-block'
116 117 link_to (on ? "Yes" : "No"), toggle_url,
117 - {class: "btn btn-block #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
118 + {class: "btn #{btn_block} #{btn_size} btn-#{on ? 'success' : 'default'} ajax-toggle",
118 119 id: id,
119 120 data: {remote: true, method: 'get'}}
120 121 end
121 122
122 123 def get_ace_mode(language)
123 124 # return ace mode string from Language
124 125
125 126 case language.pretty_name
126 127 when 'Pascal'
127 128 'ace/mode/pascal'
128 129 when 'C++','C'
129 130 'ace/mode/c_cpp'
130 131 when 'Ruby'
131 132 'ace/mode/ruby'
132 133 when 'Python'
133 134 'ace/mode/python'
134 135 when 'Java'
135 136 'ace/mode/java'
136 137 else
137 138 'ace/mode/c_cpp'
138 139 end
139 140 end
140 141
141 142
142 143 def user_title_bar(user)
143 144 header = ''
144 145 time_left = ''
145 146
146 147 #
147 148 # if the contest is over
148 149 if GraderConfiguration.time_limit_mode?
149 150 if user.contest_finished?
150 151 header = <<CONTEST_OVER
151 152 <tr><td colspan="2" align="center">
152 153 <span class="contest-over-msg">THE CONTEST IS OVER</span>
153 154 </td></tr>
154 155 CONTEST_OVER
155 156 end
156 157 if !user.contest_started?
157 158 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
158 159 else
159 160 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
160 161 " #{format_short_duration(user.contest_time_left)}"
161 162 end
162 163 end
163 164
164 165 #
165 166 # if the contest is in the anaysis mode
166 167 if GraderConfiguration.analysis_mode?
167 168 header = <<ANALYSISMODE
168 169 <tr><td colspan="2" align="center">
169 170 <span class="contest-over-msg">ANALYSIS MODE</span>
170 171 </td></tr>
171 172 ANALYSISMODE
172 173 end
173 174
174 175 contest_name = GraderConfiguration['contest.name']
175 176
176 177 #
177 178 # build real title bar
178 179 result = <<TITLEBAR
179 180 <div class="title">
180 181 <table>
181 182 #{header}
182 183 <tr>
183 184 <td class="left-col">
184 - #{user.full_name}<br/>
185 - #{t 'title_bar.current_time'} #{format_short_time(Time.zone.now)}
186 - #{time_left}
187 185 <br/>
188 186 </td>
189 187 <td class="right-col">#{contest_name}</td>
190 188 </tr>
191 189 </table>
192 190 </div>
193 191 TITLEBAR
194 192 result.html_safe
195 193 end
196 194
197 195 def markdown(text)
198 196 markdown = RDiscount.new(text)
199 197 markdown.to_html.html_safe
200 198 end
201 199
202 200
203 201 BOOTSTRAP_FLASH_MSG = {
204 202 success: 'alert-success',
205 203 error: 'alert-danger',
206 204 alert: 'alert-danger',
207 205 notice: 'alert-info'
208 206 }
209 207
210 208 def bootstrap_class_for(flash_type)
211 209 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
212 210 end
213 211
214 212 def flash_messages
215 213 flash.each do |msg_type, message|
216 214 concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do
217 215 concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' })
218 216 concat message
219 217 end)
220 218 end
221 219 nil
222 220 end
223 221
224 222 end
@@ -1,20 +1,14
1 1 module MainHelper
2 2
3 - def link_to_description_if_any(name, problem, options={})
3 + def link_to_description_if_any(name, problem)
4 4 if !problem.url.blank?
5 - return link_to name, problem.url, options
5 + return link_to name, problem.url
6 6 elsif !problem.description_filename.blank?
7 - #build a link to a problem (via task controller)
8 7 basename, ext = problem.description_filename.split('.')
9 - options[:controller] = 'tasks'
10 - options[:action] = 'download'
11 - options[:id] = problem.id
12 - options[:file] = basename
13 - options[:ext] = ext
14 - return link_to name, options
8 + return link_to name, download_task_path(problem.id,basename,ext), target: '_blank'
15 9 else
16 10 return ''
17 11 end
18 12 end
19 13
20 14 end
@@ -61,108 +61,108
61 61 if self.problem.output_only
62 62 return self.source_filename
63 63 else
64 64 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
65 65 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
66 66 end
67 67 end
68 68
69 69 protected
70 70
71 71 def self.find_option_in_source(option, source)
72 72 if source==nil
73 73 return nil
74 74 end
75 75 i = 0
76 76 source.each_line do |s|
77 77 if s =~ option
78 78 words = s.split
79 79 return words[1]
80 80 end
81 81 i = i + 1
82 82 if i==10
83 83 return nil
84 84 end
85 85 end
86 86 return nil
87 87 end
88 88
89 89 def self.find_language_in_source(source, source_filename="")
90 90 langopt = find_option_in_source(/^LANG:/,source)
91 91 if langopt
92 92 return (Language.find_by_name(langopt) ||
93 93 Language.find_by_pretty_name(langopt))
94 94 else
95 95 if source_filename
96 96 return Language.find_by_extension(source_filename.split('.').last)
97 97 else
98 98 return nil
99 99 end
100 100 end
101 101 end
102 102
103 103 def self.find_problem_in_source(source, source_filename="")
104 104 prob_opt = find_option_in_source(/^TASK:/,source)
105 105 if problem = Problem.find_by_name(prob_opt)
106 106 return problem
107 107 else
108 108 if source_filename
109 109 return Problem.find_by_name(source_filename.split('.').first)
110 110 else
111 111 return nil
112 112 end
113 113 end
114 114 end
115 115
116 116 def assign_problem
117 117 if self.problem_id!=-1
118 118 begin
119 119 self.problem = Problem.find(self.problem_id)
120 120 rescue ActiveRecord::RecordNotFound
121 121 self.problem = nil
122 122 end
123 123 else
124 124 self.problem = Submission.find_problem_in_source(self.source,
125 125 self.source_filename)
126 126 end
127 127 end
128 128
129 129 def assign_language
130 130 if self.language == nil
131 131 self.language = Submission.find_language_in_source(self.source,
132 132 self.source_filename)
133 133 end
134 134 end
135 135
136 136 # validation codes
137 137 def must_specify_language
138 138 return if self.source==nil
139 139
140 140 # for output_only tasks
141 141 return if self.problem!=nil and self.problem.output_only
142 142
143 143 if self.language == nil
144 144 errors.add('source',"Cannot detect language. Did you submit a correct source file?")
145 145 end
146 146 end
147 147
148 148 def must_have_valid_problem
149 149 return if self.source==nil
150 150 if self.problem==nil
151 151 errors.add('problem',"must be specified.")
152 152 else
153 153 #admin always have right
154 154 return if self.user.admin?
155 155
156 156 #check if user has the right to submit the problem
157 - errors.add('problem',"must be valid.") if (!self.user.available_problems.include?(self.problem)) and (self.new_record?)
157 + errors[:base] << "Authorization error: you have no right to submit to this problem" if (!self.user.available_problems.include?(self.problem)) and (self.new_record?)
158 158 end
159 159 end
160 160
161 161 # callbacks
162 162 def assign_latest_number_if_new_recond
163 163 return if !self.new_record?
164 164 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
165 165 self.number = (latest==nil) ? 1 : latest.number + 1;
166 166 end
167 167
168 168 end
@@ -223,195 +223,198
223 223 end
224 224 end
225 225
226 226 def contest_started?
227 227 if GraderConfiguration.indv_contest_mode?
228 228 stat = self.contest_stat
229 229 return ((stat != nil) and (stat.started_at != nil))
230 230 elsif GraderConfiguration.contest_mode?
231 231 return true if site==nil
232 232 return site.started
233 233 else
234 234 return true
235 235 end
236 236 end
237 237
238 238 def update_start_time
239 239 stat = self.contest_stat
240 240 if stat.nil? or stat.started_at.nil?
241 241 stat ||= UserContestStat.new(:user => self)
242 242 stat.started_at = Time.now.gmtime
243 243 stat.save
244 244 end
245 245 end
246 246
247 247 def problem_in_user_contests?(problem)
248 248 problem_contests = problem.contests.all
249 249
250 250 if problem_contests.length == 0 # this is public contest
251 251 return true
252 252 end
253 253
254 254 contests.each do |contest|
255 255 if problem_contests.find {|c| c.id == contest.id }
256 256 return true
257 257 end
258 258 end
259 259 return false
260 260 end
261 261
262 262 def available_problems_group_by_contests
263 263 contest_problems = []
264 264 pin = {}
265 265 contests.enabled.each do |contest|
266 266 available_problems = contest.problems.available
267 267 contest_problems << {
268 268 :contest => contest,
269 269 :problems => available_problems
270 270 }
271 271 available_problems.each {|p| pin[p.id] = true}
272 272 end
273 273 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
274 274 contest_problems << {
275 275 :contest => nil,
276 276 :problems => other_avaiable_problems
277 277 }
278 278 return contest_problems
279 279 end
280 280
281 281 def solve_all_available_problems?
282 282 available_problems.each do |p|
283 283 u = self
284 284 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
285 285 return false if !p or !sub or sub.points < p.full_score
286 286 end
287 287 return true
288 288 end
289 289
290 290 #get a list of available problem
291 291 def available_problems
292 292 # first, we check if this is normal mode
293 293 if not GraderConfiguration.multicontests?
294 294
295 295 #if this is a normal mode
296 296 #we show problem based on problem_group, if the config said so
297 297 if GraderConfiguration.use_problem_group?
298 298 return available_problems_in_group
299 299 else
300 300 return Problem.available_problems
301 301 end
302 302 else
303 303 #this is multi contest mode
304 304 contest_problems = []
305 305 pin = {}
306 306 contests.enabled.each do |contest|
307 307 contest.problems.available.each do |problem|
308 308 if not pin.has_key? problem.id
309 309 contest_problems << problem
310 310 end
311 311 pin[problem.id] = true
312 312 end
313 313 end
314 314 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
315 315 return contest_problems + other_avaiable_problems
316 316 end
317 317 end
318 318
319 + # new feature, get list of available problem in all enabled group that the user belongs to
319 320 def available_problems_in_group
320 321 problem = []
321 - self.groups.each do |group|
322 + self.groups.where(enabled: true).each do |group|
322 323 group.problems.where(available: true).each { |p| problem << p }
323 324 end
324 325 problem.uniq!
325 326 if problem
326 327 problem.sort! do |a,b|
327 328 case
328 329 when a.date_added < b.date_added
329 330 1
330 331 when a.date_added > b.date_added
331 332 -1
332 333 else
333 334 a.name <=> b.name
334 335 end
335 336 end
336 337 return problem
337 338 else
338 339 return []
339 340 end
340 341 end
341 342
343 + #check if the user has the right to view that problem
344 + #this also consider group based problem policy
342 345 def can_view_problem?(problem)
343 346 return true if admin?
344 347 return available_problems.include? problem
345 348 end
346 349
347 350 def self.clear_last_login
348 351 User.update_all(:last_ip => nil)
349 352 end
350 353
351 354 protected
352 355 def encrypt_new_password
353 356 return if password.blank?
354 357 self.salt = (10+rand(90)).to_s
355 358 self.hashed_password = User.encrypt(self.password,self.salt)
356 359 end
357 360
358 361 def assign_default_site
359 362 # have to catch error when migrating (because self.site is not available).
360 363 begin
361 364 if self.site==nil
362 365 self.site = Site.find_by_name('default')
363 366 if self.site==nil
364 367 self.site = Site.find(1) # when 'default has be renamed'
365 368 end
366 369 end
367 370 rescue
368 371 end
369 372 end
370 373
371 374 def assign_default_contest
372 375 # have to catch error when migrating (because self.site is not available).
373 376 begin
374 377 if self.contests.length == 0
375 378 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
376 379 if default_contest
377 380 self.contests = [default_contest]
378 381 end
379 382 end
380 383 rescue
381 384 end
382 385 end
383 386
384 387 def password_required?
385 388 self.hashed_password.blank? || !self.password.blank?
386 389 end
387 390
388 391 def self.encrypt(string,salt)
389 392 Digest::SHA1.hexdigest(salt + string)
390 393 end
391 394
392 395 def uniqueness_of_email_from_activated_users
393 396 user = User.activated_users.find_by_email(self.email)
394 397 if user and (user.login != self.login)
395 398 self.errors.add(:base,"Email has already been taken")
396 399 end
397 400 end
398 401
399 402 def enough_time_interval_between_same_email_registrations
400 403 return if !self.new_record?
401 404 return if self.activated
402 405 open_user = User.find_by_email(self.email,
403 406 :order => 'created_at DESC')
404 407 if open_user and open_user.created_at and
405 408 (open_user.created_at > Time.now.gmtime - 5.minutes)
406 409 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
407 410 end
408 411 end
409 412
410 413 def email_validation?
411 414 begin
412 415 return VALIDATE_USER_EMAILS
413 416 rescue
414 417 return false
415 418 end
416 419 end
417 420 end
@@ -1,26 +1,26
1 1
2 2 %tr
3 3 %td{:align => "center"}
4 4 = submission.number
5 5 %td.text-right
6 6 = link_to "##{submission.id}", submission_path(submission.id)
7 7 %td
8 8 = l submission.submitted_at, format: :long
9 9 = "( #{time_ago_in_words(submission.submitted_at)} ago)"
10 10 %td
11 11 = submission.source_filename
12 12 = " (#{submission.language.pretty_name}) "
13 - = link_to('[load]',{:action => 'source', :id => submission.id})
13 + = link_to '[load]', download_submission_path(submission)
14 14 %td
15 15 - if submission.graded_at
16 16 = "Graded at #{format_short_time(submission.graded_at)}."
17 17 %br/
18 18 = "Score: #{(submission.points*100/submission.problem.full_score).to_i} " if GraderConfiguration['ui.show_score']
19 19 = " ["
20 20 %tt
21 21 = submission.grader_comment
22 22 = "]"
23 23 %td
24 24 = render :partial => 'compiler_message', :locals => {:compiler_message => submission.compiler_message }
25 25 %td
26 26 = link_to 'Edit', edit_submission_path(submission.id), class: 'btn btn-success'
@@ -1,16 +1,27
1 1 = form_for @group do |f|
2 2 - if @group.errors.any?
3 3 #error_explanation
4 4 %h2= "#{pluralize(@group.errors.count, "error")} prohibited this group from being saved:"
5 5 %ul
6 6 - @group.errors.full_messages.each do |msg|
7 7 %li= msg
8 -
8 + .row
9 + .col-md-6
9 10 .form-group.field
10 11 = f.label :name
11 12 = f.text_field :name, class: 'form-control'
13 + .row
14 + .col-md-6
12 15 .form-group.field
13 16 = f.label :description
14 17 = f.text_field :description, class: 'form-control'
18 + .row
19 + .col-md-6
20 + .checkbox
21 + = f.label :enabled do
22 + = f.check_box :enabled
23 + Enabled
24 + .row
25 + .col-md-6
15 26 .form-group.actions
16 27 = f.submit 'Save', class: 'btn btn-primary'
@@ -1,22 +1,24
1 1 %h1 Groups
2 2
3 3 %p
4 4 = link_to 'New Group', new_group_path, class: 'btn btn-primary'
5 5 %table.table.table-hover
6 6 %thead
7 7 %tr
8 8 %th Name
9 9 %th Description
10 + %th Enabled?
10 11 %th
11 12 %th
12 13
13 14 %tbody
14 15 - @groups.each do |group|
15 - %tr
16 + %tr{:class => "#{(group.enabled?) ? "success" : "danger"}", id: "group-#{group.id}"}
16 17 %td= group.name
17 18 %td= group.description
19 + %td= toggle_button(group.enabled?, toggle_group_path(group), "group-enabled-#{group.id}", size: ' ', block: ' ')
18 20 %td= link_to 'View', group, class: 'btn btn-default'
19 21 %td= link_to 'Destroy', group, :method => :delete, :data => { :confirm => 'Are you sure?' }, class: 'btn btn-danger'
20 22
21 23 %br
22 24
@@ -1,47 +1,47
1 1 = user_title_bar(@user)
2 2
3 3 .announcementbox
4 4 %span{:class => 'title'}
5 5 =raw t 'help.how_to_submit'
6 6 .announcement
7 7 %p
8 8 =raw t 'help.must_specify_language'
9 9
10 10 %p
11 11 =raw t 'help.list_available_language'
12 12
13 13 %table{:border => '1'}
14 14 %tr
15 15 %th{:width => '150px'} C
16 16 %th{:width => '150px'} C++
17 17 %th{:width => '150px'} Pascal
18 18 %tr
19 19 %td=raw "<tt>/*<br/>LANG: C<br/>*/</tt>"
20 20 %td=raw "<tt>/*<br/>LANG: C++<br/>*/</tt>"
21 21 %td=raw "<tt>{<br/>LANG: Pascal<br/>}</tt>"
22 22
23 23 %p
24 24 =raw t 'help.accept_only_language_specified'
25 25
26 26 %p
27 27 =raw t 'help.specifying_task'
28 28
29 29 %p
30 30 =raw t 'help.example_cpp'
31 31
32 32 %table{:border => '1'}
33 33 %tr
34 34 %td{:width => '300px'}
35 35 %tt <tt>/*<br/>LANG: C++<br/>TASK: mobiles<br/>*/</tt>
36 36
37 37 %p
38 38 =raw t 'help.example_pas'
39 39
40 40 %table{:border => '1'}
41 41 %tr
42 42 %td{:width => '300px'}
43 43 %tt <tt>{<br/>LANG: Pascal<br/>TASK: mobiles<br/>}</tt>
44 44
45 45 %p
46 - = raw(t('help.ask_questions_at_messages',:message_link_name => (t 'menu.messages'),:url => url_for(:controller => 'messages', :action => 'list')))
46 + = raw(t('help.ask_questions_at_messages',:message_link_name => (t 'menu.messages'),url: messages_path ))
47 47
@@ -1,112 +1,113
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 @submission.user
34 34 = link_to "#{@submission.user.login}", stat_user_path(@submission.user)
35 35 = @submission.user.full_name
36 36 - else
37 37 = "(n/a)"
38 38 %tr
39 39 %td.text-right
40 40 %strong Task
41 41 %td
42 42 - if @submission.problem!=nil
43 43 = link_to "[#{@submission.problem.name}]", stat_problem_path(@submission.problem)
44 44 = @submission.problem.full_name
45 + = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @submission.problem
45 46 - else
46 47 = "(n/a)"
47 48 %tr
48 49 %td.text-right
49 50 %strong Tries
50 51 %td= @submission.number
51 52 %tr
52 53 %td.text-right
53 54 %strong Language
54 55 %td= @submission.language.pretty_name
55 56 %tr
56 57 %td.text-right
57 58 %strong Submitted
58 59 %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
59 60 %tr
60 61 %td.text-right
61 62 %strong Graded
62 63 - if @submission.graded_at
63 64 %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
64 65 - else
65 66 %td -
66 67 %tr
67 68 %td.text-right
68 69 %strong Points
69 70 %td #{@submission.points}/#{@submission.try(:problem).try(:full_score)}
70 71 %tr
71 72 %td.text-right
72 73 %strong Comment
73 74 %td #{@submission.grader_comment}
74 75 %tr
75 76 %td.text-right
76 77 %strong Runtime (s)
77 78 %td #{@submission.max_runtime}
78 79 %tr
79 80 %td.text-right
80 81 %strong Memory (kb)
81 82 %td #{@submission.peak_memory}
82 83 %tr
83 84 %td.text-right
84 85 %strong Compiler result
85 86 %td
86 87 %button.btn.btn-info.btn-xs{type: 'button', data: {toggle: 'modal', target: '#compiler'}}
87 88 view
88 89 - if session[:admin]
89 90 %tr
90 91 %td.text-right
91 92 %strong IP
92 93 %td #{@submission.ip_address}
93 94 %tr
94 95 %td.text-right
95 96 %strong Grading Task Status
96 97 %td
97 98 = @task.status_str if @task
98 99 - if session[:admin]
99 100 = link_to "rejudge", rejudge_submission_path, data: {remote: true}, class: 'btn btn-info btn-xs'
100 101
101 102
102 103 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
103 104 .modal-dialog.modal-lg{role:'document'}
104 105 .modal-content
105 106 .modal-header
106 107 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
107 108 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
108 109 %h4 Compiler message
109 110 .modal-body
110 111 %pre#compiler_msg= @submission.compiler_message
111 112 .modal-footer
112 113 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
@@ -1,45 +1,54
1 1 .container-fluid
2 2 .row
3 3 .col-md-6
4 4 %h1 Adding list of users
5 5 .row
6 6 .col-md-6
7 7 .panel.panel-default
8 8 .panel-heading
9 9 .panel-title Info
10 10 .panel-body
11 11 %ul
12 12 %li
13 13 List of user information in this format:
14 14 %tt user_id,name(,passwd(,alias(,remark)))
15 15 %li
16 16 Note that
17 17 %tt passwd, alias
18 18 and
19 19 %tt remark
20 20 is optional.
21 21 %li
22 22 When
23 23 %tt passwd
24 24 or
25 25 %tt alias
26 26 is empty, the original value will be used instead.
27 27 %li
28 28 If the users with the same user_id already exists, existing information will be overwritten.
29 + Example:
30 + %ol
31 + %li
32 + %pre user1,Somchai Jaidee
33 + will create (or update) a user with login "user1" and setting the fullname to "Somchai Jaidee", also setting a random password.
34 + %li
35 + %pre user1,Somchai Jaidee,
36 + will create (or update) a user with login "user1" and and setting the fullname "Somchai Jaidee". No change is made to the password unless this is a new user. If this is a new user, a random password will be generated.
37 +
29 38
30 39 .row
31 40 .col-md-6
32 41 = form_tag :action => 'create_from_list' do
33 42 .form-group
34 43 = submit_tag 'Create following users',class: 'btn btn-success'
35 44 .form-group
36 45 .div.checkbox
37 46 %label
38 47 = check_box_tag :add_to_group
39 48 Also add these users to the following group
40 49 = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
41 50 .form-group
42 51 = text_area_tag 'user_list', nil, :rows => 50, :cols => 80
43 52 .col-md-6
44 53
45 54
@@ -1,204 +1,204
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 + get 'toggle'
57 58 end
58 59 collection do
59 60
60 61 end
61 62 end
62 63
63 64 resources :testcases, only: [] do
64 65 member do
65 66 get 'download_input'
66 67 get 'download_sol'
67 68 end
68 69 collection do
69 70 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
70 71 end
71 72 end
72 73
73 74 resources :grader_configuration, controller: 'configurations' do
74 75 collection do
75 76 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
76 77 end
77 78 end
78 79
79 80 resources :users do
80 81 member do
81 82 get 'toggle_activate', 'toggle_enable'
82 83 get 'stat'
83 84 end
84 85 collection do
85 86 get 'profile'
86 87 post 'chg_passwd'
87 88 end
88 89 end
89 90
90 91 resources :submissions do
91 92 member do
92 93 get 'download'
93 94 get 'compiler_msg'
94 95 get 'rejudge'
95 - get 'source'
96 96 end
97 97 collection do
98 98 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
99 99 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
100 100 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
101 101 end
102 102 end
103 103
104 104
105 105 #user admin
106 106 resources :user_admin do
107 107 collection do
108 108 match 'bulk_manage', via: [:get, :post]
109 109 get 'bulk_mail'
110 110 get 'user_stat'
111 111 get 'import'
112 112 get 'new_list'
113 113 get 'admin'
114 114 get 'active'
115 115 get 'mass_mailing'
116 116 get 'revoke_admin'
117 117 post 'grant_admin'
118 118 match 'create_from_list', via: [:get, :post]
119 119 match 'random_all_passwords', via: [:get, :post]
120 120 end
121 121 member do
122 122 get 'clear_last_ip'
123 123 end
124 124 end
125 125
126 126 resources :contest_management, only: [:index] do
127 127 collection do
128 128 get 'user_stat'
129 129 get 'clear_stat'
130 130 get 'clear_all_stat'
131 131 get 'change_contest_mode'
132 132 end
133 133 end
134 134
135 135 #get 'user_admin', to: 'user_admin#index'
136 136 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
137 137 #post 'user_admin', to: 'user_admin#create'
138 138 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
139 139
140 140 #singular resource
141 141 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
142 142 #report
143 143 resource :report, only: [], controller: 'report' do
144 144 get 'login'
145 145 get 'multiple_login'
146 146 get 'problem_hof(/:id)', action: 'problem_hof', as: 'problem_hof'
147 147 get 'current_score(/:group_id)', action: 'current_score', as: 'current_score'
148 148 get 'max_score'
149 149 post 'show_max_score'
150 150 get 'stuck'
151 151 get 'cheat_report'
152 152 post 'cheat_report'
153 153 get 'cheat_scruntinize'
154 154 post 'cheat_scruntinize'
155 155 end
156 156 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
157 157 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
158 158 #get "report/login"
159 159 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
160 160 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
161 161
162 162 resource :main, only: [], controller: 'main' do
163 163 get 'login'
164 164 get 'logout'
165 165 get 'list'
166 166 get 'submission(/:id)', action: 'submission', as: 'main_submission'
167 167 get 'announcements'
168 168 get 'help'
169 169 post 'submit'
170 170 end
171 171 #main
172 172 #get "main/list"
173 173 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
174 174 #post 'main/submit', to: 'main#submit'
175 175 #get 'main/announcements', to: 'main#announcements'
176 176
177 177
178 178 #
179 179 get 'tasks/view/:file.:ext' => 'tasks#view'
180 - get 'tasks/download/:id/:file.:ext' => 'tasks#download'
180 + get 'tasks/download/:id/:file.:ext' => 'tasks#download', as: 'download_task'
181 181 get 'heartbeat/:id/edit' => 'heartbeat#edit'
182 182
183 183 #grader
184 184 get 'graders/list', to: 'graders#list', as: 'grader_list'
185 185 namespace :graders do
186 186 get 'task/:id/:type', action: 'task', as: 'task'
187 187 get 'view/:id/:type', action: 'view', as: 'view'
188 188 get 'clear/:id', action: 'clear', as: 'clear'
189 189 get 'stop'
190 190 get 'stop_all'
191 191 get 'clear_all'
192 192 get 'clear_terminated'
193 193 get 'start_grading'
194 194 get 'start_exam'
195 195
196 196 end
197 197
198 198
199 199 # See how all your routes lay out with "rake routes"
200 200
201 201 # This is a legacy wild controller route that's not recommended for RESTful applications.
202 202 # Note: This route will make all actions in every controller accessible via GET requests.
203 203 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
204 204 end
@@ -1,307 +1,308
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 5 # Note that this schema.rb definition is the authoritative source for your
6 6 # database schema. If you need to create the application database on another
7 7 # system, you should be using db:schema:load, not running all the migrations
8 8 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 9 # you'll amass, the slower it'll run and the greater likelihood for issues).
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: 2018_06_12_102327) do
13 + ActiveRecord::Schema.define(version: 2020_08_13_083020) do
14 14
15 - create_table "announcements", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
15 + create_table "announcements", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
16 16 t.string "author"
17 17 t.text "body"
18 18 t.boolean "published"
19 - t.datetime "created_at"
20 - t.datetime "updated_at"
19 + t.datetime "created_at", null: false
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 end
26 26
27 - create_table "contests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
27 + create_table "contests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
28 28 t.string "title"
29 29 t.boolean "enabled"
30 - t.datetime "created_at"
31 - t.datetime "updated_at"
30 + t.datetime "created_at", null: false
31 + t.datetime "updated_at", null: false
32 32 t.string "name"
33 33 end
34 34
35 - create_table "contests_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
35 + create_table "contests_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
36 36 t.integer "contest_id"
37 37 t.integer "problem_id"
38 38 end
39 39
40 - create_table "contests_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
40 + create_table "contests_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
41 41 t.integer "contest_id"
42 42 t.integer "user_id"
43 43 end
44 44
45 - create_table "countries", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
45 + create_table "countries", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
46 46 t.string "name"
47 - t.datetime "created_at"
48 - t.datetime "updated_at"
47 + t.datetime "created_at", null: false
48 + t.datetime "updated_at", null: false
49 49 end
50 50
51 - create_table "descriptions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
51 + create_table "descriptions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
52 52 t.text "body"
53 53 t.boolean "markdowned"
54 - t.datetime "created_at"
55 - t.datetime "updated_at"
54 + t.datetime "created_at", null: false
55 + t.datetime "updated_at", null: false
56 56 end
57 57
58 - create_table "grader_configurations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
58 + create_table "grader_configurations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
59 59 t.string "key"
60 60 t.string "value_type"
61 61 t.string "value"
62 - t.datetime "created_at"
63 - t.datetime "updated_at"
62 + t.datetime "created_at", null: false
63 + t.datetime "updated_at", null: false
64 64 t.text "description"
65 65 end
66 66
67 - create_table "grader_processes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
67 + create_table "grader_processes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
68 68 t.string "host"
69 69 t.integer "pid"
70 70 t.string "mode"
71 71 t.boolean "active"
72 - t.datetime "created_at"
73 - t.datetime "updated_at"
72 + t.datetime "created_at", null: false
73 + t.datetime "updated_at", null: false
74 74 t.integer "task_id"
75 75 t.string "task_type"
76 76 t.boolean "terminated"
77 - t.index ["host", "pid"], name: "index_grader_processes_on_host_and_pid"
77 + t.index ["host", "pid"], name: "index_grader_processes_on_ip_and_pid"
78 78 end
79 79
80 80 create_table "groups", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
81 81 t.string "name"
82 82 t.string "description"
83 + t.boolean "enabled", default: true
83 84 end
84 85
85 86 create_table "groups_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
86 87 t.integer "problem_id", null: false
87 88 t.integer "group_id", null: false
88 89 t.index ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id"
89 90 end
90 91
91 92 create_table "groups_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
92 93 t.integer "group_id", null: false
93 94 t.integer "user_id", null: false
94 95 t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
95 96 end
96 97
97 - create_table "heart_beats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
98 + create_table "heart_beats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
98 99 t.integer "user_id"
99 100 t.string "ip_address"
100 - t.datetime "created_at"
101 - t.datetime "updated_at"
101 + t.datetime "created_at", null: false
102 + t.datetime "updated_at", null: false
102 103 t.string "status"
103 104 t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
104 105 end
105 106
106 - create_table "languages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
107 + create_table "languages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
107 108 t.string "name", limit: 10
108 109 t.string "pretty_name"
109 110 t.string "ext", limit: 10
110 111 t.string "common_ext"
111 112 end
112 113
113 - create_table "logins", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
114 + create_table "logins", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
114 115 t.integer "user_id"
115 116 t.string "ip_address"
116 - t.datetime "created_at"
117 - t.datetime "updated_at"
117 + t.datetime "created_at", null: false
118 + t.datetime "updated_at", null: false
118 119 end
119 120
120 - create_table "messages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
121 + create_table "messages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
121 122 t.integer "sender_id"
122 123 t.integer "receiver_id"
123 124 t.integer "replying_message_id"
124 125 t.text "body"
125 126 t.boolean "replied"
126 - t.datetime "created_at"
127 - t.datetime "updated_at"
127 + t.datetime "created_at", null: false
128 + t.datetime "updated_at", null: false
128 129 end
129 130
130 - create_table "problems", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
131 + create_table "problems", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
131 132 t.string "name", limit: 30
132 133 t.string "full_name"
133 134 t.integer "full_score"
134 135 t.date "date_added"
135 136 t.boolean "available"
136 137 t.string "url"
137 138 t.integer "description_id"
138 139 t.boolean "test_allowed"
139 140 t.boolean "output_only"
140 141 t.string "description_filename"
141 142 t.boolean "view_testcase"
142 143 end
143 144
144 145 create_table "problems_tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
145 146 t.integer "problem_id"
146 147 t.integer "tag_id"
147 148 t.index ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true
148 149 t.index ["problem_id"], name: "index_problems_tags_on_problem_id"
149 150 t.index ["tag_id"], name: "index_problems_tags_on_tag_id"
150 151 end
151 152
152 - create_table "rights", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
153 + create_table "rights", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
153 154 t.string "name"
154 155 t.string "controller"
155 156 t.string "action"
156 157 end
157 158
158 - create_table "rights_roles", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
159 + create_table "rights_roles", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
159 160 t.integer "right_id"
160 161 t.integer "role_id"
161 162 t.index ["role_id"], name: "index_rights_roles_on_role_id"
162 163 end
163 164
164 - create_table "roles", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
165 + create_table "roles", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
165 166 t.string "name"
166 167 end
167 168
168 - create_table "roles_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
169 + create_table "roles_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
169 170 t.integer "role_id"
170 171 t.integer "user_id"
171 172 t.index ["user_id"], name: "index_roles_users_on_user_id"
172 173 end
173 174
174 - create_table "sessions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
175 + create_table "sessions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
175 176 t.string "session_id"
176 177 t.text "data"
177 178 t.datetime "updated_at"
178 179 t.index ["session_id"], name: "index_sessions_on_session_id"
179 180 t.index ["updated_at"], name: "index_sessions_on_updated_at"
180 181 end
181 182
182 - create_table "sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
183 + create_table "sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
183 184 t.string "name"
184 185 t.boolean "started"
185 186 t.datetime "start_time"
186 - t.datetime "created_at"
187 - t.datetime "updated_at"
187 + t.datetime "created_at", null: false
188 + t.datetime "updated_at", null: false
188 189 t.integer "country_id"
189 190 t.string "password"
190 191 end
191 192
192 - create_table "submission_view_logs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
193 + create_table "submission_view_logs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
193 194 t.integer "user_id"
194 195 t.integer "submission_id"
195 - t.datetime "created_at"
196 - t.datetime "updated_at"
196 + t.datetime "created_at", null: false
197 + t.datetime "updated_at", null: false
197 198 end
198 199
199 - create_table "submissions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
200 + create_table "submissions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
200 201 t.integer "user_id"
201 202 t.integer "problem_id"
202 203 t.integer "language_id"
203 204 t.text "source", limit: 16777215
204 205 t.binary "binary"
205 206 t.datetime "submitted_at"
206 207 t.datetime "compiled_at"
207 208 t.text "compiler_message"
208 209 t.datetime "graded_at"
209 210 t.integer "points"
210 211 t.text "grader_comment"
211 212 t.integer "number"
212 213 t.string "source_filename"
213 214 t.float "max_runtime"
214 215 t.integer "peak_memory"
215 216 t.integer "effective_code_length"
216 217 t.string "ip_address"
217 218 t.index ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true
218 219 t.index ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id"
219 220 end
220 221
221 222 create_table "tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
222 223 t.string "name", null: false
223 224 t.text "description"
224 225 t.boolean "public"
225 226 t.datetime "created_at", null: false
226 227 t.datetime "updated_at", null: false
227 228 end
228 229
229 - create_table "tasks", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
230 + create_table "tasks", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
230 231 t.integer "submission_id"
231 232 t.datetime "created_at"
232 233 t.integer "status"
233 234 t.datetime "updated_at"
234 235 t.index ["submission_id"], name: "index_tasks_on_submission_id"
235 236 end
236 237
237 - create_table "test_pairs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
238 + create_table "test_pairs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
238 239 t.integer "problem_id"
239 240 t.text "input", limit: 16777215
240 241 t.text "solution", limit: 16777215
241 - t.datetime "created_at"
242 - t.datetime "updated_at"
242 + t.datetime "created_at", null: false
243 + t.datetime "updated_at", null: false
243 244 end
244 245
245 - create_table "test_requests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
246 + create_table "test_requests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
246 247 t.integer "user_id"
247 248 t.integer "problem_id"
248 249 t.integer "submission_id"
249 250 t.string "input_file_name"
250 251 t.string "output_file_name"
251 252 t.string "running_stat"
252 253 t.integer "status"
253 - t.datetime "updated_at"
254 + t.datetime "updated_at", null: false
254 255 t.datetime "submitted_at"
255 256 t.datetime "compiled_at"
256 257 t.text "compiler_message"
257 258 t.datetime "graded_at"
258 259 t.string "grader_comment"
259 - t.datetime "created_at"
260 + t.datetime "created_at", null: false
260 261 t.float "running_time"
261 262 t.string "exit_status"
262 263 t.integer "memory_usage"
263 264 t.index ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id"
264 265 end
265 266
266 267 create_table "testcases", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
267 268 t.integer "problem_id"
268 269 t.integer "num"
269 270 t.integer "group"
270 271 t.integer "score"
271 272 t.text "input", limit: 4294967295
272 273 t.text "sol", limit: 4294967295
273 274 t.datetime "created_at"
274 275 t.datetime "updated_at"
275 276 t.index ["problem_id"], name: "index_testcases_on_problem_id"
276 277 end
277 278
278 - create_table "user_contest_stats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
279 + create_table "user_contest_stats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
279 280 t.integer "user_id"
280 281 t.datetime "started_at"
281 - t.datetime "created_at"
282 - t.datetime "updated_at"
282 + t.datetime "created_at", null: false
283 + t.datetime "updated_at", null: false
283 284 t.boolean "forced_logout"
284 285 end
285 286
286 - create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
287 + create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
287 288 t.string "login", limit: 50
288 289 t.string "full_name"
289 290 t.string "hashed_password"
290 291 t.string "salt", limit: 5
291 292 t.string "alias"
292 293 t.string "email"
293 294 t.integer "site_id"
294 295 t.integer "country_id"
295 296 t.boolean "activated", default: false
296 297 t.datetime "created_at"
297 298 t.datetime "updated_at"
298 - t.string "section"
299 299 t.boolean "enabled", default: true
300 300 t.string "remark"
301 301 t.string "last_ip"
302 + t.string "section"
302 303 t.index ["login"], name: "index_users_on_login", unique: true
303 304 end
304 305
305 306 add_foreign_key "problems_tags", "problems"
306 307 add_foreign_key "problems_tags", "tags"
307 308 end
You need to be logged in to leave comments. Login now