Description:
Merge pull request #26 from cafe-grader-team/master merge edit from upstream
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r777:e61e522c673f - - 8 files changed: 82 inserted, 8 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,63 +1,85
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
65 + def self.add_authenticator(authenticator)
66 + @@authenticators << authenticator
67 + end
68 +
69 + protected
70 +
71 + def get_authenticated_user(login, password)
72 + if @@authenticators.empty?
73 + return User.authenticate(login, password)
74 + else
75 + user = User.authenticate(login, password)
76 + @@authenticators.each do |authenticator|
77 + if not user
78 + user = authenticator.authenticate(login, password)
79 + end
80 + end
81 + return user
82 + end
83 + end
84 +
63 end
85 end
@@ -1,310 +1,314
1 class ProblemsController < ApplicationController
1 class ProblemsController < ApplicationController
2
2
3 before_action :authenticate, :authorization
3 before_action :authenticate, :authorization
4 before_action :testcase_authorization, only: [:show_testcase]
4 before_action :testcase_authorization, only: [:show_testcase]
5
5
6 in_place_edit_for :problem, :name
6 in_place_edit_for :problem, :name
7 in_place_edit_for :problem, :full_name
7 in_place_edit_for :problem, :full_name
8 in_place_edit_for :problem, :full_score
8 in_place_edit_for :problem, :full_score
9
9
10 def index
10 def index
11 @problems = Problem.order(date_added: :desc)
11 @problems = Problem.order(date_added: :desc)
12 end
12 end
13
13
14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
15 verify :method => :post, :only => [ :create, :quick_create,
15 verify :method => :post, :only => [ :create, :quick_create,
16 :do_manage,
16 :do_manage,
17 :do_import,
17 :do_import,
18 ],
18 ],
19 :redirect_to => { :action => :index }
19 :redirect_to => { :action => :index }
20
20
21 def show
21 def show
22 @problem = Problem.find(params[:id])
22 @problem = Problem.find(params[:id])
23 end
23 end
24
24
25 def new
25 def new
26 @problem = Problem.new
26 @problem = Problem.new
27 @description = nil
27 @description = nil
28 end
28 end
29
29
30 def create
30 def create
31 @problem = Problem.new(problem_params)
31 @problem = Problem.new(problem_params)
32 - @description = Description.new(params[:description])
32 + @description = Description.new(description_params)
33 if @description.body!=''
33 if @description.body!=''
34 if !@description.save
34 if !@description.save
35 render :action => new and return
35 render :action => new and return
36 end
36 end
37 else
37 else
38 @description = nil
38 @description = nil
39 end
39 end
40 @problem.description = @description
40 @problem.description = @description
41 if @problem.save
41 if @problem.save
42 flash[:notice] = 'Problem was successfully created.'
42 flash[:notice] = 'Problem was successfully created.'
43 redirect_to action: :index
43 redirect_to action: :index
44 else
44 else
45 render :action => 'new'
45 render :action => 'new'
46 end
46 end
47 end
47 end
48
48
49 def quick_create
49 def quick_create
50 @problem = Problem.new(problem_params)
50 @problem = Problem.new(problem_params)
51 @problem.full_name = @problem.name if @problem.full_name == ''
51 @problem.full_name = @problem.name if @problem.full_name == ''
52 @problem.full_score = 100
52 @problem.full_score = 100
53 @problem.available = false
53 @problem.available = false
54 @problem.test_allowed = true
54 @problem.test_allowed = true
55 @problem.output_only = false
55 @problem.output_only = false
56 @problem.date_added = Time.new
56 @problem.date_added = Time.new
57 if @problem.save
57 if @problem.save
58 flash[:notice] = 'Problem was successfully created.'
58 flash[:notice] = 'Problem was successfully created.'
59 redirect_to action: :index
59 redirect_to action: :index
60 else
60 else
61 flash[:notice] = 'Error saving problem'
61 flash[:notice] = 'Error saving problem'
62 redirect_to action: :index
62 redirect_to action: :index
63 end
63 end
64 end
64 end
65
65
66 def edit
66 def edit
67 @problem = Problem.find(params[:id])
67 @problem = Problem.find(params[:id])
68 @description = @problem.description
68 @description = @problem.description
69 end
69 end
70
70
71 def update
71 def update
72 @problem = Problem.find(params[:id])
72 @problem = Problem.find(params[:id])
73 @description = @problem.description
73 @description = @problem.description
74 if @description.nil? and params[:description][:body]!=''
74 if @description.nil? and params[:description][:body]!=''
75 @description = Description.new(params[:description])
75 @description = Description.new(params[:description])
76 if !@description.save
76 if !@description.save
77 flash[:notice] = 'Error saving description'
77 flash[:notice] = 'Error saving description'
78 render :action => 'edit' and return
78 render :action => 'edit' and return
79 end
79 end
80 @problem.description = @description
80 @problem.description = @description
81 elsif @description
81 elsif @description
82 if !@description.update_attributes(params[:description])
82 if !@description.update_attributes(params[:description])
83 flash[:notice] = 'Error saving description'
83 flash[:notice] = 'Error saving description'
84 render :action => 'edit' and return
84 render :action => 'edit' and return
85 end
85 end
86 end
86 end
87 if params[:file] and params[:file].content_type != 'application/pdf'
87 if params[:file] and params[:file].content_type != 'application/pdf'
88 flash[:notice] = 'Error: Uploaded file is not PDF'
88 flash[:notice] = 'Error: Uploaded file is not PDF'
89 render :action => 'edit' and return
89 render :action => 'edit' and return
90 end
90 end
91 if @problem.update_attributes(problem_params)
91 if @problem.update_attributes(problem_params)
92 flash[:notice] = 'Problem was successfully updated.'
92 flash[:notice] = 'Problem was successfully updated.'
93 unless params[:file] == nil or params[:file] == ''
93 unless params[:file] == nil or params[:file] == ''
94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
96 if not FileTest.exists? out_dirname
96 if not FileTest.exists? out_dirname
97 Dir.mkdir out_dirname
97 Dir.mkdir out_dirname
98 end
98 end
99
99
100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
101 if FileTest.exists? out_filename
101 if FileTest.exists? out_filename
102 File.delete out_filename
102 File.delete out_filename
103 end
103 end
104
104
105 File.open(out_filename,"wb") do |file|
105 File.open(out_filename,"wb") do |file|
106 file.write(params[:file].read)
106 file.write(params[:file].read)
107 end
107 end
108 @problem.description_filename = "#{@problem.name}.pdf"
108 @problem.description_filename = "#{@problem.name}.pdf"
109 @problem.save
109 @problem.save
110 end
110 end
111 redirect_to :action => 'show', :id => @problem
111 redirect_to :action => 'show', :id => @problem
112 else
112 else
113 render :action => 'edit'
113 render :action => 'edit'
114 end
114 end
115 end
115 end
116
116
117 def destroy
117 def destroy
118 p = Problem.find(params[:id]).destroy
118 p = Problem.find(params[:id]).destroy
119 redirect_to action: :index
119 redirect_to action: :index
120 end
120 end
121
121
122 def toggle
122 def toggle
123 @problem = Problem.find(params[:id])
123 @problem = Problem.find(params[:id])
124 @problem.update_attributes(available: !(@problem.available) )
124 @problem.update_attributes(available: !(@problem.available) )
125 respond_to do |format|
125 respond_to do |format|
126 format.js { }
126 format.js { }
127 end
127 end
128 end
128 end
129
129
130 def toggle_test
130 def toggle_test
131 @problem = Problem.find(params[:id])
131 @problem = Problem.find(params[:id])
132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
133 respond_to do |format|
133 respond_to do |format|
134 format.js { }
134 format.js { }
135 end
135 end
136 end
136 end
137
137
138 def toggle_view_testcase
138 def toggle_view_testcase
139 @problem = Problem.find(params[:id])
139 @problem = Problem.find(params[:id])
140 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
140 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
141 respond_to do |format|
141 respond_to do |format|
142 format.js { }
142 format.js { }
143 end
143 end
144 end
144 end
145
145
146 def turn_all_off
146 def turn_all_off
147 Problem.available.all.each do |problem|
147 Problem.available.all.each do |problem|
148 problem.available = false
148 problem.available = false
149 problem.save
149 problem.save
150 end
150 end
151 redirect_to action: :index
151 redirect_to action: :index
152 end
152 end
153
153
154 def turn_all_on
154 def turn_all_on
155 Problem.where.not(available: true).each do |problem|
155 Problem.where.not(available: true).each do |problem|
156 problem.available = true
156 problem.available = true
157 problem.save
157 problem.save
158 end
158 end
159 redirect_to action: :index
159 redirect_to action: :index
160 end
160 end
161
161
162 def stat
162 def stat
163 @problem = Problem.find(params[:id])
163 @problem = Problem.find(params[:id])
164 unless @problem.available or session[:admin]
164 unless @problem.available or session[:admin]
165 redirect_to :controller => 'main', :action => 'list'
165 redirect_to :controller => 'main', :action => 'list'
166 return
166 return
167 end
167 end
168 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
168 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
169
169
170 #stat summary
170 #stat summary
171 range =65
171 range =65
172 @histogram = { data: Array.new(range,0), summary: {} }
172 @histogram = { data: Array.new(range,0), summary: {} }
173 user = Hash.new(0)
173 user = Hash.new(0)
174 @submissions.find_each do |sub|
174 @submissions.find_each do |sub|
175 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
175 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
176 @histogram[:data][d.to_i] += 1 if d < range
176 @histogram[:data][d.to_i] += 1 if d < range
177 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
177 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
178 end
178 end
179 @histogram[:summary][:max] = [@histogram[:data].max,1].max
179 @histogram[:summary][:max] = [@histogram[:data].max,1].max
180
180
181 @summary = { attempt: user.count, solve: 0 }
181 @summary = { attempt: user.count, solve: 0 }
182 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
182 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
183 end
183 end
184
184
185 def manage
185 def manage
186 @problems = Problem.order(date_added: :desc)
186 @problems = Problem.order(date_added: :desc)
187 end
187 end
188
188
189 def do_manage
189 def do_manage
190 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
190 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
191 change_date_added
191 change_date_added
192 elsif params.has_key? 'add_to_contest'
192 elsif params.has_key? 'add_to_contest'
193 add_to_contest
193 add_to_contest
194 elsif params.has_key? 'enable_problem'
194 elsif params.has_key? 'enable_problem'
195 set_available(true)
195 set_available(true)
196 elsif params.has_key? 'disable_problem'
196 elsif params.has_key? 'disable_problem'
197 set_available(false)
197 set_available(false)
198 elsif params.has_key? 'add_group'
198 elsif params.has_key? 'add_group'
199 group = Group.find(params[:group_id])
199 group = Group.find(params[:group_id])
200 ok = []
200 ok = []
201 failed = []
201 failed = []
202 get_problems_from_params.each do |p|
202 get_problems_from_params.each do |p|
203 begin
203 begin
204 group.problems << p
204 group.problems << p
205 ok << p.full_name
205 ok << p.full_name
206 rescue => e
206 rescue => e
207 failed << p.full_name
207 failed << p.full_name
208 end
208 end
209 end
209 end
210 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
210 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
211 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
211 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
212 elsif params.has_key? 'add_tags'
212 elsif params.has_key? 'add_tags'
213 get_problems_from_params.each do |p|
213 get_problems_from_params.each do |p|
214 p.tag_ids += params[:tag_ids]
214 p.tag_ids += params[:tag_ids]
215 end
215 end
216 end
216 end
217
217
218 redirect_to :action => 'manage'
218 redirect_to :action => 'manage'
219 end
219 end
220
220
221 def import
221 def import
222 @allow_test_pair_import = allow_test_pair_import?
222 @allow_test_pair_import = allow_test_pair_import?
223 end
223 end
224
224
225 def do_import
225 def do_import
226 old_problem = Problem.find_by_name(params[:name])
226 old_problem = Problem.find_by_name(params[:name])
227 if !allow_test_pair_import? and params.has_key? :import_to_db
227 if !allow_test_pair_import? and params.has_key? :import_to_db
228 params.delete :import_to_db
228 params.delete :import_to_db
229 end
229 end
230 @problem, import_log = Problem.create_from_import_form_params(params,
230 @problem, import_log = Problem.create_from_import_form_params(params,
231 old_problem)
231 old_problem)
232
232
233 if !@problem.errors.empty?
233 if !@problem.errors.empty?
234 render :action => 'import' and return
234 render :action => 'import' and return
235 end
235 end
236
236
237 if old_problem!=nil
237 if old_problem!=nil
238 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
238 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
239 end
239 end
240 @log = import_log
240 @log = import_log
241 end
241 end
242
242
243 def remove_contest
243 def remove_contest
244 problem = Problem.find(params[:id])
244 problem = Problem.find(params[:id])
245 contest = Contest.find(params[:contest_id])
245 contest = Contest.find(params[:contest_id])
246 if problem!=nil and contest!=nil
246 if problem!=nil and contest!=nil
247 problem.contests.delete(contest)
247 problem.contests.delete(contest)
248 end
248 end
249 redirect_to :action => 'manage'
249 redirect_to :action => 'manage'
250 end
250 end
251
251
252 ##################################
252 ##################################
253 protected
253 protected
254
254
255 + def description_params
256 + params.require(:description).permit(:body, :markdowned)
257 + end
258 +
255 def allow_test_pair_import?
259 def allow_test_pair_import?
256 if defined? ALLOW_TEST_PAIR_IMPORT
260 if defined? ALLOW_TEST_PAIR_IMPORT
257 return ALLOW_TEST_PAIR_IMPORT
261 return ALLOW_TEST_PAIR_IMPORT
258 else
262 else
259 return false
263 return false
260 end
264 end
261 end
265 end
262
266
263 def change_date_added
267 def change_date_added
264 problems = get_problems_from_params
268 problems = get_problems_from_params
265 date = Date.parse(params[:date_added])
269 date = Date.parse(params[:date_added])
266 problems.each do |p|
270 problems.each do |p|
267 p.date_added = date
271 p.date_added = date
268 p.save
272 p.save
269 end
273 end
270 end
274 end
271
275
272 def add_to_contest
276 def add_to_contest
273 problems = get_problems_from_params
277 problems = get_problems_from_params
274 contest = Contest.find(params[:contest][:id])
278 contest = Contest.find(params[:contest][:id])
275 if contest!=nil and contest.enabled
279 if contest!=nil and contest.enabled
276 problems.each do |p|
280 problems.each do |p|
277 p.contests << contest
281 p.contests << contest
278 end
282 end
279 end
283 end
280 end
284 end
281
285
282 def set_available(avail)
286 def set_available(avail)
283 problems = get_problems_from_params
287 problems = get_problems_from_params
284 problems.each do |p|
288 problems.each do |p|
285 p.available = avail
289 p.available = avail
286 p.save
290 p.save
287 end
291 end
288 end
292 end
289
293
290 def get_problems_from_params
294 def get_problems_from_params
291 problems = []
295 problems = []
292 params.keys.each do |k|
296 params.keys.each do |k|
293 if k.index('prob-')==0
297 if k.index('prob-')==0
294 name, id, order = k.split('-')
298 name, id, order = k.split('-')
295 problems << Problem.find(id)
299 problems << Problem.find(id)
296 end
300 end
297 end
301 end
298 problems
302 problems
299 end
303 end
300
304
301 def get_problems_stat
305 def get_problems_stat
302 end
306 end
303
307
304 private
308 private
305
309
306 def problem_params
310 def problem_params
307 params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[])
311 params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[])
308 end
312 end
309
313
310 end
314 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 - self.language = Submission.find_language_in_source(self.source,
130 + if self.language == nil
131 - self.source_filename)
131 + self.language = Submission.find_language_in_source(self.source,
132 + self.source_filename)
133 + end
132 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); uniq}
30 has_and_belongs_to_many :contests, -> { order(:name); uniq}
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.detect {|r| r.name == 'admin' }
84 self.roles.detect {|r| r.name == 'admin' }
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(true)==nil
176 return false if self.contest_stat(true)==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,254 +1,256
1 %h2 Live submit
1 %h2 Live submit
2 %br
2 %br
3
3
4 %textarea#text_sourcecode{style: "display:none"}~ @source
4 %textarea#text_sourcecode{style: "display:none"}~ @source
5 .container
5 .container
6 .row
6 .row
7 .col-md-12
7 .col-md-12
8 .alert.alert-info
8 .alert.alert-info
9 Write your code in the following box, choose language, and click submit button when finished
9 Write your code in the following box, choose language, and click submit button when finished
10 .row
10 .row
11 .col-md-8
11 .col-md-8
12 %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'}
12 %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'}
13 .col-md-4
13 .col-md-4
14 - # submission form
14 - # submission form
15 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
15 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
16
16
17 = hidden_field_tag 'editor_text', @source
17 = hidden_field_tag 'editor_text', @source
18 = hidden_field_tag 'submission[problem_id]', @problem.id
18 = hidden_field_tag 'submission[problem_id]', @problem.id
19 .form-group
19 .form-group
20 = label_tag "Task:"
20 = label_tag "Task:"
21 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
21 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
22 .form-group
22 .form-group
23 = label_tag "Description:"
23 = label_tag "Description:"
24 = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @problem
24 = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @problem
25
25
26 .form-group
26 .form-group
27 = label_tag 'Language:'
27 = label_tag 'Language:'
28 = 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"
28 = 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 .form-group
29 .form-group
30 .input-group
30 .input-group
31 %span.input-group-btn
31 %span.input-group-btn
32 %span.btn.btn-default.btn-file
32 %span.btn.btn-default.btn-file
33 Browse
33 Browse
34 = file_field_tag 'load_file'
34 = file_field_tag 'load_file'
35 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
35 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
36 .form-group
36 .form-group
37 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
37 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
38 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
38 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
39 - # latest submission status
39 - # latest submission status
40 .panel{class: (@submission && @submission.graded_at) ? "panel-info" : "panel-warning"}
40 .panel{class: (@submission && @submission.graded_at) ? "panel-info" : "panel-warning"}
41 .panel-heading
41 .panel-heading
42 Latest Submission Status
42 Latest Submission Status
43 = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission
43 = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission
44 .panel-body
44 .panel-body
45 %div#latest_status
45 %div#latest_status
46 - if @submission
46 - if @submission
47 = render :partial => 'submission_short',
47 = render :partial => 'submission_short',
48 :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
48 :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
49 .row
49 .row
50 .col-md-12
50 .col-md-12
51 %h2 Console
51 %h2 Console
52 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
52 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
53
53
54 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
54 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
55 .modal-dialog.modal-lg{role:'document'}
55 .modal-dialog.modal-lg{role:'document'}
56 .modal-content
56 .modal-content
57 .modal-header
57 .modal-header
58 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
58 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
59 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
59 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
60 %h4 Compiler message
60 %h4 Compiler message
61 .modal-body
61 .modal-body
62 - %pre#compiler_msg= @submission.compiler_message
62 + %pre#compiler_msg
63 + - if @submission
64 + = @submission.compiler_message
63 .modal-footer
65 .modal-footer
64 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
66 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
65
67
66 :javascript
68 :javascript
67 $(document).ready(function() {
69 $(document).ready(function() {
68 e = ace.edit("editor")
70 e = ace.edit("editor")
69 e.setValue($("#text_sourcecode").val());
71 e.setValue($("#text_sourcecode").val());
70 e.gotoLine(1);
72 e.gotoLine(1);
71 $("#language_id").trigger('change');
73 $("#language_id").trigger('change');
72
74
73 $("#load_file").on('change',function(evt) {
75 $("#load_file").on('change',function(evt) {
74 var file = evt.target.files[0];
76 var file = evt.target.files[0];
75 var reader = new FileReader();
77 var reader = new FileReader();
76 reader.onload = function(theFile) {
78 reader.onload = function(theFile) {
77 var e = ace.edit("editor")
79 var e = ace.edit("editor")
78 e.setValue(theFile.target.result);
80 e.setValue(theFile.target.result);
79 e.gotoLine(1);
81 e.gotoLine(1);
80 };
82 };
81 reader.readAsText(file)
83 reader.readAsText(file)
82 });
84 });
83
85
84 //brython();
86 //brython();
85 });
87 });
86
88
87
89
88
90
89
91
90
92
91 %script#__main__{type:'text/python3'}
93 %script#__main__{type:'text/python3'}
92 :plain
94 :plain
93 import sys
95 import sys
94 import traceback
96 import traceback
95
97
96 from browser import document as doc
98 from browser import document as doc
97 from browser import window, alert, console
99 from browser import window, alert, console
98
100
99 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
101 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
100 for supporting Python development. See www.python.org for more information."""
102 for supporting Python development. See www.python.org for more information."""
101
103
102 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
104 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
103 All Rights Reserved.
105 All Rights Reserved.
104
106
105 Copyright (c) 2001-2013 Python Software Foundation.
107 Copyright (c) 2001-2013 Python Software Foundation.
106 All Rights Reserved.
108 All Rights Reserved.
107
109
108 Copyright (c) 2000 BeOpen.com.
110 Copyright (c) 2000 BeOpen.com.
109 All Rights Reserved.
111 All Rights Reserved.
110
112
111 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
113 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
112 All Rights Reserved.
114 All Rights Reserved.
113
115
114 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
116 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
115 All Rights Reserved."""
117 All Rights Reserved."""
116
118
117 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
119 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
118 All rights reserved.
120 All rights reserved.
119
121
120 Redistribution and use in source and binary forms, with or without
122 Redistribution and use in source and binary forms, with or without
121 modification, are permitted provided that the following conditions are met:
123 modification, are permitted provided that the following conditions are met:
122
124
123 Redistributions of source code must retain the above copyright notice, this
125 Redistributions of source code must retain the above copyright notice, this
124 list of conditions and the following disclaimer. Redistributions in binary
126 list of conditions and the following disclaimer. Redistributions in binary
125 form must reproduce the above copyright notice, this list of conditions and
127 form must reproduce the above copyright notice, this list of conditions and
126 the following disclaimer in the documentation and/or other materials provided
128 the following disclaimer in the documentation and/or other materials provided
127 with the distribution.
129 with the distribution.
128 Neither the name of the <ORGANIZATION> nor the names of its contributors may
130 Neither the name of the <ORGANIZATION> nor the names of its contributors may
129 be used to endorse or promote products derived from this software without
131 be used to endorse or promote products derived from this software without
130 specific prior written permission.
132 specific prior written permission.
131
133
132 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
134 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
133 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
135 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
134 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
136 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
135 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
137 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
136 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
138 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
137 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
139 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
138 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
140 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
139 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
141 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
140 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
142 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
141 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
143 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
142 POSSIBILITY OF SUCH DAMAGE.
144 POSSIBILITY OF SUCH DAMAGE.
143 """
145 """
144
146
145 def credits():
147 def credits():
146 print(_credits)
148 print(_credits)
147 credits.__repr__ = lambda:_credits
149 credits.__repr__ = lambda:_credits
148
150
149 def copyright():
151 def copyright():
150 print(_copyright)
152 print(_copyright)
151 copyright.__repr__ = lambda:_copyright
153 copyright.__repr__ = lambda:_copyright
152
154
153 def license():
155 def license():
154 print(_license)
156 print(_license)
155 license.__repr__ = lambda:_license
157 license.__repr__ = lambda:_license
156
158
157 def write(data):
159 def write(data):
158 doc['console'].value += str(data)
160 doc['console'].value += str(data)
159
161
160
162
161 sys.stdout.write = sys.stderr.write = write
163 sys.stdout.write = sys.stderr.write = write
162 history = []
164 history = []
163 current = 0
165 current = 0
164 _status = "main" # or "block" if typing inside a block
166 _status = "main" # or "block" if typing inside a block
165
167
166 # execution namespace
168 # execution namespace
167 editor_ns = {'credits':credits,
169 editor_ns = {'credits':credits,
168 'copyright':copyright,
170 'copyright':copyright,
169 'license':license,
171 'license':license,
170 '__name__':'__main__'}
172 '__name__':'__main__'}
171
173
172 def cursorToEnd(*args):
174 def cursorToEnd(*args):
173 pos = len(doc['console'].value)
175 pos = len(doc['console'].value)
174 doc['console'].setSelectionRange(pos, pos)
176 doc['console'].setSelectionRange(pos, pos)
175 doc['console'].scrollTop = doc['console'].scrollHeight
177 doc['console'].scrollTop = doc['console'].scrollHeight
176
178
177 def get_col(area):
179 def get_col(area):
178 # returns the column num of cursor
180 # returns the column num of cursor
179 sel = doc['console'].selectionStart
181 sel = doc['console'].selectionStart
180 lines = doc['console'].value.split('\n')
182 lines = doc['console'].value.split('\n')
181 for line in lines[:-1]:
183 for line in lines[:-1]:
182 sel -= len(line) + 1
184 sel -= len(line) + 1
183 return sel
185 return sel
184
186
185
187
186 def myKeyPress(event):
188 def myKeyPress(event):
187 global _status, current
189 global _status, current
188 if event.keyCode == 9: # tab key
190 if event.keyCode == 9: # tab key
189 event.preventDefault()
191 event.preventDefault()
190 doc['console'].value += " "
192 doc['console'].value += " "
191 elif event.keyCode == 13: # return
193 elif event.keyCode == 13: # return
192 src = doc['console'].value
194 src = doc['console'].value
193 if _status == "main":
195 if _status == "main":
194 currentLine = src[src.rfind('>>>') + 4:]
196 currentLine = src[src.rfind('>>>') + 4:]
195 elif _status == "3string":
197 elif _status == "3string":
196 currentLine = src[src.rfind('>>>') + 4:]
198 currentLine = src[src.rfind('>>>') + 4:]
197 currentLine = currentLine.replace('\n... ', '\n')
199 currentLine = currentLine.replace('\n... ', '\n')
198 else:
200 else:
199 currentLine = src[src.rfind('...') + 4:]
201 currentLine = src[src.rfind('...') + 4:]
200 if _status == 'main' and not currentLine.strip():
202 if _status == 'main' and not currentLine.strip():
201 doc['console'].value += '\n>>> '
203 doc['console'].value += '\n>>> '
202 event.preventDefault()
204 event.preventDefault()
203 return
205 return
204 doc['console'].value += '\n'
206 doc['console'].value += '\n'
205 history.append(currentLine)
207 history.append(currentLine)
206 current = len(history)
208 current = len(history)
207 if _status == "main" or _status == "3string":
209 if _status == "main" or _status == "3string":
208 try:
210 try:
209 _ = editor_ns['_'] = eval(currentLine, editor_ns)
211 _ = editor_ns['_'] = eval(currentLine, editor_ns)
210 if _ is not None:
212 if _ is not None:
211 write(repr(_)+'\n')
213 write(repr(_)+'\n')
212 doc['console'].value += '>>> '
214 doc['console'].value += '>>> '
213 _status = "main"
215 _status = "main"
214 except IndentationError:
216 except IndentationError:
215 doc['console'].value += '... '
217 doc['console'].value += '... '
216 _status = "block"
218 _status = "block"
217 except SyntaxError as msg:
219 except SyntaxError as msg:
218 if str(msg) == 'invalid syntax : triple string end not found' or \
220 if str(msg) == 'invalid syntax : triple string end not found' or \
219 str(msg).startswith('Unbalanced bracket'):
221 str(msg).startswith('Unbalanced bracket'):
220 doc['console'].value += '... '
222 doc['console'].value += '... '
221 _status = "3string"
223 _status = "3string"
222 elif str(msg) == 'eval() argument must be an expression':
224 elif str(msg) == 'eval() argument must be an expression':
223 try:
225 try:
224 exec(currentLine, editor_ns)
226 exec(currentLine, editor_ns)
225 except:
227 except:
226 traceback.print_exc()
228 traceback.print_exc()
227 doc['console'].value += '>>> '
229 doc['console'].value += '>>> '
228 _status = "main"
230 _status = "main"
229 elif str(msg) == 'decorator expects function':
231 elif str(msg) == 'decorator expects function':
230 doc['console'].value += '... '
232 doc['console'].value += '... '
231 _status = "block"
233 _status = "block"
232 else:
234 else:
233 traceback.print_exc()
235 traceback.print_exc()
234 doc['console'].value += '>>> '
236 doc['console'].value += '>>> '
235 _status = "main"
237 _status = "main"
236 except:
238 except:
237 traceback.print_exc()
239 traceback.print_exc()
238 doc['console'].value += '>>> '
240 doc['console'].value += '>>> '
239 _status = "main"
241 _status = "main"
240 elif currentLine == "": # end of block
242 elif currentLine == "": # end of block
241 block = src[src.rfind('>>>') + 4:].splitlines()
243 block = src[src.rfind('>>>') + 4:].splitlines()
242 block = [block[0]] + [b[4:] for b in block[1:]]
244 block = [block[0]] + [b[4:] for b in block[1:]]
243 block_src = '\n'.join(block)
245 block_src = '\n'.join(block)
244 # status must be set before executing code in globals()
246 # status must be set before executing code in globals()
245 _status = "main"
247 _status = "main"
246 try:
248 try:
247 _ = exec(block_src, editor_ns)
249 _ = exec(block_src, editor_ns)
248 if _ is not None:
250 if _ is not None:
249 print(repr(_))
251 print(repr(_))
250 except:
252 except:
251 traceback.print_exc()
253 traceback.print_exc()
252 doc['console'].value += '>>> '
254 doc['console'].value += '>>> '
253 else:
255 else:
254 doc['console'].value += '... '
256 doc['console'].value += '... '
@@ -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,267 +1,275
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 ]
185 ]
178
186
179
187
180 def create_configuration_key(key,
188 def create_configuration_key(key,
181 value_type,
189 value_type,
182 default_value,
190 default_value,
183 description='')
191 description='')
184 conf = (GraderConfiguration.find_by_key(key) ||
192 conf = (GraderConfiguration.find_by_key(key) ||
185 GraderConfiguration.new(:key => key,
193 GraderConfiguration.new(:key => key,
186 :value_type => value_type,
194 :value_type => value_type,
187 :value => default_value))
195 :value => default_value))
188 conf.description = description
196 conf.description = description
189 conf.save
197 conf.save
190 end
198 end
191
199
192 def seed_config
200 def seed_config
193 CONFIGURATIONS.each do |conf|
201 CONFIGURATIONS.each do |conf|
194 if conf.has_key? :description
202 if conf.has_key? :description
195 desc = conf[:description]
203 desc = conf[:description]
196 else
204 else
197 desc = ''
205 desc = ''
198 end
206 end
199 create_configuration_key(conf[:key],
207 create_configuration_key(conf[:key],
200 conf[:value_type],
208 conf[:value_type],
201 conf[:default_value],
209 conf[:default_value],
202 desc)
210 desc)
203 end
211 end
204 end
212 end
205
213
206 def seed_roles
214 def seed_roles
207 return if Role.find_by_name('admin')
215 return if Role.find_by_name('admin')
208
216
209 role = Role.create(:name => 'admin')
217 role = Role.create(:name => 'admin')
210 user_admin_right = Right.create(:name => 'user_admin',
218 user_admin_right = Right.create(:name => 'user_admin',
211 :controller => 'user_admin',
219 :controller => 'user_admin',
212 :action => 'all')
220 :action => 'all')
213 problem_admin_right = Right.create(:name=> 'problem_admin',
221 problem_admin_right = Right.create(:name=> 'problem_admin',
214 :controller => 'problems',
222 :controller => 'problems',
215 :action => 'all')
223 :action => 'all')
216
224
217 graders_right = Right.create(:name => 'graders_admin',
225 graders_right = Right.create(:name => 'graders_admin',
218 :controller => 'graders',
226 :controller => 'graders',
219 :action => 'all')
227 :action => 'all')
220
228
221 role.rights << user_admin_right;
229 role.rights << user_admin_right;
222 role.rights << problem_admin_right;
230 role.rights << problem_admin_right;
223 role.rights << graders_right;
231 role.rights << graders_right;
224 role.save
232 role.save
225 end
233 end
226
234
227 def seed_root
235 def seed_root
228 return if User.find_by_login('root')
236 return if User.find_by_login('root')
229
237
230 root = User.new(:login => 'root',
238 root = User.new(:login => 'root',
231 :full_name => 'Administrator',
239 :full_name => 'Administrator',
232 :alias => 'root')
240 :alias => 'root')
233 root.password = 'ioionrails';
241 root.password = 'ioionrails';
234
242
235 class << root
243 class << root
236 public :encrypt_new_password
244 public :encrypt_new_password
237 def valid?(context=nil)
245 def valid?(context=nil)
238 true
246 true
239 end
247 end
240 end
248 end
241
249
242 root.encrypt_new_password
250 root.encrypt_new_password
243
251
244 root.roles << Role.find_by_name('admin')
252 root.roles << Role.find_by_name('admin')
245
253
246 root.activated = true
254 root.activated = true
247 root.save
255 root.save
248 end
256 end
249
257
250 def seed_users_and_roles
258 def seed_users_and_roles
251 seed_roles
259 seed_roles
252 seed_root
260 seed_root
253 end
261 end
254
262
255 def seed_more_languages
263 def seed_more_languages
256 Language.delete_all
264 Language.delete_all
257 Language.create( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
265 Language.create( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
258 Language.create( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
266 Language.create( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
259 Language.create( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
267 Language.create( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
260 Language.create( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
268 Language.create( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
261 Language.create( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
269 Language.create( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
262 Language.create( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
270 Language.create( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
263 end
271 end
264
272
265 seed_config
273 seed_config
266 seed_users_and_roles
274 seed_users_and_roles
267 seed_more_languages
275 seed_more_languages
You need to be logged in to leave comments. Login now