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 1 class LoginController < ApplicationController
2 2
3 + @@authenticators = []
4 +
3 5 def index
4 6 # show login screen
5 7 reset_session
6 8 redirect_to :controller => 'main', :action => 'login'
7 9 end
8 10
9 11 def login
10 - user = User.authenticate(params[:login], params[:password])
12 + user = get_authenticated_user(params[:login], params[:password])
11 13 unless user
12 14 flash[:notice] = 'Wrong password'
13 15 redirect_to :controller => 'main', :action => 'login'
14 16 return
15 17 end
16 18
17 19 if (!GraderConfiguration['right.bypass_agreement']) and (!params[:accept_agree]) and !user.admin?
18 20 flash[:notice] = 'You must accept the agreement before logging in'
19 21 redirect_to :controller => 'main', :action => 'login'
20 22 return
21 23 end
22 24
23 25 #process logging in
24 26 session[:user_id] = user.id
25 27 session[:admin] = user.admin?
26 28
27 29 # clear forced logout flag for multicontests contest change
28 30 if GraderConfiguration.multicontests?
29 31 contest_stat = user.contest_stat
30 32 if contest_stat.respond_to? :forced_logout
31 33 if contest_stat.forced_logout
32 34 contest_stat.forced_logout = false
33 35 contest_stat.save
34 36 end
35 37 end
36 38 end
37 39
38 40 #save login information
39 41 Login.create(user_id: user.id, ip_address: request.remote_ip)
40 42
41 43 redirect_to :controller => 'main', :action => 'list'
42 44 end
43 45
44 46 def site_login
45 47 begin
46 48 site = Site.find(params[:login][:site_id])
47 49 rescue ActiveRecord::RecordNotFound
48 50 site = nil
49 51 end
50 52 if site==nil
51 53 flash[:notice] = 'Wrong site'
52 54 redirect_to :controller => 'main', :action => 'login' and return
53 55 end
54 56 if (site.password) and (site.password == params[:login][:password])
55 57 session[:site_id] = site.id
56 58 redirect_to :controller => 'site', :action => 'index'
57 59 else
58 60 flash[:notice] = 'Wrong site password'
59 61 redirect_to :controller => 'site', :action => 'login'
60 62 end
61 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 85 end
@@ -1,310 +1,314
1 1 class ProblemsController < ApplicationController
2 2
3 3 before_action :authenticate, :authorization
4 4 before_action :testcase_authorization, only: [:show_testcase]
5 5
6 6 in_place_edit_for :problem, :name
7 7 in_place_edit_for :problem, :full_name
8 8 in_place_edit_for :problem, :full_score
9 9
10 10 def index
11 11 @problems = Problem.order(date_added: :desc)
12 12 end
13 13
14 14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
15 15 verify :method => :post, :only => [ :create, :quick_create,
16 16 :do_manage,
17 17 :do_import,
18 18 ],
19 19 :redirect_to => { :action => :index }
20 20
21 21 def show
22 22 @problem = Problem.find(params[:id])
23 23 end
24 24
25 25 def new
26 26 @problem = Problem.new
27 27 @description = nil
28 28 end
29 29
30 30 def create
31 31 @problem = Problem.new(problem_params)
32 - @description = Description.new(params[:description])
32 + @description = Description.new(description_params)
33 33 if @description.body!=''
34 34 if !@description.save
35 35 render :action => new and return
36 36 end
37 37 else
38 38 @description = nil
39 39 end
40 40 @problem.description = @description
41 41 if @problem.save
42 42 flash[:notice] = 'Problem was successfully created.'
43 43 redirect_to action: :index
44 44 else
45 45 render :action => 'new'
46 46 end
47 47 end
48 48
49 49 def quick_create
50 50 @problem = Problem.new(problem_params)
51 51 @problem.full_name = @problem.name if @problem.full_name == ''
52 52 @problem.full_score = 100
53 53 @problem.available = false
54 54 @problem.test_allowed = true
55 55 @problem.output_only = false
56 56 @problem.date_added = Time.new
57 57 if @problem.save
58 58 flash[:notice] = 'Problem was successfully created.'
59 59 redirect_to action: :index
60 60 else
61 61 flash[:notice] = 'Error saving problem'
62 62 redirect_to action: :index
63 63 end
64 64 end
65 65
66 66 def edit
67 67 @problem = Problem.find(params[:id])
68 68 @description = @problem.description
69 69 end
70 70
71 71 def update
72 72 @problem = Problem.find(params[:id])
73 73 @description = @problem.description
74 74 if @description.nil? and params[:description][:body]!=''
75 75 @description = Description.new(params[:description])
76 76 if !@description.save
77 77 flash[:notice] = 'Error saving description'
78 78 render :action => 'edit' and return
79 79 end
80 80 @problem.description = @description
81 81 elsif @description
82 82 if !@description.update_attributes(params[:description])
83 83 flash[:notice] = 'Error saving description'
84 84 render :action => 'edit' and return
85 85 end
86 86 end
87 87 if params[:file] and params[:file].content_type != 'application/pdf'
88 88 flash[:notice] = 'Error: Uploaded file is not PDF'
89 89 render :action => 'edit' and return
90 90 end
91 91 if @problem.update_attributes(problem_params)
92 92 flash[:notice] = 'Problem was successfully updated.'
93 93 unless params[:file] == nil or params[:file] == ''
94 94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
95 95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
96 96 if not FileTest.exists? out_dirname
97 97 Dir.mkdir out_dirname
98 98 end
99 99
100 100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
101 101 if FileTest.exists? out_filename
102 102 File.delete out_filename
103 103 end
104 104
105 105 File.open(out_filename,"wb") do |file|
106 106 file.write(params[:file].read)
107 107 end
108 108 @problem.description_filename = "#{@problem.name}.pdf"
109 109 @problem.save
110 110 end
111 111 redirect_to :action => 'show', :id => @problem
112 112 else
113 113 render :action => 'edit'
114 114 end
115 115 end
116 116
117 117 def destroy
118 118 p = Problem.find(params[:id]).destroy
119 119 redirect_to action: :index
120 120 end
121 121
122 122 def toggle
123 123 @problem = Problem.find(params[:id])
124 124 @problem.update_attributes(available: !(@problem.available) )
125 125 respond_to do |format|
126 126 format.js { }
127 127 end
128 128 end
129 129
130 130 def toggle_test
131 131 @problem = Problem.find(params[:id])
132 132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
133 133 respond_to do |format|
134 134 format.js { }
135 135 end
136 136 end
137 137
138 138 def toggle_view_testcase
139 139 @problem = Problem.find(params[:id])
140 140 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
141 141 respond_to do |format|
142 142 format.js { }
143 143 end
144 144 end
145 145
146 146 def turn_all_off
147 147 Problem.available.all.each do |problem|
148 148 problem.available = false
149 149 problem.save
150 150 end
151 151 redirect_to action: :index
152 152 end
153 153
154 154 def turn_all_on
155 155 Problem.where.not(available: true).each do |problem|
156 156 problem.available = true
157 157 problem.save
158 158 end
159 159 redirect_to action: :index
160 160 end
161 161
162 162 def stat
163 163 @problem = Problem.find(params[:id])
164 164 unless @problem.available or session[:admin]
165 165 redirect_to :controller => 'main', :action => 'list'
166 166 return
167 167 end
168 168 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
169 169
170 170 #stat summary
171 171 range =65
172 172 @histogram = { data: Array.new(range,0), summary: {} }
173 173 user = Hash.new(0)
174 174 @submissions.find_each do |sub|
175 175 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
176 176 @histogram[:data][d.to_i] += 1 if d < range
177 177 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
178 178 end
179 179 @histogram[:summary][:max] = [@histogram[:data].max,1].max
180 180
181 181 @summary = { attempt: user.count, solve: 0 }
182 182 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
183 183 end
184 184
185 185 def manage
186 186 @problems = Problem.order(date_added: :desc)
187 187 end
188 188
189 189 def do_manage
190 190 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
191 191 change_date_added
192 192 elsif params.has_key? 'add_to_contest'
193 193 add_to_contest
194 194 elsif params.has_key? 'enable_problem'
195 195 set_available(true)
196 196 elsif params.has_key? 'disable_problem'
197 197 set_available(false)
198 198 elsif params.has_key? 'add_group'
199 199 group = Group.find(params[:group_id])
200 200 ok = []
201 201 failed = []
202 202 get_problems_from_params.each do |p|
203 203 begin
204 204 group.problems << p
205 205 ok << p.full_name
206 206 rescue => e
207 207 failed << p.full_name
208 208 end
209 209 end
210 210 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
211 211 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
212 212 elsif params.has_key? 'add_tags'
213 213 get_problems_from_params.each do |p|
214 214 p.tag_ids += params[:tag_ids]
215 215 end
216 216 end
217 217
218 218 redirect_to :action => 'manage'
219 219 end
220 220
221 221 def import
222 222 @allow_test_pair_import = allow_test_pair_import?
223 223 end
224 224
225 225 def do_import
226 226 old_problem = Problem.find_by_name(params[:name])
227 227 if !allow_test_pair_import? and params.has_key? :import_to_db
228 228 params.delete :import_to_db
229 229 end
230 230 @problem, import_log = Problem.create_from_import_form_params(params,
231 231 old_problem)
232 232
233 233 if !@problem.errors.empty?
234 234 render :action => 'import' and return
235 235 end
236 236
237 237 if old_problem!=nil
238 238 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
239 239 end
240 240 @log = import_log
241 241 end
242 242
243 243 def remove_contest
244 244 problem = Problem.find(params[:id])
245 245 contest = Contest.find(params[:contest_id])
246 246 if problem!=nil and contest!=nil
247 247 problem.contests.delete(contest)
248 248 end
249 249 redirect_to :action => 'manage'
250 250 end
251 251
252 252 ##################################
253 253 protected
254 254
255 + def description_params
256 + params.require(:description).permit(:body, :markdowned)
257 + end
258 +
255 259 def allow_test_pair_import?
256 260 if defined? ALLOW_TEST_PAIR_IMPORT
257 261 return ALLOW_TEST_PAIR_IMPORT
258 262 else
259 263 return false
260 264 end
261 265 end
262 266
263 267 def change_date_added
264 268 problems = get_problems_from_params
265 269 date = Date.parse(params[:date_added])
266 270 problems.each do |p|
267 271 p.date_added = date
268 272 p.save
269 273 end
270 274 end
271 275
272 276 def add_to_contest
273 277 problems = get_problems_from_params
274 278 contest = Contest.find(params[:contest][:id])
275 279 if contest!=nil and contest.enabled
276 280 problems.each do |p|
277 281 p.contests << contest
278 282 end
279 283 end
280 284 end
281 285
282 286 def set_available(avail)
283 287 problems = get_problems_from_params
284 288 problems.each do |p|
285 289 p.available = avail
286 290 p.save
287 291 end
288 292 end
289 293
290 294 def get_problems_from_params
291 295 problems = []
292 296 params.keys.each do |k|
293 297 if k.index('prob-')==0
294 298 name, id, order = k.split('-')
295 299 problems << Problem.find(id)
296 300 end
297 301 end
298 302 problems
299 303 end
300 304
301 305 def get_problems_stat
302 306 end
303 307
304 308 private
305 309
306 310 def problem_params
307 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 312 end
309 313
310 314 end
@@ -1,166 +1,168
1 1 class Submission < ActiveRecord::Base
2 2
3 3 belongs_to :language
4 4 belongs_to :problem
5 5 belongs_to :user
6 6
7 7 before_validation :assign_problem
8 8 before_validation :assign_language
9 9
10 10 validates_presence_of :source
11 11 validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'code too long, the limit is 100,000 bytes'
12 12 validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short'
13 13 validate :must_have_valid_problem
14 14 validate :must_specify_language
15 15
16 16 has_one :task
17 17
18 18 before_save :assign_latest_number_if_new_recond
19 19
20 20 def self.find_last_by_user_and_problem(user_id, problem_id)
21 21 where("user_id = ? AND problem_id = ?",user_id,problem_id).last
22 22 end
23 23
24 24 def self.find_all_last_by_problem(problem_id)
25 25 # need to put in SQL command, maybe there's a better way
26 26 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
27 27 "WHERE id = " +
28 28 "(SELECT MAX(id) FROM submissions AS subs " +
29 29 "WHERE subs.user_id = submissions.user_id AND " +
30 30 "problem_id = " + problem_id.to_s + " " +
31 31 "GROUP BY user_id) " +
32 32 "ORDER BY user_id")
33 33 end
34 34
35 35 def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id)
36 36 records = Submission.where(problem_id: problem_id,user_id: user_id)
37 37 records = records.where('id >= ?',since_id) if since_id and since_id > 0
38 38 records = records.where('id <= ?',until_id) if until_id and until_id > 0
39 39 records.all
40 40 end
41 41
42 42 def self.find_last_for_all_available_problems(user_id)
43 43 submissions = Array.new
44 44 problems = Problem.available_problems
45 45 problems.each do |problem|
46 46 sub = Submission.find_last_by_user_and_problem(user_id, problem.id)
47 47 submissions << sub if sub!=nil
48 48 end
49 49 submissions
50 50 end
51 51
52 52 def self.find_by_user_problem_number(user_id, problem_id, number)
53 53 where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first
54 54 end
55 55
56 56 def self.find_all_by_user_problem(user_id, problem_id)
57 57 where("user_id = ? AND problem_id = ?",user_id,problem_id)
58 58 end
59 59
60 60 def download_filename
61 61 if self.problem.output_only
62 62 return self.source_filename
63 63 else
64 64 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
65 65 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
66 66 end
67 67 end
68 68
69 69 protected
70 70
71 71 def self.find_option_in_source(option, source)
72 72 if source==nil
73 73 return nil
74 74 end
75 75 i = 0
76 76 source.each_line do |s|
77 77 if s =~ option
78 78 words = s.split
79 79 return words[1]
80 80 end
81 81 i = i + 1
82 82 if i==10
83 83 return nil
84 84 end
85 85 end
86 86 return nil
87 87 end
88 88
89 89 def self.find_language_in_source(source, source_filename="")
90 90 langopt = find_option_in_source(/^LANG:/,source)
91 91 if langopt
92 92 return (Language.find_by_name(langopt) ||
93 93 Language.find_by_pretty_name(langopt))
94 94 else
95 95 if source_filename
96 96 return Language.find_by_extension(source_filename.split('.').last)
97 97 else
98 98 return nil
99 99 end
100 100 end
101 101 end
102 102
103 103 def self.find_problem_in_source(source, source_filename="")
104 104 prob_opt = find_option_in_source(/^TASK:/,source)
105 105 if problem = Problem.find_by_name(prob_opt)
106 106 return problem
107 107 else
108 108 if source_filename
109 109 return Problem.find_by_name(source_filename.split('.').first)
110 110 else
111 111 return nil
112 112 end
113 113 end
114 114 end
115 115
116 116 def assign_problem
117 117 if self.problem_id!=-1
118 118 begin
119 119 self.problem = Problem.find(self.problem_id)
120 120 rescue ActiveRecord::RecordNotFound
121 121 self.problem = nil
122 122 end
123 123 else
124 124 self.problem = Submission.find_problem_in_source(self.source,
125 125 self.source_filename)
126 126 end
127 127 end
128 128
129 129 def assign_language
130 - self.language = Submission.find_language_in_source(self.source,
131 - self.source_filename)
130 + if self.language == nil
131 + self.language = Submission.find_language_in_source(self.source,
132 + self.source_filename)
133 + end
132 134 end
133 135
134 136 # validation codes
135 137 def must_specify_language
136 138 return if self.source==nil
137 139
138 140 # for output_only tasks
139 141 return if self.problem!=nil and self.problem.output_only
140 142
141 - if self.language==nil
142 - errors.add('source',"Cannot detect language. Did you submit a correct source file?") unless self.language!=nil
143 + if self.language == nil
144 + errors.add('source',"Cannot detect language. Did you submit a correct source file?")
143 145 end
144 146 end
145 147
146 148 def must_have_valid_problem
147 149 return if self.source==nil
148 150 if self.problem==nil
149 151 errors.add('problem',"must be specified.")
150 152 else
151 153 #admin always have right
152 154 return if self.user.admin?
153 155
154 156 #check if user has the right to submit the problem
155 157 errors.add('problem',"must be valid.") if (!self.user.available_problems.include?(self.problem)) and (self.new_record?)
156 158 end
157 159 end
158 160
159 161 # callbacks
160 162 def assign_latest_number_if_new_recond
161 163 return if !self.new_record?
162 164 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
163 165 self.number = (latest==nil) ? 1 : latest.number + 1;
164 166 end
165 167
166 168 end
@@ -1,369 +1,369
1 1 require 'digest/sha1'
2 2 require 'net/pop'
3 3 require 'net/https'
4 4 require 'net/http'
5 5 require 'json'
6 6
7 7 class User < ActiveRecord::Base
8 8
9 9 has_and_belongs_to_many :roles
10 10
11 11 #has_and_belongs_to_many :groups
12 12 has_many :groups_users, class_name: GroupUser
13 13 has_many :groups, :through => :groups_users
14 14
15 15 has_many :test_requests, -> {order(submitted_at: DESC)}
16 16
17 17 has_many :messages, -> { order(created_at: DESC) },
18 18 :class_name => "Message",
19 19 :foreign_key => "sender_id"
20 20
21 21 has_many :replied_messages, -> { order(created_at: DESC) },
22 22 :class_name => "Message",
23 23 :foreign_key => "receiver_id"
24 24
25 25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
26 26
27 27 belongs_to :site
28 28 belongs_to :country
29 29
30 30 has_and_belongs_to_many :contests, -> { order(:name); uniq}
31 31
32 32 scope :activated_users, -> {where activated: true}
33 33
34 34 validates_presence_of :login
35 35 validates_uniqueness_of :login
36 36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
37 37 validates_length_of :login, :within => 3..30
38 38
39 39 validates_presence_of :full_name
40 40 validates_length_of :full_name, :minimum => 1
41 41
42 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 44 validates_confirmation_of :password, :if => :password_required?
45 45
46 46 validates_format_of :email,
47 47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
48 48 :if => :email_validation?
49 49 validate :uniqueness_of_email_from_activated_users,
50 50 :if => :email_validation?
51 51 validate :enough_time_interval_between_same_email_registrations,
52 52 :if => :email_validation?
53 53
54 54 # these are for ytopc
55 55 # disable for now
56 56 #validates_presence_of :province
57 57
58 58 attr_accessor :password
59 59
60 60 before_save :encrypt_new_password
61 61 before_save :assign_default_site
62 62 before_save :assign_default_contest
63 63
64 64 # this is for will_paginate
65 65 cattr_reader :per_page
66 66 @@per_page = 50
67 67
68 68 def self.authenticate(login, password)
69 69 user = find_by_login(login)
70 70 if user
71 71 return user if user.authenticated?(password)
72 72 end
73 73 end
74 74
75 75 def authenticated?(password)
76 76 if self.activated
77 77 hashed_password == User.encrypt(password,self.salt)
78 78 else
79 79 false
80 80 end
81 81 end
82 82
83 83 def admin?
84 84 self.roles.detect {|r| r.name == 'admin' }
85 85 end
86 86
87 87 def email_for_editing
88 88 if self.email==nil
89 89 "(unknown)"
90 90 elsif self.email==''
91 91 "(blank)"
92 92 else
93 93 self.email
94 94 end
95 95 end
96 96
97 97 def email_for_editing=(e)
98 98 self.email=e
99 99 end
100 100
101 101 def alias_for_editing
102 102 if self.alias==nil
103 103 "(unknown)"
104 104 elsif self.alias==''
105 105 "(blank)"
106 106 else
107 107 self.alias
108 108 end
109 109 end
110 110
111 111 def alias_for_editing=(e)
112 112 self.alias=e
113 113 end
114 114
115 115 def activation_key
116 116 if self.hashed_password==nil
117 117 encrypt_new_password
118 118 end
119 119 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
120 120 end
121 121
122 122 def verify_activation_key(key)
123 123 key == activation_key
124 124 end
125 125
126 126 def self.random_password(length=5)
127 127 chars = 'abcdefghjkmnopqrstuvwxyz'
128 128 password = ''
129 129 length.times { password << chars[rand(chars.length - 1)] }
130 130 password
131 131 end
132 132
133 133 def self.find_non_admin_with_prefix(prefix='')
134 134 users = User.all
135 135 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
136 136 end
137 137
138 138 # Contest information
139 139
140 140 def self.find_users_with_no_contest()
141 141 users = User.all
142 142 return users.find_all { |u| u.contests.length == 0 }
143 143 end
144 144
145 145
146 146 def contest_time_left
147 147 if GraderConfiguration.contest_mode?
148 148 return nil if site==nil
149 149 return site.time_left
150 150 elsif GraderConfiguration.indv_contest_mode?
151 151 time_limit = GraderConfiguration.contest_time_limit
152 152 if time_limit == nil
153 153 return nil
154 154 end
155 155 if contest_stat==nil or contest_stat.started_at==nil
156 156 return (Time.now.gmtime + time_limit) - Time.now.gmtime
157 157 else
158 158 finish_time = contest_stat.started_at + time_limit
159 159 current_time = Time.now.gmtime
160 160 if current_time > finish_time
161 161 return 0
162 162 else
163 163 return finish_time - current_time
164 164 end
165 165 end
166 166 else
167 167 return nil
168 168 end
169 169 end
170 170
171 171 def contest_finished?
172 172 if GraderConfiguration.contest_mode?
173 173 return false if site==nil
174 174 return site.finished?
175 175 elsif GraderConfiguration.indv_contest_mode?
176 176 return false if self.contest_stat(true)==nil
177 177 return contest_time_left == 0
178 178 else
179 179 return false
180 180 end
181 181 end
182 182
183 183 def contest_started?
184 184 if GraderConfiguration.indv_contest_mode?
185 185 stat = self.contest_stat
186 186 return ((stat != nil) and (stat.started_at != nil))
187 187 elsif GraderConfiguration.contest_mode?
188 188 return true if site==nil
189 189 return site.started
190 190 else
191 191 return true
192 192 end
193 193 end
194 194
195 195 def update_start_time
196 196 stat = self.contest_stat
197 197 if stat.nil? or stat.started_at.nil?
198 198 stat ||= UserContestStat.new(:user => self)
199 199 stat.started_at = Time.now.gmtime
200 200 stat.save
201 201 end
202 202 end
203 203
204 204 def problem_in_user_contests?(problem)
205 205 problem_contests = problem.contests.all
206 206
207 207 if problem_contests.length == 0 # this is public contest
208 208 return true
209 209 end
210 210
211 211 contests.each do |contest|
212 212 if problem_contests.find {|c| c.id == contest.id }
213 213 return true
214 214 end
215 215 end
216 216 return false
217 217 end
218 218
219 219 def available_problems_group_by_contests
220 220 contest_problems = []
221 221 pin = {}
222 222 contests.enabled.each do |contest|
223 223 available_problems = contest.problems.available
224 224 contest_problems << {
225 225 :contest => contest,
226 226 :problems => available_problems
227 227 }
228 228 available_problems.each {|p| pin[p.id] = true}
229 229 end
230 230 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
231 231 contest_problems << {
232 232 :contest => nil,
233 233 :problems => other_avaiable_problems
234 234 }
235 235 return contest_problems
236 236 end
237 237
238 238 def solve_all_available_problems?
239 239 available_problems.each do |p|
240 240 u = self
241 241 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
242 242 return false if !p or !sub or sub.points < p.full_score
243 243 end
244 244 return true
245 245 end
246 246
247 247 #get a list of available problem
248 248 def available_problems
249 249 if not GraderConfiguration.multicontests?
250 250 if GraderConfiguration.use_problem_group?
251 251 return available_problems_in_group
252 252 else
253 253 return Problem.available_problems
254 254 end
255 255 else
256 256 contest_problems = []
257 257 pin = {}
258 258 contests.enabled.each do |contest|
259 259 contest.problems.available.each do |problem|
260 260 if not pin.has_key? problem.id
261 261 contest_problems << problem
262 262 end
263 263 pin[problem.id] = true
264 264 end
265 265 end
266 266 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
267 267 return contest_problems + other_avaiable_problems
268 268 end
269 269 end
270 270
271 271 def available_problems_in_group
272 272 problem = []
273 273 self.groups.each do |group|
274 274 group.problems.where(available: true).each { |p| problem << p }
275 275 end
276 276 problem.uniq!
277 277 if problem
278 278 problem.sort! do |a,b|
279 279 case
280 280 when a.date_added < b.date_added
281 281 1
282 282 when a.date_added > b.date_added
283 283 -1
284 284 else
285 285 a.name <=> b.name
286 286 end
287 287 end
288 288 return problem
289 289 else
290 290 return []
291 291 end
292 292 end
293 293
294 294 def can_view_problem?(problem)
295 295 return true if admin?
296 296 return available_problems.include? problem
297 297 end
298 298
299 299 def self.clear_last_login
300 300 User.update_all(:last_ip => nil)
301 301 end
302 302
303 303 protected
304 304 def encrypt_new_password
305 305 return if password.blank?
306 306 self.salt = (10+rand(90)).to_s
307 307 self.hashed_password = User.encrypt(self.password,self.salt)
308 308 end
309 309
310 310 def assign_default_site
311 311 # have to catch error when migrating (because self.site is not available).
312 312 begin
313 313 if self.site==nil
314 314 self.site = Site.find_by_name('default')
315 315 if self.site==nil
316 316 self.site = Site.find(1) # when 'default has be renamed'
317 317 end
318 318 end
319 319 rescue
320 320 end
321 321 end
322 322
323 323 def assign_default_contest
324 324 # have to catch error when migrating (because self.site is not available).
325 325 begin
326 326 if self.contests.length == 0
327 327 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
328 328 if default_contest
329 329 self.contests = [default_contest]
330 330 end
331 331 end
332 332 rescue
333 333 end
334 334 end
335 335
336 336 def password_required?
337 337 self.hashed_password.blank? || !self.password.blank?
338 338 end
339 339
340 340 def self.encrypt(string,salt)
341 341 Digest::SHA1.hexdigest(salt + string)
342 342 end
343 343
344 344 def uniqueness_of_email_from_activated_users
345 345 user = User.activated_users.find_by_email(self.email)
346 346 if user and (user.login != self.login)
347 347 self.errors.add(:base,"Email has already been taken")
348 348 end
349 349 end
350 350
351 351 def enough_time_interval_between_same_email_registrations
352 352 return if !self.new_record?
353 353 return if self.activated
354 354 open_user = User.find_by_email(self.email,
355 355 :order => 'created_at DESC')
356 356 if open_user and open_user.created_at and
357 357 (open_user.created_at > Time.now.gmtime - 5.minutes)
358 358 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
359 359 end
360 360 end
361 361
362 362 def email_validation?
363 363 begin
364 364 return VALIDATE_USER_EMAILS
365 365 rescue
366 366 return false
367 367 end
368 368 end
369 369 end
@@ -1,309 +1,311
1 1 %h2 Live submit
2 2 %br
3 3
4 4 %textarea#text_sourcecode{style: "display:none"}~ @source
5 5 .container
6 6 .row
7 7 .col-md-12
8 8 .alert.alert-info
9 9 Write your code in the following box, choose language, and click submit button when finished
10 10 .row
11 11 .col-md-8
12 12 %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'}
13 13 .col-md-4
14 14 - # submission form
15 15 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
16 16
17 17 = hidden_field_tag 'editor_text', @source
18 18 = hidden_field_tag 'submission[problem_id]', @problem.id
19 19 .form-group
20 20 = label_tag "Task:"
21 21 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
22 22 .form-group
23 23 = label_tag "Description:"
24 24 = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @problem
25 25
26 26 .form-group
27 27 = label_tag 'Language:'
28 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 29 .form-group
30 30 .input-group
31 31 %span.input-group-btn
32 32 %span.btn.btn-default.btn-file
33 33 Browse
34 34 = file_field_tag 'load_file'
35 35 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
36 36 .form-group
37 37 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
38 38 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
39 39 - # latest submission status
40 40 .panel{class: (@submission && @submission.graded_at) ? "panel-info" : "panel-warning"}
41 41 .panel-heading
42 42 Latest Submission Status
43 43 = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true if @submission
44 44 .panel-body
45 45 %div#latest_status
46 46 - if @submission
47 47 = render :partial => 'submission_short',
48 48 :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
49 49 .row
50 50 .col-md-12
51 51 %h2 Console
52 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 54 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
55 55 .modal-dialog.modal-lg{role:'document'}
56 56 .modal-content
57 57 .modal-header
58 58 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
59 59 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
60 60 %h4 Compiler message
61 61 .modal-body
62 - %pre#compiler_msg= @submission.compiler_message
62 + %pre#compiler_msg
63 + - if @submission
64 + = @submission.compiler_message
63 65 .modal-footer
64 66 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
65 67
66 68 :javascript
67 69 $(document).ready(function() {
68 70 e = ace.edit("editor")
69 71 e.setValue($("#text_sourcecode").val());
70 72 e.gotoLine(1);
71 73 $("#language_id").trigger('change');
72 74
73 75 $("#load_file").on('change',function(evt) {
74 76 var file = evt.target.files[0];
75 77 var reader = new FileReader();
76 78 reader.onload = function(theFile) {
77 79 var e = ace.edit("editor")
78 80 e.setValue(theFile.target.result);
79 81 e.gotoLine(1);
80 82 };
81 83 reader.readAsText(file)
82 84 });
83 85
84 86 //brython();
85 87 });
86 88
87 89
88 90
89 91
90 92
91 93 %script#__main__{type:'text/python3'}
92 94 :plain
93 95 import sys
94 96 import traceback
95 97
96 98 from browser import document as doc
97 99 from browser import window, alert, console
98 100
99 101 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
100 102 for supporting Python development. See www.python.org for more information."""
101 103
102 104 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
103 105 All Rights Reserved.
104 106
105 107 Copyright (c) 2001-2013 Python Software Foundation.
106 108 All Rights Reserved.
107 109
108 110 Copyright (c) 2000 BeOpen.com.
109 111 All Rights Reserved.
110 112
111 113 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
112 114 All Rights Reserved.
113 115
114 116 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
115 117 All Rights Reserved."""
116 118
117 119 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
118 120 All rights reserved.
119 121
120 122 Redistribution and use in source and binary forms, with or without
121 123 modification, are permitted provided that the following conditions are met:
122 124
123 125 Redistributions of source code must retain the above copyright notice, this
124 126 list of conditions and the following disclaimer. Redistributions in binary
125 127 form must reproduce the above copyright notice, this list of conditions and
126 128 the following disclaimer in the documentation and/or other materials provided
127 129 with the distribution.
128 130 Neither the name of the <ORGANIZATION> nor the names of its contributors may
129 131 be used to endorse or promote products derived from this software without
130 132 specific prior written permission.
131 133
132 134 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
133 135 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
134 136 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
135 137 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
136 138 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
137 139 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
138 140 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
139 141 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
140 142 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
141 143 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
142 144 POSSIBILITY OF SUCH DAMAGE.
143 145 """
144 146
145 147 def credits():
146 148 print(_credits)
147 149 credits.__repr__ = lambda:_credits
148 150
149 151 def copyright():
150 152 print(_copyright)
151 153 copyright.__repr__ = lambda:_copyright
152 154
153 155 def license():
154 156 print(_license)
155 157 license.__repr__ = lambda:_license
156 158
157 159 def write(data):
158 160 doc['console'].value += str(data)
159 161
160 162
161 163 sys.stdout.write = sys.stderr.write = write
162 164 history = []
163 165 current = 0
164 166 _status = "main" # or "block" if typing inside a block
165 167
166 168 # execution namespace
167 169 editor_ns = {'credits':credits,
168 170 'copyright':copyright,
169 171 'license':license,
170 172 '__name__':'__main__'}
171 173
172 174 def cursorToEnd(*args):
173 175 pos = len(doc['console'].value)
174 176 doc['console'].setSelectionRange(pos, pos)
175 177 doc['console'].scrollTop = doc['console'].scrollHeight
176 178
177 179 def get_col(area):
178 180 # returns the column num of cursor
179 181 sel = doc['console'].selectionStart
180 182 lines = doc['console'].value.split('\n')
181 183 for line in lines[:-1]:
182 184 sel -= len(line) + 1
183 185 return sel
184 186
185 187
186 188 def myKeyPress(event):
187 189 global _status, current
188 190 if event.keyCode == 9: # tab key
189 191 event.preventDefault()
190 192 doc['console'].value += " "
191 193 elif event.keyCode == 13: # return
192 194 src = doc['console'].value
193 195 if _status == "main":
194 196 currentLine = src[src.rfind('>>>') + 4:]
195 197 elif _status == "3string":
196 198 currentLine = src[src.rfind('>>>') + 4:]
197 199 currentLine = currentLine.replace('\n... ', '\n')
198 200 else:
199 201 currentLine = src[src.rfind('...') + 4:]
200 202 if _status == 'main' and not currentLine.strip():
201 203 doc['console'].value += '\n>>> '
202 204 event.preventDefault()
203 205 return
204 206 doc['console'].value += '\n'
205 207 history.append(currentLine)
206 208 current = len(history)
207 209 if _status == "main" or _status == "3string":
208 210 try:
209 211 _ = editor_ns['_'] = eval(currentLine, editor_ns)
210 212 if _ is not None:
211 213 write(repr(_)+'\n')
212 214 doc['console'].value += '>>> '
213 215 _status = "main"
214 216 except IndentationError:
215 217 doc['console'].value += '... '
216 218 _status = "block"
217 219 except SyntaxError as msg:
218 220 if str(msg) == 'invalid syntax : triple string end not found' or \
219 221 str(msg).startswith('Unbalanced bracket'):
220 222 doc['console'].value += '... '
221 223 _status = "3string"
222 224 elif str(msg) == 'eval() argument must be an expression':
223 225 try:
224 226 exec(currentLine, editor_ns)
225 227 except:
226 228 traceback.print_exc()
227 229 doc['console'].value += '>>> '
228 230 _status = "main"
229 231 elif str(msg) == 'decorator expects function':
230 232 doc['console'].value += '... '
231 233 _status = "block"
232 234 else:
233 235 traceback.print_exc()
234 236 doc['console'].value += '>>> '
235 237 _status = "main"
236 238 except:
237 239 traceback.print_exc()
238 240 doc['console'].value += '>>> '
239 241 _status = "main"
240 242 elif currentLine == "": # end of block
241 243 block = src[src.rfind('>>>') + 4:].splitlines()
242 244 block = [block[0]] + [b[4:] for b in block[1:]]
243 245 block_src = '\n'.join(block)
244 246 # status must be set before executing code in globals()
245 247 _status = "main"
246 248 try:
247 249 _ = exec(block_src, editor_ns)
248 250 if _ is not None:
249 251 print(repr(_))
250 252 except:
251 253 traceback.print_exc()
252 254 doc['console'].value += '>>> '
253 255 else:
254 256 doc['console'].value += '... '
255 257
256 258 cursorToEnd()
257 259 event.preventDefault()
258 260
259 261 def myKeyDown(event):
260 262 global _status, current
261 263 if event.keyCode == 37: # left arrow
262 264 sel = get_col(doc['console'])
263 265 if sel < 5:
264 266 event.preventDefault()
265 267 event.stopPropagation()
266 268 elif event.keyCode == 36: # line start
267 269 pos = doc['console'].selectionStart
268 270 col = get_col(doc['console'])
269 271 doc['console'].setSelectionRange(pos - col + 4, pos - col + 4)
270 272 event.preventDefault()
271 273 elif event.keyCode == 38: # up
272 274 if current > 0:
273 275 pos = doc['console'].selectionStart
274 276 col = get_col(doc['console'])
275 277 # remove current line
276 278 doc['console'].value = doc['console'].value[:pos - col + 4]
277 279 current -= 1
278 280 doc['console'].value += history[current]
279 281 event.preventDefault()
280 282 elif event.keyCode == 40: # down
281 283 if current < len(history) - 1:
282 284 pos = doc['console'].selectionStart
283 285 col = get_col(doc['console'])
284 286 # remove current line
285 287 doc['console'].value = doc['console'].value[:pos - col + 4]
286 288 current += 1
287 289 doc['console'].value += history[current]
288 290 event.preventDefault()
289 291 elif event.keyCode == 8: # backspace
290 292 src = doc['console'].value
291 293 lstart = src.rfind('\n')
292 294 if (lstart == -1 and len(src) < 5) or (len(src) - lstart < 6):
293 295 event.preventDefault()
294 296 event.stopPropagation()
295 297
296 298
297 299 doc['console'].bind('keypress', myKeyPress)
298 300 doc['console'].bind('keydown', myKeyDown)
299 301 doc['console'].bind('click', cursorToEnd)
300 302 v = sys.implementation.version
301 303 doc['console'].value = "Brython %s.%s.%s on %s %s\n>>> " % (
302 304 v[0], v[1], v[2], window.navigator.appName, window.navigator.appVersion)
303 305 #doc['console'].value += 'Type "copyright", "credits" or "license" for more information.'
304 306 doc['console'].focus()
305 307 cursorToEnd()
306 308
307 309
308 310
309 311
@@ -1,30 +1,33
1 1 # If you want to manage graders through web interface, set the path to
2 2 # the grader directory below. This dir is where raw, ev, ev-exam,
3 3 # scripts reside. All grader scripts will be in
4 4 # #{GRADER_ROOT_DIR}/scripts.
5 5 GRADER_ROOT_DIR = ''
6 6
7 7 # These are where inputs and outputs of test requests are stored
8 8 TEST_REQUEST_INPUT_FILE_DIR = (Rails.root + 'data/test_request/input').to_s
9 9 TEST_REQUEST_OUTPUT_FILE_DIR = (Rails.root + 'data/test_request/output').to_s
10 10
11 11 # To use ANALYSIS MODE, provide the testcases/testruns breakdown,
12 12 # and the directory of the grading result (usually in judge's dir).
13 13 TASK_GRADING_INFO_FILENAME = Rails.root + 'config/tasks.yml'
14 14
15 15 # TODO: change this to where results are kept.
16 16 GRADING_RESULT_DIR = 'RESULT-DIR'
17 17
18 18 # Change this to allow importing testdata into database as test-pairs.
19 19 # This is mainly for Code Jom contest.
20 20 ALLOW_TEST_PAIR_IMPORT = false
21 21
22 22 # Uncomment so that the system validates user e-mails
23 23 # VALIDATE_USER_EMAILS = true
24 24
25 25 # Uncomment so that Apache X-Sendfile is used when delivering files
26 26 # (e.g., in /tasks/view).
27 27 # USE_APACHE_XSENDFILE = true
28 28
29 29 # Uncomment so that configuration is read only once when the server is loaded
30 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 1 CONFIGURATIONS =
2 2 [
3 3 {
4 4 :key => 'system.single_user_mode',
5 5 :value_type => 'boolean',
6 6 :default_value => 'false',
7 7 :description => 'Only admins can log in to the system when running under single user mode.'
8 8 },
9 9
10 10 {
11 11 :key => 'ui.front.title',
12 12 :value_type => 'string',
13 13 :default_value => 'Grader'
14 14 },
15 15
16 16 {
17 17 :key => 'ui.front.welcome_message',
18 18 :value_type => 'string',
19 19 :default_value => 'Welcome!'
20 20 },
21 21
22 22 {
23 23 :key => 'ui.show_score',
24 24 :value_type => 'boolean',
25 25 :default_value => 'true'
26 26 },
27 27
28 28 {
29 29 :key => 'contest.time_limit',
30 30 :value_type => 'string',
31 31 :default_value => 'unlimited',
32 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 36 :key => 'system.mode',
37 37 :value_type => 'string',
38 38 :default_value => 'standard',
39 39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
40 40 },
41 41
42 42 {
43 43 :key => 'contest.name',
44 44 :value_type => 'string',
45 45 :default_value => 'Grader',
46 46 :description => 'This name will be shown on the user header bar.'
47 47 },
48 48
49 49 {
50 50 :key => 'contest.multisites',
51 51 :value_type => 'boolean',
52 52 :default_value => 'false',
53 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 56 #---------------------------- right --------------------------------
57 57 {
58 58 :key => 'right.user_hall_of_fame',
59 59 :value_type => 'boolean',
60 60 :default_value => 'false',
61 61 :description => 'If true, any user can access hall of fame page.'
62 62 },
63 63
64 64 {
65 65 :key => 'right.multiple_ip_login',
66 66 :value_type => 'boolean',
67 67 :default_value => 'true',
68 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 72 :key => 'right.user_view_submission',
73 73 :value_type => 'boolean',
74 74 :default_value => 'false',
75 75 :description => 'If true, any user can view submissions of every one.'
76 76 },
77 77
78 78 {
79 79 :key => 'right.bypass_agreement',
80 80 :value_type => 'boolean',
81 81 :default_value => 'true',
82 82 :description => 'When false, a user must accept usage agreement before login'
83 83 },
84 84
85 85 {
86 86 :key => 'right.heartbeat_response',
87 87 :value_type => 'string',
88 88 :default_value => 'OK',
89 89 :description => 'Heart beat response text'
90 90 },
91 91
92 92 {
93 93 :key => 'right.heartbeat_response_full',
94 94 :value_type => 'string',
95 95 :default_value => 'OK',
96 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 100 :key => 'right.view_testcase',
101 101 :value_type => 'boolean',
102 102 :default_value => 'false',
103 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 113 # If Configuration['system.online_registration'] is true, the
106 114 # system allows online registration, and will use these
107 115 # information for sending confirmation emails.
108 116 {
109 117 :key => 'system.online_registration.smtp',
110 118 :value_type => 'string',
111 119 :default_value => 'smtp.somehost.com'
112 120 },
113 121
114 122 {
115 123 :key => 'system.online_registration.from',
116 124 :value_type => 'string',
117 125 :default_value => 'your.email@address'
118 126 },
119 127
120 128 {
121 129 :key => 'system.admin_email',
122 130 :value_type => 'string',
123 131 :default_value => 'admin@admin.email'
124 132 },
125 133
126 134 {
127 135 :key => 'system.user_setting_enabled',
128 136 :value_type => 'boolean',
129 137 :default_value => 'true',
130 138 :description => 'If this option is true, users can change their settings'
131 139 },
132 140
133 141 {
134 142 :key => 'system.user_setting_enabled',
135 143 :value_type => 'boolean',
136 144 :default_value => 'true',
137 145 :description => 'If this option is true, users can change their settings'
138 146 },
139 147
140 148 # If Configuration['contest.test_request.early_timeout'] is true
141 149 # the user will not be able to use test request at 30 minutes
142 150 # before the contest ends.
143 151 {
144 152 :key => 'contest.test_request.early_timeout',
145 153 :value_type => 'boolean',
146 154 :default_value => 'false'
147 155 },
148 156
149 157 {
150 158 :key => 'system.multicontests',
151 159 :value_type => 'boolean',
152 160 :default_value => 'false'
153 161 },
154 162
155 163 {
156 164 :key => 'contest.confirm_indv_contest_start',
157 165 :value_type => 'boolean',
158 166 :default_value => 'false'
159 167 },
160 168
161 169 {
162 170 :key => 'contest.default_contest_name',
163 171 :value_type => 'string',
164 172 :default_value => 'none',
165 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 177 :key => 'system.use_problem_group',
170 178 :value_type => 'boolean',
171 179 :default_value => 'false',
172 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 188 def create_configuration_key(key,
181 189 value_type,
182 190 default_value,
183 191 description='')
184 192 conf = (GraderConfiguration.find_by_key(key) ||
185 193 GraderConfiguration.new(:key => key,
186 194 :value_type => value_type,
187 195 :value => default_value))
188 196 conf.description = description
189 197 conf.save
190 198 end
191 199
192 200 def seed_config
193 201 CONFIGURATIONS.each do |conf|
194 202 if conf.has_key? :description
195 203 desc = conf[:description]
196 204 else
197 205 desc = ''
198 206 end
199 207 create_configuration_key(conf[:key],
200 208 conf[:value_type],
201 209 conf[:default_value],
202 210 desc)
203 211 end
204 212 end
205 213
206 214 def seed_roles
207 215 return if Role.find_by_name('admin')
208 216
209 217 role = Role.create(:name => 'admin')
210 218 user_admin_right = Right.create(:name => 'user_admin',
211 219 :controller => 'user_admin',
212 220 :action => 'all')
213 221 problem_admin_right = Right.create(:name=> 'problem_admin',
214 222 :controller => 'problems',
215 223 :action => 'all')
216 224
217 225 graders_right = Right.create(:name => 'graders_admin',
218 226 :controller => 'graders',
219 227 :action => 'all')
220 228
221 229 role.rights << user_admin_right;
222 230 role.rights << problem_admin_right;
223 231 role.rights << graders_right;
224 232 role.save
225 233 end
226 234
227 235 def seed_root
228 236 return if User.find_by_login('root')
229 237
230 238 root = User.new(:login => 'root',
231 239 :full_name => 'Administrator',
232 240 :alias => 'root')
233 241 root.password = 'ioionrails';
234 242
235 243 class << root
236 244 public :encrypt_new_password
237 245 def valid?(context=nil)
238 246 true
239 247 end
240 248 end
241 249
242 250 root.encrypt_new_password
243 251
244 252 root.roles << Role.find_by_name('admin')
245 253
246 254 root.activated = true
247 255 root.save
248 256 end
249 257
250 258 def seed_users_and_roles
251 259 seed_roles
252 260 seed_root
253 261 end
254 262
255 263 def seed_more_languages
256 264 Language.delete_all
257 265 Language.create( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
258 266 Language.create( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
259 267 Language.create( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
260 268 Language.create( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
261 269 Language.create( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
262 270 Language.create( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
263 271 end
264 272
265 273 seed_config
266 274 seed_users_and_roles
267 275 seed_more_languages
You need to be logged in to leave comments. Login now