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

r779:df983f8fc960 - - 8 files changed: 73 inserted, 10 deleted

@@ -0,0 +1,33
1 + # Authentication and user imports through programming.in.th web request
2 + require 'net/http'
3 + require 'uri'
4 + require 'json'
5 +
6 + class ProgrammingAuthenticator
7 + PROGRAMMING_AUTHEN_URL = "https://programming.in.th/authen.php"
8 +
9 + def find_or_create_user(result)
10 + user = User.find_by(login: result['username'])
11 + if not user
12 + user = User.new(login: result['username'],
13 + full_name: result['firstname'] + ' ' + result['surname'],
14 + alias: result['display'],
15 + email: result['email'])
16 + user.password = User.random_password
17 + user.save
18 + end
19 + return user
20 + end
21 +
22 + def authenticate(login, password)
23 + uri = URI(PROGRAMMING_AUTHEN_URL)
24 + result = Net::HTTP.post_form(uri, 'username' => login, 'password' => password)
25 + request_result = JSON.parse(result.body)
26 +
27 + if request_result.fetch('status', 'incorrect') == 'OK'
28 + return find_or_create_user(request_result)
29 + else
30 + return nil
31 + end
32 + end
33 + end
@@ -1,67 +1,89
1 class LoginController < ApplicationController
1 class LoginController < ApplicationController
2
2
3 + @@authenticators = []
4 +
3 def index
5 def index
4 # show login screen
6 # show login screen
5 reset_session
7 reset_session
6 redirect_to :controller => 'main', :action => 'login'
8 redirect_to :controller => 'main', :action => 'login'
7 end
9 end
8
10
9 def login
11 def login
10 - user = User.authenticate(params[:login], params[:password])
12 + user = get_authenticated_user(params[:login], params[:password])
11 unless user
13 unless user
12 flash[:notice] = 'Wrong password'
14 flash[:notice] = 'Wrong password'
13 redirect_to :controller => 'main', :action => 'login'
15 redirect_to :controller => 'main', :action => 'login'
14 return
16 return
15 end
17 end
16
18
17 if (!GraderConfiguration['right.bypass_agreement']) and (!params[:accept_agree]) and !user.admin?
19 if (!GraderConfiguration['right.bypass_agreement']) and (!params[:accept_agree]) and !user.admin?
18 flash[:notice] = 'You must accept the agreement before logging in'
20 flash[:notice] = 'You must accept the agreement before logging in'
19 redirect_to :controller => 'main', :action => 'login'
21 redirect_to :controller => 'main', :action => 'login'
20 return
22 return
21 end
23 end
22
24
23 #process logging in
25 #process logging in
24 session[:user_id] = user.id
26 session[:user_id] = user.id
25 session[:admin] = user.admin?
27 session[:admin] = user.admin?
26
28
27 # clear forced logout flag for multicontests contest change
29 # clear forced logout flag for multicontests contest change
28 if GraderConfiguration.multicontests?
30 if GraderConfiguration.multicontests?
29 contest_stat = user.contest_stat
31 contest_stat = user.contest_stat
30 if contest_stat.respond_to? :forced_logout
32 if contest_stat.respond_to? :forced_logout
31 if contest_stat.forced_logout
33 if contest_stat.forced_logout
32 contest_stat.forced_logout = false
34 contest_stat.forced_logout = false
33 contest_stat.save
35 contest_stat.save
34 end
36 end
35 end
37 end
36 end
38 end
37
39
38 #save login information
40 #save login information
39 Login.create(user_id: user.id, ip_address: request.remote_ip)
41 Login.create(user_id: user.id, ip_address: request.remote_ip)
40
42
41 redirect_to :controller => 'main', :action => 'list'
43 redirect_to :controller => 'main', :action => 'list'
42 end
44 end
43
45
44 def site_login
46 def site_login
45 begin
47 begin
46 site = Site.find(params[:login][:site_id])
48 site = Site.find(params[:login][:site_id])
47 rescue ActiveRecord::RecordNotFound
49 rescue ActiveRecord::RecordNotFound
48 site = nil
50 site = nil
49 end
51 end
50 if site==nil
52 if site==nil
51 flash[:notice] = 'Wrong site'
53 flash[:notice] = 'Wrong site'
52 redirect_to :controller => 'main', :action => 'login' and return
54 redirect_to :controller => 'main', :action => 'login' and return
53 end
55 end
54 if (site.password) and (site.password == params[:login][:password])
56 if (site.password) and (site.password == params[:login][:password])
55 session[:site_id] = site.id
57 session[:site_id] = site.id
56 redirect_to :controller => 'site', :action => 'index'
58 redirect_to :controller => 'site', :action => 'index'
57 else
59 else
58 flash[:notice] = 'Wrong site password'
60 flash[:notice] = 'Wrong site password'
59 redirect_to :controller => 'site', :action => 'login'
61 redirect_to :controller => 'site', :action => 'login'
60 end
62 end
61 end
63 end
62
64
63 def logout
65 def logout
64 redirect_to root_path
66 redirect_to root_path
65 end
67 end
66
68
69 + def self.add_authenticator(authenticator)
70 + @@authenticators << authenticator
67 end
71 end
72 +
73 + protected
74 +
75 + def get_authenticated_user(login, password)
76 + if @@authenticators.empty?
77 + return User.authenticate(login, password)
78 + else
79 + user = User.authenticate(login, password)
80 + @@authenticators.each do |authenticator|
81 + if not user
82 + user = authenticator.authenticate(login, password)
83 + end
84 + end
85 + return user
86 + end
87 + end
88 +
89 + end
@@ -1,311 +1,307
1 class ProblemsController < ApplicationController
1 class ProblemsController < ApplicationController
2
2
3 before_action :admin_authorization
3 before_action :admin_authorization
4
4
5 - #NOTE: ghost from the past?
6 - #before_action :testcase_authorization, only: [:show_testcase]
7 -
8 -
9 in_place_edit_for :problem, :name
5 in_place_edit_for :problem, :name
10 in_place_edit_for :problem, :full_name
6 in_place_edit_for :problem, :full_name
11 in_place_edit_for :problem, :full_score
7 in_place_edit_for :problem, :full_score
12
8
13 def index
9 def index
14 @problems = Problem.order(date_added: :desc)
10 @problems = Problem.order(date_added: :desc)
15 end
11 end
16
12
17
13
18 def show
14 def show
19 @problem = Problem.find(params[:id])
15 @problem = Problem.find(params[:id])
20 end
16 end
21
17
22 def new
18 def new
23 @problem = Problem.new
19 @problem = Problem.new
24 @description = nil
20 @description = nil
25 end
21 end
26
22
27 def create
23 def create
28 @problem = Problem.new(problem_params)
24 @problem = Problem.new(problem_params)
29 - @description = Description.new(problem_params[:description])
25 + @description = Description.new(description_params)
30 if @description.body!=''
26 if @description.body!=''
31 if !@description.save
27 if !@description.save
32 render :action => new and return
28 render :action => new and return
33 end
29 end
34 else
30 else
35 @description = nil
31 @description = nil
36 end
32 end
37 @problem.description = @description
33 @problem.description = @description
38 if @problem.save
34 if @problem.save
39 flash[:notice] = 'Problem was successfully created.'
35 flash[:notice] = 'Problem was successfully created.'
40 redirect_to action: :index
36 redirect_to action: :index
41 else
37 else
42 render :action => 'new'
38 render :action => 'new'
43 end
39 end
44 end
40 end
45
41
46 def quick_create
42 def quick_create
47 @problem = Problem.new(problem_params)
43 @problem = Problem.new(problem_params)
48 @problem.full_name = @problem.name if @problem.full_name == ''
44 @problem.full_name = @problem.name if @problem.full_name == ''
49 @problem.full_score = 100
45 @problem.full_score = 100
50 @problem.available = false
46 @problem.available = false
51 @problem.test_allowed = true
47 @problem.test_allowed = true
52 @problem.output_only = false
48 @problem.output_only = false
53 @problem.date_added = Time.new
49 @problem.date_added = Time.new
54 if @problem.save
50 if @problem.save
55 flash[:notice] = 'Problem was successfully created.'
51 flash[:notice] = 'Problem was successfully created.'
56 redirect_to action: :index
52 redirect_to action: :index
57 else
53 else
58 flash[:notice] = 'Error saving problem'
54 flash[:notice] = 'Error saving problem'
59 redirect_to action: :index
55 redirect_to action: :index
60 end
56 end
61 end
57 end
62
58
63 def edit
59 def edit
64 @problem = Problem.find(params[:id])
60 @problem = Problem.find(params[:id])
65 @description = @problem.description
61 @description = @problem.description
66 end
62 end
67
63
68 def update
64 def update
69 @problem = Problem.find(params[:id])
65 @problem = Problem.find(params[:id])
70 @description = @problem.description
66 @description = @problem.description
71 if @description.nil? and params[:description][:body]!=''
67 if @description.nil? and params[:description][:body]!=''
72 @description = Description.new(description_params)
68 @description = Description.new(description_params)
73 if !@description.save
69 if !@description.save
74 flash[:notice] = 'Error saving description'
70 flash[:notice] = 'Error saving description'
75 render :action => 'edit' and return
71 render :action => 'edit' and return
76 end
72 end
77 @problem.description = @description
73 @problem.description = @description
78 elsif @description
74 elsif @description
79 if !@description.update_attributes(description_params)
75 if !@description.update_attributes(description_params)
80 flash[:notice] = 'Error saving description'
76 flash[:notice] = 'Error saving description'
81 render :action => 'edit' and return
77 render :action => 'edit' and return
82 end
78 end
83 end
79 end
84 if params[:file] and params[:file].content_type != 'application/pdf'
80 if params[:file] and params[:file].content_type != 'application/pdf'
85 flash[:notice] = 'Error: Uploaded file is not PDF'
81 flash[:notice] = 'Error: Uploaded file is not PDF'
86 render :action => 'edit' and return
82 render :action => 'edit' and return
87 end
83 end
88 if @problem.update_attributes(problem_params)
84 if @problem.update_attributes(problem_params)
89 flash[:notice] = 'Problem was successfully updated.'
85 flash[:notice] = 'Problem was successfully updated.'
90 unless params[:file] == nil or params[:file] == ''
86 unless params[:file] == nil or params[:file] == ''
91 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
87 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
92 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
88 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
93 if not FileTest.exists? out_dirname
89 if not FileTest.exists? out_dirname
94 Dir.mkdir out_dirname
90 Dir.mkdir out_dirname
95 end
91 end
96
92
97 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
93 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
98 if FileTest.exists? out_filename
94 if FileTest.exists? out_filename
99 File.delete out_filename
95 File.delete out_filename
100 end
96 end
101
97
102 File.open(out_filename,"wb") do |file|
98 File.open(out_filename,"wb") do |file|
103 file.write(params[:file].read)
99 file.write(params[:file].read)
104 end
100 end
105 @problem.description_filename = "#{@problem.name}.pdf"
101 @problem.description_filename = "#{@problem.name}.pdf"
106 @problem.save
102 @problem.save
107 end
103 end
108 redirect_to :action => 'show', :id => @problem
104 redirect_to :action => 'show', :id => @problem
109 else
105 else
110 render :action => 'edit'
106 render :action => 'edit'
111 end
107 end
112 end
108 end
113
109
114 def destroy
110 def destroy
115 p = Problem.find(params[:id]).destroy
111 p = Problem.find(params[:id]).destroy
116 redirect_to action: :index
112 redirect_to action: :index
117 end
113 end
118
114
119 def toggle
115 def toggle
120 @problem = Problem.find(params[:id])
116 @problem = Problem.find(params[:id])
121 @problem.update_attributes(available: !(@problem.available) )
117 @problem.update_attributes(available: !(@problem.available) )
122 respond_to do |format|
118 respond_to do |format|
123 format.js { }
119 format.js { }
124 end
120 end
125 end
121 end
126
122
127 def toggle_test
123 def toggle_test
128 @problem = Problem.find(params[:id])
124 @problem = Problem.find(params[:id])
129 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
125 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
130 respond_to do |format|
126 respond_to do |format|
131 format.js { }
127 format.js { }
132 end
128 end
133 end
129 end
134
130
135 def toggle_view_testcase
131 def toggle_view_testcase
136 @problem = Problem.find(params[:id])
132 @problem = Problem.find(params[:id])
137 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
133 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
138 respond_to do |format|
134 respond_to do |format|
139 format.js { }
135 format.js { }
140 end
136 end
141 end
137 end
142
138
143 def turn_all_off
139 def turn_all_off
144 Problem.available.all.each do |problem|
140 Problem.available.all.each do |problem|
145 problem.available = false
141 problem.available = false
146 problem.save
142 problem.save
147 end
143 end
148 redirect_to action: :index
144 redirect_to action: :index
149 end
145 end
150
146
151 def turn_all_on
147 def turn_all_on
152 Problem.where.not(available: true).each do |problem|
148 Problem.where.not(available: true).each do |problem|
153 problem.available = true
149 problem.available = true
154 problem.save
150 problem.save
155 end
151 end
156 redirect_to action: :index
152 redirect_to action: :index
157 end
153 end
158
154
159 def stat
155 def stat
160 @problem = Problem.find(params[:id])
156 @problem = Problem.find(params[:id])
161 unless @problem.available or session[:admin]
157 unless @problem.available or session[:admin]
162 redirect_to :controller => 'main', :action => 'list'
158 redirect_to :controller => 'main', :action => 'list'
163 return
159 return
164 end
160 end
165 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
161 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
166
162
167 #stat summary
163 #stat summary
168 range =65
164 range =65
169 @histogram = { data: Array.new(range,0), summary: {} }
165 @histogram = { data: Array.new(range,0), summary: {} }
170 user = Hash.new(0)
166 user = Hash.new(0)
171 @submissions.find_each do |sub|
167 @submissions.find_each do |sub|
172 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
168 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
173 @histogram[:data][d.to_i] += 1 if d < range
169 @histogram[:data][d.to_i] += 1 if d < range
174 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
170 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
175 end
171 end
176 @histogram[:summary][:max] = [@histogram[:data].max,1].max
172 @histogram[:summary][:max] = [@histogram[:data].max,1].max
177
173
178 @summary = { attempt: user.count, solve: 0 }
174 @summary = { attempt: user.count, solve: 0 }
179 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
175 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
180 end
176 end
181
177
182 def manage
178 def manage
183 @problems = Problem.order(date_added: :desc)
179 @problems = Problem.order(date_added: :desc)
184 end
180 end
185
181
186 def do_manage
182 def do_manage
187 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
183 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
188 change_date_added
184 change_date_added
189 elsif params.has_key? 'add_to_contest'
185 elsif params.has_key? 'add_to_contest'
190 add_to_contest
186 add_to_contest
191 elsif params.has_key? 'enable_problem'
187 elsif params.has_key? 'enable_problem'
192 set_available(true)
188 set_available(true)
193 elsif params.has_key? 'disable_problem'
189 elsif params.has_key? 'disable_problem'
194 set_available(false)
190 set_available(false)
195 elsif params.has_key? 'add_group'
191 elsif params.has_key? 'add_group'
196 group = Group.find(params[:group_id])
192 group = Group.find(params[:group_id])
197 ok = []
193 ok = []
198 failed = []
194 failed = []
199 get_problems_from_params.each do |p|
195 get_problems_from_params.each do |p|
200 begin
196 begin
201 group.problems << p
197 group.problems << p
202 ok << p.full_name
198 ok << p.full_name
203 rescue => e
199 rescue => e
204 failed << p.full_name
200 failed << p.full_name
205 end
201 end
206 end
202 end
207 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
203 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
208 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
204 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
209 elsif params.has_key? 'add_tags'
205 elsif params.has_key? 'add_tags'
210 get_problems_from_params.each do |p|
206 get_problems_from_params.each do |p|
211 p.tag_ids += params[:tag_ids]
207 p.tag_ids += params[:tag_ids]
212 end
208 end
213 end
209 end
214
210
215 redirect_to :action => 'manage'
211 redirect_to :action => 'manage'
216 end
212 end
217
213
218 def import
214 def import
219 @allow_test_pair_import = allow_test_pair_import?
215 @allow_test_pair_import = allow_test_pair_import?
220 end
216 end
221
217
222 def do_import
218 def do_import
223 old_problem = Problem.find_by_name(params[:name])
219 old_problem = Problem.find_by_name(params[:name])
224 if !allow_test_pair_import? and params.has_key? :import_to_db
220 if !allow_test_pair_import? and params.has_key? :import_to_db
225 params.delete :import_to_db
221 params.delete :import_to_db
226 end
222 end
227 @problem, import_log = Problem.create_from_import_form_params(params,
223 @problem, import_log = Problem.create_from_import_form_params(params,
228 old_problem)
224 old_problem)
229
225
230 if !@problem.errors.empty?
226 if !@problem.errors.empty?
231 render :action => 'import' and return
227 render :action => 'import' and return
232 end
228 end
233
229
234 if old_problem!=nil
230 if old_problem!=nil
235 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
231 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
236 end
232 end
237 @log = import_log
233 @log = import_log
238 end
234 end
239
235
240 def remove_contest
236 def remove_contest
241 problem = Problem.find(params[:id])
237 problem = Problem.find(params[:id])
242 contest = Contest.find(params[:contest_id])
238 contest = Contest.find(params[:contest_id])
243 if problem!=nil and contest!=nil
239 if problem!=nil and contest!=nil
244 problem.contests.delete(contest)
240 problem.contests.delete(contest)
245 end
241 end
246 redirect_to :action => 'manage'
242 redirect_to :action => 'manage'
247 end
243 end
248
244
249 ##################################
245 ##################################
250 protected
246 protected
251
247
252 def allow_test_pair_import?
248 def allow_test_pair_import?
253 if defined? ALLOW_TEST_PAIR_IMPORT
249 if defined? ALLOW_TEST_PAIR_IMPORT
254 return ALLOW_TEST_PAIR_IMPORT
250 return ALLOW_TEST_PAIR_IMPORT
255 else
251 else
256 return false
252 return false
257 end
253 end
258 end
254 end
259
255
260 def change_date_added
256 def change_date_added
261 problems = get_problems_from_params
257 problems = get_problems_from_params
262 date = Date.parse(params[:date_added])
258 date = Date.parse(params[:date_added])
263 problems.each do |p|
259 problems.each do |p|
264 p.date_added = date
260 p.date_added = date
265 p.save
261 p.save
266 end
262 end
267 end
263 end
268
264
269 def add_to_contest
265 def add_to_contest
270 problems = get_problems_from_params
266 problems = get_problems_from_params
271 contest = Contest.find(params[:contest][:id])
267 contest = Contest.find(params[:contest][:id])
272 if contest!=nil and contest.enabled
268 if contest!=nil and contest.enabled
273 problems.each do |p|
269 problems.each do |p|
274 p.contests << contest
270 p.contests << contest
275 end
271 end
276 end
272 end
277 end
273 end
278
274
279 def set_available(avail)
275 def set_available(avail)
280 problems = get_problems_from_params
276 problems = get_problems_from_params
281 problems.each do |p|
277 problems.each do |p|
282 p.available = avail
278 p.available = avail
283 p.save
279 p.save
284 end
280 end
285 end
281 end
286
282
287 def get_problems_from_params
283 def get_problems_from_params
288 problems = []
284 problems = []
289 params.keys.each do |k|
285 params.keys.each do |k|
290 if k.index('prob-')==0
286 if k.index('prob-')==0
291 name, id, order = k.split('-')
287 name, id, order = k.split('-')
292 problems << Problem.find(id)
288 problems << Problem.find(id)
293 end
289 end
294 end
290 end
295 problems
291 problems
296 end
292 end
297
293
298 def get_problems_stat
294 def get_problems_stat
299 end
295 end
300
296
301 private
297 private
302
298
303 def problem_params
299 def problem_params
304 params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[])
300 params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[])
305 end
301 end
306
302
307 def description_params
303 def description_params
308 - params.require(:description).permit(:body, :markdown)
304 + params.require(:description).permit(:body, :markdowned)
309 end
305 end
310
306
311 end
307 end
@@ -1,166 +1,168
1 class Submission < ActiveRecord::Base
1 class Submission < ActiveRecord::Base
2
2
3 belongs_to :language
3 belongs_to :language
4 belongs_to :problem
4 belongs_to :problem
5 belongs_to :user
5 belongs_to :user
6
6
7 before_validation :assign_problem
7 before_validation :assign_problem
8 before_validation :assign_language
8 before_validation :assign_language
9
9
10 validates_presence_of :source
10 validates_presence_of :source
11 validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'code too long, the limit is 100,000 bytes'
11 validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'code too long, the limit is 100,000 bytes'
12 validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short'
12 validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short'
13 validate :must_have_valid_problem
13 validate :must_have_valid_problem
14 validate :must_specify_language
14 validate :must_specify_language
15
15
16 has_one :task
16 has_one :task
17
17
18 before_save :assign_latest_number_if_new_recond
18 before_save :assign_latest_number_if_new_recond
19
19
20 def self.find_last_by_user_and_problem(user_id, problem_id)
20 def self.find_last_by_user_and_problem(user_id, problem_id)
21 where("user_id = ? AND problem_id = ?",user_id,problem_id).last
21 where("user_id = ? AND problem_id = ?",user_id,problem_id).last
22 end
22 end
23
23
24 def self.find_all_last_by_problem(problem_id)
24 def self.find_all_last_by_problem(problem_id)
25 # need to put in SQL command, maybe there's a better way
25 # need to put in SQL command, maybe there's a better way
26 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
26 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
27 "WHERE id = " +
27 "WHERE id = " +
28 "(SELECT MAX(id) FROM submissions AS subs " +
28 "(SELECT MAX(id) FROM submissions AS subs " +
29 "WHERE subs.user_id = submissions.user_id AND " +
29 "WHERE subs.user_id = submissions.user_id AND " +
30 "problem_id = " + problem_id.to_s + " " +
30 "problem_id = " + problem_id.to_s + " " +
31 "GROUP BY user_id) " +
31 "GROUP BY user_id) " +
32 "ORDER BY user_id")
32 "ORDER BY user_id")
33 end
33 end
34
34
35 def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id)
35 def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id)
36 records = Submission.where(problem_id: problem_id,user_id: user_id)
36 records = Submission.where(problem_id: problem_id,user_id: user_id)
37 records = records.where('id >= ?',since_id) if since_id and since_id > 0
37 records = records.where('id >= ?',since_id) if since_id and since_id > 0
38 records = records.where('id <= ?',until_id) if until_id and until_id > 0
38 records = records.where('id <= ?',until_id) if until_id and until_id > 0
39 records.all
39 records.all
40 end
40 end
41
41
42 def self.find_last_for_all_available_problems(user_id)
42 def self.find_last_for_all_available_problems(user_id)
43 submissions = Array.new
43 submissions = Array.new
44 problems = Problem.available_problems
44 problems = Problem.available_problems
45 problems.each do |problem|
45 problems.each do |problem|
46 sub = Submission.find_last_by_user_and_problem(user_id, problem.id)
46 sub = Submission.find_last_by_user_and_problem(user_id, problem.id)
47 submissions << sub if sub!=nil
47 submissions << sub if sub!=nil
48 end
48 end
49 submissions
49 submissions
50 end
50 end
51
51
52 def self.find_by_user_problem_number(user_id, problem_id, number)
52 def self.find_by_user_problem_number(user_id, problem_id, number)
53 where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first
53 where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first
54 end
54 end
55
55
56 def self.find_all_by_user_problem(user_id, problem_id)
56 def self.find_all_by_user_problem(user_id, problem_id)
57 where("user_id = ? AND problem_id = ?",user_id,problem_id)
57 where("user_id = ? AND problem_id = ?",user_id,problem_id)
58 end
58 end
59
59
60 def download_filename
60 def download_filename
61 if self.problem.output_only
61 if self.problem.output_only
62 return self.source_filename
62 return self.source_filename
63 else
63 else
64 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
64 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
65 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
65 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
66 end
66 end
67 end
67 end
68
68
69 protected
69 protected
70
70
71 def self.find_option_in_source(option, source)
71 def self.find_option_in_source(option, source)
72 if source==nil
72 if source==nil
73 return nil
73 return nil
74 end
74 end
75 i = 0
75 i = 0
76 source.each_line do |s|
76 source.each_line do |s|
77 if s =~ option
77 if s =~ option
78 words = s.split
78 words = s.split
79 return words[1]
79 return words[1]
80 end
80 end
81 i = i + 1
81 i = i + 1
82 if i==10
82 if i==10
83 return nil
83 return nil
84 end
84 end
85 end
85 end
86 return nil
86 return nil
87 end
87 end
88
88
89 def self.find_language_in_source(source, source_filename="")
89 def self.find_language_in_source(source, source_filename="")
90 langopt = find_option_in_source(/^LANG:/,source)
90 langopt = find_option_in_source(/^LANG:/,source)
91 if langopt
91 if langopt
92 return (Language.find_by_name(langopt) ||
92 return (Language.find_by_name(langopt) ||
93 Language.find_by_pretty_name(langopt))
93 Language.find_by_pretty_name(langopt))
94 else
94 else
95 if source_filename
95 if source_filename
96 return Language.find_by_extension(source_filename.split('.').last)
96 return Language.find_by_extension(source_filename.split('.').last)
97 else
97 else
98 return nil
98 return nil
99 end
99 end
100 end
100 end
101 end
101 end
102
102
103 def self.find_problem_in_source(source, source_filename="")
103 def self.find_problem_in_source(source, source_filename="")
104 prob_opt = find_option_in_source(/^TASK:/,source)
104 prob_opt = find_option_in_source(/^TASK:/,source)
105 if problem = Problem.find_by_name(prob_opt)
105 if problem = Problem.find_by_name(prob_opt)
106 return problem
106 return problem
107 else
107 else
108 if source_filename
108 if source_filename
109 return Problem.find_by_name(source_filename.split('.').first)
109 return Problem.find_by_name(source_filename.split('.').first)
110 else
110 else
111 return nil
111 return nil
112 end
112 end
113 end
113 end
114 end
114 end
115
115
116 def assign_problem
116 def assign_problem
117 if self.problem_id!=-1
117 if self.problem_id!=-1
118 begin
118 begin
119 self.problem = Problem.find(self.problem_id)
119 self.problem = Problem.find(self.problem_id)
120 rescue ActiveRecord::RecordNotFound
120 rescue ActiveRecord::RecordNotFound
121 self.problem = nil
121 self.problem = nil
122 end
122 end
123 else
123 else
124 self.problem = Submission.find_problem_in_source(self.source,
124 self.problem = Submission.find_problem_in_source(self.source,
125 self.source_filename)
125 self.source_filename)
126 end
126 end
127 end
127 end
128
128
129 def assign_language
129 def assign_language
130 + if self.language == nil
130 self.language = Submission.find_language_in_source(self.source,
131 self.language = Submission.find_language_in_source(self.source,
131 self.source_filename)
132 self.source_filename)
132 end
133 end
134 + end
133
135
134 # validation codes
136 # validation codes
135 def must_specify_language
137 def must_specify_language
136 return if self.source==nil
138 return if self.source==nil
137
139
138 # for output_only tasks
140 # for output_only tasks
139 return if self.problem!=nil and self.problem.output_only
141 return if self.problem!=nil and self.problem.output_only
140
142
141 if self.language==nil
143 if self.language == nil
142 - errors.add('source',"Cannot detect language. Did you submit a correct source file?") unless self.language!=nil
144 + errors.add('source',"Cannot detect language. Did you submit a correct source file?")
143 end
145 end
144 end
146 end
145
147
146 def must_have_valid_problem
148 def must_have_valid_problem
147 return if self.source==nil
149 return if self.source==nil
148 if self.problem==nil
150 if self.problem==nil
149 errors.add('problem',"must be specified.")
151 errors.add('problem',"must be specified.")
150 else
152 else
151 #admin always have right
153 #admin always have right
152 return if self.user.admin?
154 return if self.user.admin?
153
155
154 #check if user has the right to submit the problem
156 #check if user has the right to submit the problem
155 errors.add('problem',"must be valid.") if (!self.user.available_problems.include?(self.problem)) and (self.new_record?)
157 errors.add('problem',"must be valid.") if (!self.user.available_problems.include?(self.problem)) and (self.new_record?)
156 end
158 end
157 end
159 end
158
160
159 # callbacks
161 # callbacks
160 def assign_latest_number_if_new_recond
162 def assign_latest_number_if_new_recond
161 return if !self.new_record?
163 return if !self.new_record?
162 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
164 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
163 self.number = (latest==nil) ? 1 : latest.number + 1;
165 self.number = (latest==nil) ? 1 : latest.number + 1;
164 end
166 end
165
167
166 end
168 end
@@ -1,235 +1,235
1 require 'digest/sha1'
1 require 'digest/sha1'
2 require 'net/pop'
2 require 'net/pop'
3 require 'net/https'
3 require 'net/https'
4 require 'net/http'
4 require 'net/http'
5 require 'json'
5 require 'json'
6
6
7 class User < ActiveRecord::Base
7 class User < ActiveRecord::Base
8
8
9 has_and_belongs_to_many :roles
9 has_and_belongs_to_many :roles
10
10
11 #has_and_belongs_to_many :groups
11 #has_and_belongs_to_many :groups
12 has_many :groups_users, class_name: 'GroupUser'
12 has_many :groups_users, class_name: 'GroupUser'
13 has_many :groups, :through => :groups_users
13 has_many :groups, :through => :groups_users
14
14
15 has_many :test_requests, -> {order(submitted_at: :desc)}
15 has_many :test_requests, -> {order(submitted_at: :desc)}
16
16
17 has_many :messages, -> { order(created_at: :desc) },
17 has_many :messages, -> { order(created_at: :desc) },
18 :class_name => "Message",
18 :class_name => "Message",
19 :foreign_key => "sender_id"
19 :foreign_key => "sender_id"
20
20
21 has_many :replied_messages, -> { order(created_at: :desc) },
21 has_many :replied_messages, -> { order(created_at: :desc) },
22 :class_name => "Message",
22 :class_name => "Message",
23 :foreign_key => "receiver_id"
23 :foreign_key => "receiver_id"
24
24
25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
26
26
27 belongs_to :site
27 belongs_to :site
28 belongs_to :country
28 belongs_to :country
29
29
30 has_and_belongs_to_many :contests, -> { order(:name)}
30 has_and_belongs_to_many :contests, -> { order(:name)}
31
31
32 scope :activated_users, -> {where activated: true}
32 scope :activated_users, -> {where activated: true}
33
33
34 validates_presence_of :login
34 validates_presence_of :login
35 validates_uniqueness_of :login
35 validates_uniqueness_of :login
36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
37 validates_length_of :login, :within => 3..30
37 validates_length_of :login, :within => 3..30
38
38
39 validates_presence_of :full_name
39 validates_presence_of :full_name
40 validates_length_of :full_name, :minimum => 1
40 validates_length_of :full_name, :minimum => 1
41
41
42 validates_presence_of :password, :if => :password_required?
42 validates_presence_of :password, :if => :password_required?
43 - validates_length_of :password, :within => 4..20, :if => :password_required?
43 + validates_length_of :password, :within => 4..50, :if => :password_required?
44 validates_confirmation_of :password, :if => :password_required?
44 validates_confirmation_of :password, :if => :password_required?
45
45
46 validates_format_of :email,
46 validates_format_of :email,
47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
48 :if => :email_validation?
48 :if => :email_validation?
49 validate :uniqueness_of_email_from_activated_users,
49 validate :uniqueness_of_email_from_activated_users,
50 :if => :email_validation?
50 :if => :email_validation?
51 validate :enough_time_interval_between_same_email_registrations,
51 validate :enough_time_interval_between_same_email_registrations,
52 :if => :email_validation?
52 :if => :email_validation?
53
53
54 # these are for ytopc
54 # these are for ytopc
55 # disable for now
55 # disable for now
56 #validates_presence_of :province
56 #validates_presence_of :province
57
57
58 attr_accessor :password
58 attr_accessor :password
59
59
60 before_save :encrypt_new_password
60 before_save :encrypt_new_password
61 before_save :assign_default_site
61 before_save :assign_default_site
62 before_save :assign_default_contest
62 before_save :assign_default_contest
63
63
64 # this is for will_paginate
64 # this is for will_paginate
65 cattr_reader :per_page
65 cattr_reader :per_page
66 @@per_page = 50
66 @@per_page = 50
67
67
68 def self.authenticate(login, password)
68 def self.authenticate(login, password)
69 user = find_by_login(login)
69 user = find_by_login(login)
70 if user
70 if user
71 return user if user.authenticated?(password)
71 return user if user.authenticated?(password)
72 end
72 end
73 end
73 end
74
74
75 def authenticated?(password)
75 def authenticated?(password)
76 if self.activated
76 if self.activated
77 hashed_password == User.encrypt(password,self.salt)
77 hashed_password == User.encrypt(password,self.salt)
78 else
78 else
79 false
79 false
80 end
80 end
81 end
81 end
82
82
83 def admin?
83 def admin?
84 self.roles.where(name: 'admin').count > 0
84 self.roles.where(name: 'admin').count > 0
85 end
85 end
86
86
87 def email_for_editing
87 def email_for_editing
88 if self.email==nil
88 if self.email==nil
89 "(unknown)"
89 "(unknown)"
90 elsif self.email==''
90 elsif self.email==''
91 "(blank)"
91 "(blank)"
92 else
92 else
93 self.email
93 self.email
94 end
94 end
95 end
95 end
96
96
97 def email_for_editing=(e)
97 def email_for_editing=(e)
98 self.email=e
98 self.email=e
99 end
99 end
100
100
101 def alias_for_editing
101 def alias_for_editing
102 if self.alias==nil
102 if self.alias==nil
103 "(unknown)"
103 "(unknown)"
104 elsif self.alias==''
104 elsif self.alias==''
105 "(blank)"
105 "(blank)"
106 else
106 else
107 self.alias
107 self.alias
108 end
108 end
109 end
109 end
110
110
111 def alias_for_editing=(e)
111 def alias_for_editing=(e)
112 self.alias=e
112 self.alias=e
113 end
113 end
114
114
115 def activation_key
115 def activation_key
116 if self.hashed_password==nil
116 if self.hashed_password==nil
117 encrypt_new_password
117 encrypt_new_password
118 end
118 end
119 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
119 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
120 end
120 end
121
121
122 def verify_activation_key(key)
122 def verify_activation_key(key)
123 key == activation_key
123 key == activation_key
124 end
124 end
125
125
126 def self.random_password(length=5)
126 def self.random_password(length=5)
127 chars = 'abcdefghjkmnopqrstuvwxyz'
127 chars = 'abcdefghjkmnopqrstuvwxyz'
128 password = ''
128 password = ''
129 length.times { password << chars[rand(chars.length - 1)] }
129 length.times { password << chars[rand(chars.length - 1)] }
130 password
130 password
131 end
131 end
132
132
133 def self.find_non_admin_with_prefix(prefix='')
133 def self.find_non_admin_with_prefix(prefix='')
134 users = User.all
134 users = User.all
135 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
135 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
136 end
136 end
137
137
138 # Contest information
138 # Contest information
139
139
140 def self.find_users_with_no_contest()
140 def self.find_users_with_no_contest()
141 users = User.all
141 users = User.all
142 return users.find_all { |u| u.contests.length == 0 }
142 return users.find_all { |u| u.contests.length == 0 }
143 end
143 end
144
144
145
145
146 def contest_time_left
146 def contest_time_left
147 if GraderConfiguration.contest_mode?
147 if GraderConfiguration.contest_mode?
148 return nil if site==nil
148 return nil if site==nil
149 return site.time_left
149 return site.time_left
150 elsif GraderConfiguration.indv_contest_mode?
150 elsif GraderConfiguration.indv_contest_mode?
151 time_limit = GraderConfiguration.contest_time_limit
151 time_limit = GraderConfiguration.contest_time_limit
152 if time_limit == nil
152 if time_limit == nil
153 return nil
153 return nil
154 end
154 end
155 if contest_stat==nil or contest_stat.started_at==nil
155 if contest_stat==nil or contest_stat.started_at==nil
156 return (Time.now.gmtime + time_limit) - Time.now.gmtime
156 return (Time.now.gmtime + time_limit) - Time.now.gmtime
157 else
157 else
158 finish_time = contest_stat.started_at + time_limit
158 finish_time = contest_stat.started_at + time_limit
159 current_time = Time.now.gmtime
159 current_time = Time.now.gmtime
160 if current_time > finish_time
160 if current_time > finish_time
161 return 0
161 return 0
162 else
162 else
163 return finish_time - current_time
163 return finish_time - current_time
164 end
164 end
165 end
165 end
166 else
166 else
167 return nil
167 return nil
168 end
168 end
169 end
169 end
170
170
171 def contest_finished?
171 def contest_finished?
172 if GraderConfiguration.contest_mode?
172 if GraderConfiguration.contest_mode?
173 return false if site==nil
173 return false if site==nil
174 return site.finished?
174 return site.finished?
175 elsif GraderConfiguration.indv_contest_mode?
175 elsif GraderConfiguration.indv_contest_mode?
176 return false if self.contest_stat==nil
176 return false if self.contest_stat==nil
177 return contest_time_left == 0
177 return contest_time_left == 0
178 else
178 else
179 return false
179 return false
180 end
180 end
181 end
181 end
182
182
183 def contest_started?
183 def contest_started?
184 if GraderConfiguration.indv_contest_mode?
184 if GraderConfiguration.indv_contest_mode?
185 stat = self.contest_stat
185 stat = self.contest_stat
186 return ((stat != nil) and (stat.started_at != nil))
186 return ((stat != nil) and (stat.started_at != nil))
187 elsif GraderConfiguration.contest_mode?
187 elsif GraderConfiguration.contest_mode?
188 return true if site==nil
188 return true if site==nil
189 return site.started
189 return site.started
190 else
190 else
191 return true
191 return true
192 end
192 end
193 end
193 end
194
194
195 def update_start_time
195 def update_start_time
196 stat = self.contest_stat
196 stat = self.contest_stat
197 if stat.nil? or stat.started_at.nil?
197 if stat.nil? or stat.started_at.nil?
198 stat ||= UserContestStat.new(:user => self)
198 stat ||= UserContestStat.new(:user => self)
199 stat.started_at = Time.now.gmtime
199 stat.started_at = Time.now.gmtime
200 stat.save
200 stat.save
201 end
201 end
202 end
202 end
203
203
204 def problem_in_user_contests?(problem)
204 def problem_in_user_contests?(problem)
205 problem_contests = problem.contests.all
205 problem_contests = problem.contests.all
206
206
207 if problem_contests.length == 0 # this is public contest
207 if problem_contests.length == 0 # this is public contest
208 return true
208 return true
209 end
209 end
210
210
211 contests.each do |contest|
211 contests.each do |contest|
212 if problem_contests.find {|c| c.id == contest.id }
212 if problem_contests.find {|c| c.id == contest.id }
213 return true
213 return true
214 end
214 end
215 end
215 end
216 return false
216 return false
217 end
217 end
218
218
219 def available_problems_group_by_contests
219 def available_problems_group_by_contests
220 contest_problems = []
220 contest_problems = []
221 pin = {}
221 pin = {}
222 contests.enabled.each do |contest|
222 contests.enabled.each do |contest|
223 available_problems = contest.problems.available
223 available_problems = contest.problems.available
224 contest_problems << {
224 contest_problems << {
225 :contest => contest,
225 :contest => contest,
226 :problems => available_problems
226 :problems => available_problems
227 }
227 }
228 available_problems.each {|p| pin[p.id] = true}
228 available_problems.each {|p| pin[p.id] = true}
229 end
229 end
230 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
230 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
231 contest_problems << {
231 contest_problems << {
232 :contest => nil,
232 :contest => nil,
233 :problems => other_avaiable_problems
233 :problems => other_avaiable_problems
234 }
234 }
235 return contest_problems
235 return contest_problems
@@ -1,88 +1,87
1 %textarea#text_sourcecode{style: "display:none"}~ @source
1 %textarea#text_sourcecode{style: "display:none"}~ @source
2 .container
2 .container
3 .row
3 .row
4 .col-md-12
4 .col-md-12
5 %h2 Live submit
5 %h2 Live submit
6
6
7 .row
7 .row
8 .col-md-12
8 .col-md-12
9 .alert.alert-info
9 .alert.alert-info
10 Write your code in the following box, choose language, and click submit button when finished
10 Write your code in the following box, choose language, and click submit button when finished
11 .row
11 .row
12 .col-md-8
12 .col-md-8
13 %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'}
13 %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'}
14 .col-md-4
14 .col-md-4
15 - # submission form
15 - # submission form
16 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
16 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
17
17
18 = hidden_field_tag 'editor_text', @source
18 = hidden_field_tag 'editor_text', @source
19 = hidden_field_tag 'submission[problem_id]', @problem.id
19 = hidden_field_tag 'submission[problem_id]', @problem.id
20 .form-group
20 .form-group
21 = label_tag "Task:"
21 = label_tag "Task:"
22 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
22 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
23 .form-group
23 .form-group
24 = label_tag "Description:"
24 = label_tag "Description:"
25 = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @problem
25 = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @problem
26
26
27 .form-group
27 .form-group
28 = label_tag 'Language:'
28 = label_tag 'Language:'
29 = select_tag 'language_id', options_from_collection_for_select(Language.all, 'id', 'pretty_name', @lang_id || Language.find_by_pretty_name("Python").id || Language.first.id), class: 'form-control select', style: "width: 100px"
29 = select_tag 'language_id', options_from_collection_for_select(Language.all, 'id', 'pretty_name', @lang_id || Language.find_by_pretty_name("Python").id || Language.first.id), class: 'form-control select', style: "width: 100px"
30 .form-group
30 .form-group
31 .input-group
31 .input-group
32 %span.input-group-btn
32 %span.input-group-btn
33 %span.btn.btn-default.btn-file
33 %span.btn.btn-default.btn-file
34 Browse
34 Browse
35 = file_field_tag 'load_file'
35 = file_field_tag 'load_file'
36 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
36 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
37 .form-group
37 .form-group
38 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
38 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
39 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
39 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
40 - # latest submission status
40 - # latest submission status
41 .panel{class: (@submission && @submission.graded_at) ? "panel-info" : "panel-warning"}
41 .panel{class: (@submission && @submission.graded_at) ? "panel-info" : "panel-warning"}
42 .panel-heading
42 .panel-heading
43 Latest Submission Status
43 Latest Submission Status
44 = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission
44 = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission
45 .panel-body
45 .panel-body
46 %div#latest_status
46 %div#latest_status
47 - if @submission
47 - if @submission
48 = render :partial => 'submission_short',
48 = render :partial => 'submission_short',
49 :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
49 :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
50
50
51 - if @submission
51 - if @submission
52 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
52 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
53 .modal-dialog.modal-lg{role:'document'}
53 .modal-dialog.modal-lg{role:'document'}
54 .modal-content
54 .modal-content
55 .modal-header
55 .modal-header
56 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
56 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
57 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
57 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
58 %h4 Compiler message
58 %h4 Compiler message
59 .modal-body
59 .modal-body
60 %pre#compiler_msg= @submission.compiler_message
60 %pre#compiler_msg= @submission.compiler_message
61 .modal-footer
61 .modal-footer
62 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
62 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
63
63
64 :javascript
64 :javascript
65 $(document).ready(function() {
65 $(document).ready(function() {
66 e = ace.edit("editor")
66 e = ace.edit("editor")
67 e.setValue($("#text_sourcecode").val());
67 e.setValue($("#text_sourcecode").val());
68 e.gotoLine(1);
68 e.gotoLine(1);
69 $("#language_id").trigger('change');
69 $("#language_id").trigger('change');
70
70
71 $("#load_file").on('change',function(evt) {
71 $("#load_file").on('change',function(evt) {
72 var file = evt.target.files[0];
72 var file = evt.target.files[0];
73 var reader = new FileReader();
73 var reader = new FileReader();
74 reader.onload = function(theFile) {
74 reader.onload = function(theFile) {
75 var e = ace.edit("editor")
75 var e = ace.edit("editor")
76 e.setValue(theFile.target.result);
76 e.setValue(theFile.target.result);
77 e.gotoLine(1);
77 e.gotoLine(1);
78 };
78 };
79 reader.readAsText(file)
79 reader.readAsText(file)
80 });
80 });
81
81
82 //brython();
82 //brython();
83 });
83 });
84
84
85
85
86
86
87
87
88 -
@@ -1,30 +1,33
1 # If you want to manage graders through web interface, set the path to
1 # If you want to manage graders through web interface, set the path to
2 # the grader directory below. This dir is where raw, ev, ev-exam,
2 # the grader directory below. This dir is where raw, ev, ev-exam,
3 # scripts reside. All grader scripts will be in
3 # scripts reside. All grader scripts will be in
4 # #{GRADER_ROOT_DIR}/scripts.
4 # #{GRADER_ROOT_DIR}/scripts.
5 GRADER_ROOT_DIR = ''
5 GRADER_ROOT_DIR = ''
6
6
7 # These are where inputs and outputs of test requests are stored
7 # These are where inputs and outputs of test requests are stored
8 TEST_REQUEST_INPUT_FILE_DIR = (Rails.root + 'data/test_request/input').to_s
8 TEST_REQUEST_INPUT_FILE_DIR = (Rails.root + 'data/test_request/input').to_s
9 TEST_REQUEST_OUTPUT_FILE_DIR = (Rails.root + 'data/test_request/output').to_s
9 TEST_REQUEST_OUTPUT_FILE_DIR = (Rails.root + 'data/test_request/output').to_s
10
10
11 # To use ANALYSIS MODE, provide the testcases/testruns breakdown,
11 # To use ANALYSIS MODE, provide the testcases/testruns breakdown,
12 # and the directory of the grading result (usually in judge's dir).
12 # and the directory of the grading result (usually in judge's dir).
13 TASK_GRADING_INFO_FILENAME = Rails.root + 'config/tasks.yml'
13 TASK_GRADING_INFO_FILENAME = Rails.root + 'config/tasks.yml'
14
14
15 # TODO: change this to where results are kept.
15 # TODO: change this to where results are kept.
16 GRADING_RESULT_DIR = 'RESULT-DIR'
16 GRADING_RESULT_DIR = 'RESULT-DIR'
17
17
18 # Change this to allow importing testdata into database as test-pairs.
18 # Change this to allow importing testdata into database as test-pairs.
19 # This is mainly for Code Jom contest.
19 # This is mainly for Code Jom contest.
20 ALLOW_TEST_PAIR_IMPORT = false
20 ALLOW_TEST_PAIR_IMPORT = false
21
21
22 # Uncomment so that the system validates user e-mails
22 # Uncomment so that the system validates user e-mails
23 # VALIDATE_USER_EMAILS = true
23 # VALIDATE_USER_EMAILS = true
24
24
25 # Uncomment so that Apache X-Sendfile is used when delivering files
25 # Uncomment so that Apache X-Sendfile is used when delivering files
26 # (e.g., in /tasks/view).
26 # (e.g., in /tasks/view).
27 # USE_APACHE_XSENDFILE = true
27 # USE_APACHE_XSENDFILE = true
28
28
29 # Uncomment so that configuration is read only once when the server is loaded
29 # Uncomment so that configuration is read only once when the server is loaded
30 # CONFIGURATION_CACHE_ENABLED = true
30 # CONFIGURATION_CACHE_ENABLED = true
31 +
32 + # Uncomment to allow authentication and user import from programming.in.th
33 + # LoginController.add_authenticator(ProgrammingAuthenticator.new)
@@ -1,280 +1,288
1 CONFIGURATIONS =
1 CONFIGURATIONS =
2 [
2 [
3 {
3 {
4 :key => 'system.single_user_mode',
4 :key => 'system.single_user_mode',
5 :value_type => 'boolean',
5 :value_type => 'boolean',
6 :default_value => 'false',
6 :default_value => 'false',
7 :description => 'Only admins can log in to the system when running under single user mode.'
7 :description => 'Only admins can log in to the system when running under single user mode.'
8 },
8 },
9
9
10 {
10 {
11 :key => 'ui.front.title',
11 :key => 'ui.front.title',
12 :value_type => 'string',
12 :value_type => 'string',
13 :default_value => 'Grader'
13 :default_value => 'Grader'
14 },
14 },
15
15
16 {
16 {
17 :key => 'ui.front.welcome_message',
17 :key => 'ui.front.welcome_message',
18 :value_type => 'string',
18 :value_type => 'string',
19 :default_value => 'Welcome!'
19 :default_value => 'Welcome!'
20 },
20 },
21
21
22 {
22 {
23 :key => 'ui.show_score',
23 :key => 'ui.show_score',
24 :value_type => 'boolean',
24 :value_type => 'boolean',
25 :default_value => 'true'
25 :default_value => 'true'
26 },
26 },
27
27
28 {
28 {
29 :key => 'contest.time_limit',
29 :key => 'contest.time_limit',
30 :value_type => 'string',
30 :value_type => 'string',
31 :default_value => 'unlimited',
31 :default_value => 'unlimited',
32 :description => 'Time limit in format hh:mm, or "unlimited" for contests with no time limits. This config is CACHED. Restart the server before the change can take effect.'
32 :description => 'Time limit in format hh:mm, or "unlimited" for contests with no time limits. This config is CACHED. Restart the server before the change can take effect.'
33 },
33 },
34
34
35 {
35 {
36 :key => 'system.mode',
36 :key => 'system.mode',
37 :value_type => 'string',
37 :value_type => 'string',
38 :default_value => 'standard',
38 :default_value => 'standard',
39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
40 },
40 },
41
41
42 {
42 {
43 :key => 'contest.name',
43 :key => 'contest.name',
44 :value_type => 'string',
44 :value_type => 'string',
45 :default_value => 'Grader',
45 :default_value => 'Grader',
46 :description => 'This name will be shown on the user header bar.'
46 :description => 'This name will be shown on the user header bar.'
47 },
47 },
48
48
49 {
49 {
50 :key => 'contest.multisites',
50 :key => 'contest.multisites',
51 :value_type => 'boolean',
51 :value_type => 'boolean',
52 :default_value => 'false',
52 :default_value => 'false',
53 :description => 'If the server is in contest mode and this option is true, on the log in of the admin a menu for site selections is shown.'
53 :description => 'If the server is in contest mode and this option is true, on the log in of the admin a menu for site selections is shown.'
54 },
54 },
55
55
56 #---------------------------- right --------------------------------
56 #---------------------------- right --------------------------------
57 {
57 {
58 :key => 'right.user_hall_of_fame',
58 :key => 'right.user_hall_of_fame',
59 :value_type => 'boolean',
59 :value_type => 'boolean',
60 :default_value => 'false',
60 :default_value => 'false',
61 :description => 'If true, any user can access hall of fame page.'
61 :description => 'If true, any user can access hall of fame page.'
62 },
62 },
63
63
64 {
64 {
65 :key => 'right.multiple_ip_login',
65 :key => 'right.multiple_ip_login',
66 :value_type => 'boolean',
66 :value_type => 'boolean',
67 :default_value => 'true',
67 :default_value => 'true',
68 :description => 'When change from true to false, a user can login from the first IP they logged into afterward.'
68 :description => 'When change from true to false, a user can login from the first IP they logged into afterward.'
69 },
69 },
70
70
71 {
71 {
72 :key => 'right.user_view_submission',
72 :key => 'right.user_view_submission',
73 :value_type => 'boolean',
73 :value_type => 'boolean',
74 :default_value => 'false',
74 :default_value => 'false',
75 :description => 'If true, any user can view submissions of every one.'
75 :description => 'If true, any user can view submissions of every one.'
76 },
76 },
77
77
78 {
78 {
79 :key => 'right.bypass_agreement',
79 :key => 'right.bypass_agreement',
80 :value_type => 'boolean',
80 :value_type => 'boolean',
81 :default_value => 'true',
81 :default_value => 'true',
82 :description => 'When false, a user must accept usage agreement before login'
82 :description => 'When false, a user must accept usage agreement before login'
83 },
83 },
84
84
85 {
85 {
86 :key => 'right.heartbeat_response',
86 :key => 'right.heartbeat_response',
87 :value_type => 'string',
87 :value_type => 'string',
88 :default_value => 'OK',
88 :default_value => 'OK',
89 :description => 'Heart beat response text'
89 :description => 'Heart beat response text'
90 },
90 },
91
91
92 {
92 {
93 :key => 'right.heartbeat_response_full',
93 :key => 'right.heartbeat_response_full',
94 :value_type => 'string',
94 :value_type => 'string',
95 :default_value => 'OK',
95 :default_value => 'OK',
96 :description => 'Heart beat response text when user got full score (set this value to the empty string to disable this feature)'
96 :description => 'Heart beat response text when user got full score (set this value to the empty string to disable this feature)'
97 },
97 },
98
98
99 {
99 {
100 :key => 'right.view_testcase',
100 :key => 'right.view_testcase',
101 :value_type => 'boolean',
101 :value_type => 'boolean',
102 :default_value => 'false',
102 :default_value => 'false',
103 :description => 'When true, any user can view/download test data'
103 :description => 'When true, any user can view/download test data'
104 },
104 },
105 +
106 + {
107 + :key => 'system.online_registration',
108 + :value_type => 'boolean',
109 + :default_value => 'false',
110 + :description => 'This option enables online registration.'
111 + },
112 +
105 # If Configuration['system.online_registration'] is true, the
113 # If Configuration['system.online_registration'] is true, the
106 # system allows online registration, and will use these
114 # system allows online registration, and will use these
107 # information for sending confirmation emails.
115 # information for sending confirmation emails.
108 {
116 {
109 :key => 'system.online_registration.smtp',
117 :key => 'system.online_registration.smtp',
110 :value_type => 'string',
118 :value_type => 'string',
111 :default_value => 'smtp.somehost.com'
119 :default_value => 'smtp.somehost.com'
112 },
120 },
113
121
114 {
122 {
115 :key => 'system.online_registration.from',
123 :key => 'system.online_registration.from',
116 :value_type => 'string',
124 :value_type => 'string',
117 :default_value => 'your.email@address'
125 :default_value => 'your.email@address'
118 },
126 },
119
127
120 {
128 {
121 :key => 'system.admin_email',
129 :key => 'system.admin_email',
122 :value_type => 'string',
130 :value_type => 'string',
123 :default_value => 'admin@admin.email'
131 :default_value => 'admin@admin.email'
124 },
132 },
125
133
126 {
134 {
127 :key => 'system.user_setting_enabled',
135 :key => 'system.user_setting_enabled',
128 :value_type => 'boolean',
136 :value_type => 'boolean',
129 :default_value => 'true',
137 :default_value => 'true',
130 :description => 'If this option is true, users can change their settings'
138 :description => 'If this option is true, users can change their settings'
131 },
139 },
132
140
133 {
141 {
134 :key => 'system.user_setting_enabled',
142 :key => 'system.user_setting_enabled',
135 :value_type => 'boolean',
143 :value_type => 'boolean',
136 :default_value => 'true',
144 :default_value => 'true',
137 :description => 'If this option is true, users can change their settings'
145 :description => 'If this option is true, users can change their settings'
138 },
146 },
139
147
140 # If Configuration['contest.test_request.early_timeout'] is true
148 # If Configuration['contest.test_request.early_timeout'] is true
141 # the user will not be able to use test request at 30 minutes
149 # the user will not be able to use test request at 30 minutes
142 # before the contest ends.
150 # before the contest ends.
143 {
151 {
144 :key => 'contest.test_request.early_timeout',
152 :key => 'contest.test_request.early_timeout',
145 :value_type => 'boolean',
153 :value_type => 'boolean',
146 :default_value => 'false'
154 :default_value => 'false'
147 },
155 },
148
156
149 {
157 {
150 :key => 'system.multicontests',
158 :key => 'system.multicontests',
151 :value_type => 'boolean',
159 :value_type => 'boolean',
152 :default_value => 'false'
160 :default_value => 'false'
153 },
161 },
154
162
155 {
163 {
156 :key => 'contest.confirm_indv_contest_start',
164 :key => 'contest.confirm_indv_contest_start',
157 :value_type => 'boolean',
165 :value_type => 'boolean',
158 :default_value => 'false'
166 :default_value => 'false'
159 },
167 },
160
168
161 {
169 {
162 :key => 'contest.default_contest_name',
170 :key => 'contest.default_contest_name',
163 :value_type => 'string',
171 :value_type => 'string',
164 :default_value => 'none',
172 :default_value => 'none',
165 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
173 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
166 },
174 },
167
175
168 {
176 {
169 :key => 'system.use_problem_group',
177 :key => 'system.use_problem_group',
170 :value_type => 'boolean',
178 :value_type => 'boolean',
171 :default_value => 'false',
179 :default_value => 'false',
172 :description => "If true, available problem to the user will be only ones associated with the group of the user."
180 :description => "If true, available problem to the user will be only ones associated with the group of the user."
173 },
181 },
174
182
175
183
176 {
184 {
177 :key => 'right.whitelist_ip_only',
185 :key => 'right.whitelist_ip_only',
178 :value_type => 'boolean',
186 :value_type => 'boolean',
179 :default_value => 'false',
187 :default_value => 'false',
180 :description => "If true, non-admin user will be able to use the system only when their ip is in the 'whitelist_ip'."
188 :description => "If true, non-admin user will be able to use the system only when their ip is in the 'whitelist_ip'."
181 },
189 },
182
190
183 {
191 {
184 :key => 'right.whitelist_ip',
192 :key => 'right.whitelist_ip',
185 :value_type => 'string',
193 :value_type => 'string',
186 :default_value => '0.0.0.0/0',
194 :default_value => '0.0.0.0/0',
187 :description => "list of whitelist ip, given in comma separated CIDR notation. For example '161.200.92.0/23, 161.200.80.1/32'"
195 :description => "list of whitelist ip, given in comma separated CIDR notation. For example '161.200.92.0/23, 161.200.80.1/32'"
188 },
196 },
189
197
190 ]
198 ]
191
199
192
200
193 def create_configuration_key(key,
201 def create_configuration_key(key,
194 value_type,
202 value_type,
195 default_value,
203 default_value,
196 description='')
204 description='')
197 conf = (GraderConfiguration.find_by_key(key) ||
205 conf = (GraderConfiguration.find_by_key(key) ||
198 GraderConfiguration.new(:key => key,
206 GraderConfiguration.new(:key => key,
199 :value_type => value_type,
207 :value_type => value_type,
200 :value => default_value))
208 :value => default_value))
201 conf.description = description
209 conf.description = description
202 conf.save
210 conf.save
203 end
211 end
204
212
205 def seed_config
213 def seed_config
206 CONFIGURATIONS.each do |conf|
214 CONFIGURATIONS.each do |conf|
207 if conf.has_key? :description
215 if conf.has_key? :description
208 desc = conf[:description]
216 desc = conf[:description]
209 else
217 else
210 desc = ''
218 desc = ''
211 end
219 end
212 create_configuration_key(conf[:key],
220 create_configuration_key(conf[:key],
213 conf[:value_type],
221 conf[:value_type],
214 conf[:default_value],
222 conf[:default_value],
215 desc)
223 desc)
216 end
224 end
217 end
225 end
218
226
219 def seed_roles
227 def seed_roles
220 return if Role.find_by_name('admin')
228 return if Role.find_by_name('admin')
221
229
222 role = Role.create(:name => 'admin')
230 role = Role.create(:name => 'admin')
223 user_admin_right = Right.create(:name => 'user_admin',
231 user_admin_right = Right.create(:name => 'user_admin',
224 :controller => 'user_admin',
232 :controller => 'user_admin',
225 :action => 'all')
233 :action => 'all')
226 problem_admin_right = Right.create(:name=> 'problem_admin',
234 problem_admin_right = Right.create(:name=> 'problem_admin',
227 :controller => 'problems',
235 :controller => 'problems',
228 :action => 'all')
236 :action => 'all')
229
237
230 graders_right = Right.create(:name => 'graders_admin',
238 graders_right = Right.create(:name => 'graders_admin',
231 :controller => 'graders',
239 :controller => 'graders',
232 :action => 'all')
240 :action => 'all')
233
241
234 role.rights << user_admin_right;
242 role.rights << user_admin_right;
235 role.rights << problem_admin_right;
243 role.rights << problem_admin_right;
236 role.rights << graders_right;
244 role.rights << graders_right;
237 role.save
245 role.save
238 end
246 end
239
247
240 def seed_root
248 def seed_root
241 return if User.find_by_login('root')
249 return if User.find_by_login('root')
242
250
243 root = User.new(:login => 'root',
251 root = User.new(:login => 'root',
244 :full_name => 'Administrator',
252 :full_name => 'Administrator',
245 :alias => 'root')
253 :alias => 'root')
246 root.password = 'ioionrails';
254 root.password = 'ioionrails';
247
255
248 class << root
256 class << root
249 public :encrypt_new_password
257 public :encrypt_new_password
250 def valid?(context=nil)
258 def valid?(context=nil)
251 true
259 true
252 end
260 end
253 end
261 end
254
262
255 root.encrypt_new_password
263 root.encrypt_new_password
256
264
257 root.roles << Role.find_by_name('admin')
265 root.roles << Role.find_by_name('admin')
258
266
259 root.activated = true
267 root.activated = true
260 root.save
268 root.save
261 end
269 end
262
270
263 def seed_users_and_roles
271 def seed_users_and_roles
264 seed_roles
272 seed_roles
265 seed_root
273 seed_root
266 end
274 end
267
275
268 def seed_more_languages
276 def seed_more_languages
269 Language.delete_all
277 Language.delete_all
270 Language.find_or_create_by( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
278 Language.find_or_create_by( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
271 Language.find_or_create_by( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
279 Language.find_or_create_by( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
272 Language.find_or_create_by( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
280 Language.find_or_create_by( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
273 Language.find_or_create_by( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
281 Language.find_or_create_by( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
274 Language.find_or_create_by( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
282 Language.find_or_create_by( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
275 Language.find_or_create_by( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
283 Language.find_or_create_by( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
276 end
284 end
277
285
278 seed_config
286 seed_config
279 seed_users_and_roles
287 seed_users_and_roles
280 seed_more_languages
288 seed_more_languages
You need to be logged in to leave comments. Login now