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

r753:9918c6e0c313 - - 6 files changed: 15 inserted, 6 deleted

@@ -1,304 +1,308
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
15 15 def show
16 16 @problem = Problem.find(params[:id])
17 17 end
18 18
19 19 def new
20 20 @problem = Problem.new
21 21 @description = nil
22 22 end
23 23
24 24 def create
25 25 @problem = Problem.new(problem_params)
26 26 @description = Description.new(problem_params[:description])
27 27 if @description.body!=''
28 28 if !@description.save
29 29 render :action => new and return
30 30 end
31 31 else
32 32 @description = nil
33 33 end
34 34 @problem.description = @description
35 35 if @problem.save
36 36 flash[:notice] = 'Problem was successfully created.'
37 37 redirect_to action: :index
38 38 else
39 39 render :action => 'new'
40 40 end
41 41 end
42 42
43 43 def quick_create
44 44 @problem = Problem.new(problem_params)
45 45 @problem.full_name = @problem.name if @problem.full_name == ''
46 46 @problem.full_score = 100
47 47 @problem.available = false
48 48 @problem.test_allowed = true
49 49 @problem.output_only = false
50 50 @problem.date_added = Time.new
51 51 if @problem.save
52 52 flash[:notice] = 'Problem was successfully created.'
53 53 redirect_to action: :index
54 54 else
55 55 flash[:notice] = 'Error saving problem'
56 56 redirect_to action: :index
57 57 end
58 58 end
59 59
60 60 def edit
61 61 @problem = Problem.find(params[:id])
62 62 @description = @problem.description
63 63 end
64 64
65 65 def update
66 66 @problem = Problem.find(params[:id])
67 67 @description = @problem.description
68 68 if @description.nil? and params[:description][:body]!=''
69 - @description = Description.new(params[:description])
69 + @description = Description.new(description_params)
70 70 if !@description.save
71 71 flash[:notice] = 'Error saving description'
72 72 render :action => 'edit' and return
73 73 end
74 74 @problem.description = @description
75 75 elsif @description
76 - if !@description.update_attributes(params[:description])
76 + if !@description.update_attributes(description_params)
77 77 flash[:notice] = 'Error saving description'
78 78 render :action => 'edit' and return
79 79 end
80 80 end
81 81 if params[:file] and params[:file].content_type != 'application/pdf'
82 82 flash[:notice] = 'Error: Uploaded file is not PDF'
83 83 render :action => 'edit' and return
84 84 end
85 85 if @problem.update_attributes(problem_params)
86 86 flash[:notice] = 'Problem was successfully updated.'
87 87 unless params[:file] == nil or params[:file] == ''
88 88 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
89 89 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
90 90 if not FileTest.exists? out_dirname
91 91 Dir.mkdir out_dirname
92 92 end
93 93
94 94 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
95 95 if FileTest.exists? out_filename
96 96 File.delete out_filename
97 97 end
98 98
99 99 File.open(out_filename,"wb") do |file|
100 100 file.write(params[:file].read)
101 101 end
102 102 @problem.description_filename = "#{@problem.name}.pdf"
103 103 @problem.save
104 104 end
105 105 redirect_to :action => 'show', :id => @problem
106 106 else
107 107 render :action => 'edit'
108 108 end
109 109 end
110 110
111 111 def destroy
112 112 p = Problem.find(params[:id]).destroy
113 113 redirect_to action: :index
114 114 end
115 115
116 116 def toggle
117 117 @problem = Problem.find(params[:id])
118 118 @problem.update_attributes(available: !(@problem.available) )
119 119 respond_to do |format|
120 120 format.js { }
121 121 end
122 122 end
123 123
124 124 def toggle_test
125 125 @problem = Problem.find(params[:id])
126 126 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
127 127 respond_to do |format|
128 128 format.js { }
129 129 end
130 130 end
131 131
132 132 def toggle_view_testcase
133 133 @problem = Problem.find(params[:id])
134 134 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
135 135 respond_to do |format|
136 136 format.js { }
137 137 end
138 138 end
139 139
140 140 def turn_all_off
141 141 Problem.available.all.each do |problem|
142 142 problem.available = false
143 143 problem.save
144 144 end
145 145 redirect_to action: :index
146 146 end
147 147
148 148 def turn_all_on
149 149 Problem.where.not(available: true).each do |problem|
150 150 problem.available = true
151 151 problem.save
152 152 end
153 153 redirect_to action: :index
154 154 end
155 155
156 156 def stat
157 157 @problem = Problem.find(params[:id])
158 158 unless @problem.available or session[:admin]
159 159 redirect_to :controller => 'main', :action => 'list'
160 160 return
161 161 end
162 162 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
163 163
164 164 #stat summary
165 165 range =65
166 166 @histogram = { data: Array.new(range,0), summary: {} }
167 167 user = Hash.new(0)
168 168 @submissions.find_each do |sub|
169 169 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
170 170 @histogram[:data][d.to_i] += 1 if d < range
171 171 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
172 172 end
173 173 @histogram[:summary][:max] = [@histogram[:data].max,1].max
174 174
175 175 @summary = { attempt: user.count, solve: 0 }
176 176 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
177 177 end
178 178
179 179 def manage
180 180 @problems = Problem.order(date_added: :desc)
181 181 end
182 182
183 183 def do_manage
184 184 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
185 185 change_date_added
186 186 elsif params.has_key? 'add_to_contest'
187 187 add_to_contest
188 188 elsif params.has_key? 'enable_problem'
189 189 set_available(true)
190 190 elsif params.has_key? 'disable_problem'
191 191 set_available(false)
192 192 elsif params.has_key? 'add_group'
193 193 group = Group.find(params[:group_id])
194 194 ok = []
195 195 failed = []
196 196 get_problems_from_params.each do |p|
197 197 begin
198 198 group.problems << p
199 199 ok << p.full_name
200 200 rescue => e
201 201 failed << p.full_name
202 202 end
203 203 end
204 204 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
205 205 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
206 206 elsif params.has_key? 'add_tags'
207 207 get_problems_from_params.each do |p|
208 208 p.tag_ids += params[:tag_ids]
209 209 end
210 210 end
211 211
212 212 redirect_to :action => 'manage'
213 213 end
214 214
215 215 def import
216 216 @allow_test_pair_import = allow_test_pair_import?
217 217 end
218 218
219 219 def do_import
220 220 old_problem = Problem.find_by_name(params[:name])
221 221 if !allow_test_pair_import? and params.has_key? :import_to_db
222 222 params.delete :import_to_db
223 223 end
224 224 @problem, import_log = Problem.create_from_import_form_params(params,
225 225 old_problem)
226 226
227 227 if !@problem.errors.empty?
228 228 render :action => 'import' and return
229 229 end
230 230
231 231 if old_problem!=nil
232 232 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
233 233 end
234 234 @log = import_log
235 235 end
236 236
237 237 def remove_contest
238 238 problem = Problem.find(params[:id])
239 239 contest = Contest.find(params[:contest_id])
240 240 if problem!=nil and contest!=nil
241 241 problem.contests.delete(contest)
242 242 end
243 243 redirect_to :action => 'manage'
244 244 end
245 245
246 246 ##################################
247 247 protected
248 248
249 249 def allow_test_pair_import?
250 250 if defined? ALLOW_TEST_PAIR_IMPORT
251 251 return ALLOW_TEST_PAIR_IMPORT
252 252 else
253 253 return false
254 254 end
255 255 end
256 256
257 257 def change_date_added
258 258 problems = get_problems_from_params
259 259 date = Date.parse(params[:date_added])
260 260 problems.each do |p|
261 261 p.date_added = date
262 262 p.save
263 263 end
264 264 end
265 265
266 266 def add_to_contest
267 267 problems = get_problems_from_params
268 268 contest = Contest.find(params[:contest][:id])
269 269 if contest!=nil and contest.enabled
270 270 problems.each do |p|
271 271 p.contests << contest
272 272 end
273 273 end
274 274 end
275 275
276 276 def set_available(avail)
277 277 problems = get_problems_from_params
278 278 problems.each do |p|
279 279 p.available = avail
280 280 p.save
281 281 end
282 282 end
283 283
284 284 def get_problems_from_params
285 285 problems = []
286 286 params.keys.each do |k|
287 287 if k.index('prob-')==0
288 288 name, id, order = k.split('-')
289 289 problems << Problem.find(id)
290 290 end
291 291 end
292 292 problems
293 293 end
294 294
295 295 def get_problems_stat
296 296 end
297 297
298 298 private
299 299
300 300 def problem_params
301 301 params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[])
302 302 end
303 303
304 + def description_params
305 + params.require(:description).permit(:body, :markdown)
304 306 end
307 +
308 + 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 - has_and_belongs_to_many :contests, -> { order(:name); uniq}
30 + has_and_belongs_to_many :contests, -> { order(:name)}
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 43 validates_length_of :password, :within => 4..20, :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 - return false if self.contest_stat(true)==nil
176 + return false if self.contest_stat==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,71 +1,72
1 1 - content_for :head do
2 2 = stylesheet_link_tag 'problems'
3 3
4 4 %h1 Import problems
5 5
6 6 %p= link_to '[Back to problem list]', problems_path
7 7
8 8 - if @problem and @problem.errors
9 9 =error_messages_for 'problem'
10 10
11 - = form_tag({:action => 'do_import'}, :multipart => true) do
11 + = simple_form_for :problem, url: do_import_problems_path, :multipart => true do |f|
12 +
12 13 .submitbox
13 14 %table
14 15 %tr
15 16 %td Name:
16 17 %td= text_field_tag 'name'
17 18 %tr
18 19 %td Full name:
19 20 %td
20 21 = text_field_tag 'full_name'
21 22 %span{:class => 'help'} Leave blank to use the same value as the name above.
22 23 %tr
23 24 %td Testdata file:
24 25 %td= file_field_tag 'file'
25 26 %tr
26 27 %td
27 28 %td
28 29 %span{:class => 'help'}
29 30 In .zip, .tgz, tar.gz, .tar format.
30 31 It should includes inputs (e.g., 1.in, 2a.in, 2b.in)
31 32 and solutions (e.g., 1.sol, 2a.sol, 2b.sol).
32 33 %br/
33 34 You may put task description in *.html for raw html
34 35 and *.md or *.markdown for markdown.
35 36 %br/
36 37 You may also put a pdf file for the task description
37 38 %tr
38 39 %td Checker:
39 40 %td= select_tag 'checker', options_for_select([['Text checker','text'],['Float checker','float']], 'text')
40 41 %tr
41 42 %td
42 43 %td
43 44 %span{:class => 'help'}
44 45 "Text" checker checks if the text (including numbers) is the same, ignoring any whitespace
45 46 %br/
46 47 "Float" checker checks if all numbers is within EPSILON error using formula |a-b| < EPSILON * max(|a|,|b|)
47 48
48 49 - if @allow_test_pair_import
49 50 %tr
50 51 %td
51 52 %td
52 53 = check_box_tag 'import_to_db'
53 54 Import test data to database (for a test-pair task)
54 55 %tr
55 56 %td Time limit:
56 57 %td
57 58 = text_field_tag 'time_limit'
58 59 %span{:class => 'help'} In seconds. Leave blank to use 1 sec.
59 60 %tr
60 61 %td Memory limit:
61 62 %td
62 63 = text_field_tag 'memory_limit'
63 64 %span{:class => 'help'} In MB. Leave blank to use 32MB.
64 65 %tr
65 66 %td
66 67 %td= submit_tag 'Import problem'
67 68
68 69 - if @log
69 70 %h3 Import log
70 71 %pre.import-log
71 72 = @log
@@ -1,49 +1,49
1 1 %h1 Maximum score
2 2
3 - = form_tag report_show_max_score_path
3 + = form_tag show_max_score_report_path
4 4 .row
5 5 .col-md-4
6 6 .panel.panel-primary
7 7 .panel-heading
8 8 Problems
9 9 .panel-body
10 10 %p
11 11 Select problem(s) that we wish to know the score.
12 12 = label_tag :problem_id, "Problems"
13 13 = select_tag 'problem_id[]',
14 14 options_for_select(Problem.all.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},params[:problem_id]),
15 15 { class: 'select2 form-control', multiple: "true" }
16 16 .col-md-4
17 17 .panel.panel-primary
18 18 .panel-heading
19 19 Submission range
20 20 .panel-body
21 21 %p
22 22 Input minimum and maximum range of submission ID that should be included. A blank value for min and max means -1 and infinity, respectively.
23 23 .form-group
24 24 = label_tag :from, "Min"
25 25 = text_field_tag 'from_id', @since_id, class: "form-control"
26 26 .form-group
27 27 = label_tag :from, "Max"
28 28 = text_field_tag 'to_id', @until_id, class: "form-control"
29 29 .col-md-4
30 30 .panel.panel-primary
31 31 .panel-heading
32 32 Users
33 33 .panel-body
34 34 .radio
35 35 %label
36 36 = radio_button_tag 'users', 'all', (params[:users] == "all")
37 37 All users
38 38 .radio
39 39 %label
40 40 = radio_button_tag 'users', 'enabled', (params[:users] == "enabled")
41 41 Only enabled users
42 42 .row
43 43 .col-md-12
44 44 = button_tag 'Show', class: "btn btn-primary btn-large", value: "show"
45 45 = button_tag 'Download CSV', class: "btn btn-primary btn-large", value: "download"
46 46
47 47 - if @scorearray
48 48 %h2 Result
49 49 =render "score_table"
@@ -1,309 +1,310
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 + - if @submission
54 55 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
55 56 .modal-dialog.modal-lg{role:'document'}
56 57 .modal-content
57 58 .modal-header
58 59 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
59 60 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
60 61 %h4 Compiler message
61 62 .modal-body
62 63 %pre#compiler_msg= @submission.compiler_message
63 64 .modal-footer
64 65 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
65 66
66 67 :javascript
67 68 $(document).ready(function() {
68 69 e = ace.edit("editor")
69 70 e.setValue($("#text_sourcecode").val());
70 71 e.gotoLine(1);
71 72 $("#language_id").trigger('change');
72 73
73 74 $("#load_file").on('change',function(evt) {
74 75 var file = evt.target.files[0];
75 76 var reader = new FileReader();
76 77 reader.onload = function(theFile) {
77 78 var e = ace.edit("editor")
78 79 e.setValue(theFile.target.result);
79 80 e.gotoLine(1);
80 81 };
81 82 reader.readAsText(file)
82 83 });
83 84
84 85 //brython();
85 86 });
86 87
87 88
88 89
89 90
90 91
91 92 %script#__main__{type:'text/python3'}
92 93 :plain
93 94 import sys
94 95 import traceback
95 96
96 97 from browser import document as doc
97 98 from browser import window, alert, console
98 99
99 100 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
100 101 for supporting Python development. See www.python.org for more information."""
101 102
102 103 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
103 104 All Rights Reserved.
104 105
105 106 Copyright (c) 2001-2013 Python Software Foundation.
106 107 All Rights Reserved.
107 108
108 109 Copyright (c) 2000 BeOpen.com.
109 110 All Rights Reserved.
110 111
111 112 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
112 113 All Rights Reserved.
113 114
114 115 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
115 116 All Rights Reserved."""
116 117
117 118 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
118 119 All rights reserved.
119 120
120 121 Redistribution and use in source and binary forms, with or without
121 122 modification, are permitted provided that the following conditions are met:
122 123
123 124 Redistributions of source code must retain the above copyright notice, this
124 125 list of conditions and the following disclaimer. Redistributions in binary
125 126 form must reproduce the above copyright notice, this list of conditions and
126 127 the following disclaimer in the documentation and/or other materials provided
127 128 with the distribution.
128 129 Neither the name of the <ORGANIZATION> nor the names of its contributors may
129 130 be used to endorse or promote products derived from this software without
130 131 specific prior written permission.
131 132
132 133 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
133 134 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
134 135 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
135 136 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
136 137 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
137 138 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
138 139 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
139 140 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
140 141 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
141 142 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
142 143 POSSIBILITY OF SUCH DAMAGE.
143 144 """
144 145
145 146 def credits():
146 147 print(_credits)
147 148 credits.__repr__ = lambda:_credits
148 149
149 150 def copyright():
150 151 print(_copyright)
151 152 copyright.__repr__ = lambda:_copyright
152 153
153 154 def license():
154 155 print(_license)
155 156 license.__repr__ = lambda:_license
156 157
157 158 def write(data):
158 159 doc['console'].value += str(data)
159 160
160 161
161 162 sys.stdout.write = sys.stderr.write = write
162 163 history = []
163 164 current = 0
164 165 _status = "main" # or "block" if typing inside a block
165 166
166 167 # execution namespace
167 168 editor_ns = {'credits':credits,
168 169 'copyright':copyright,
169 170 'license':license,
170 171 '__name__':'__main__'}
171 172
172 173 def cursorToEnd(*args):
173 174 pos = len(doc['console'].value)
174 175 doc['console'].setSelectionRange(pos, pos)
175 176 doc['console'].scrollTop = doc['console'].scrollHeight
176 177
177 178 def get_col(area):
178 179 # returns the column num of cursor
179 180 sel = doc['console'].selectionStart
180 181 lines = doc['console'].value.split('\n')
181 182 for line in lines[:-1]:
182 183 sel -= len(line) + 1
183 184 return sel
184 185
185 186
186 187 def myKeyPress(event):
187 188 global _status, current
188 189 if event.keyCode == 9: # tab key
189 190 event.preventDefault()
190 191 doc['console'].value += " "
191 192 elif event.keyCode == 13: # return
192 193 src = doc['console'].value
193 194 if _status == "main":
194 195 currentLine = src[src.rfind('>>>') + 4:]
195 196 elif _status == "3string":
196 197 currentLine = src[src.rfind('>>>') + 4:]
197 198 currentLine = currentLine.replace('\n... ', '\n')
198 199 else:
199 200 currentLine = src[src.rfind('...') + 4:]
200 201 if _status == 'main' and not currentLine.strip():
201 202 doc['console'].value += '\n>>> '
202 203 event.preventDefault()
203 204 return
204 205 doc['console'].value += '\n'
205 206 history.append(currentLine)
206 207 current = len(history)
207 208 if _status == "main" or _status == "3string":
208 209 try:
209 210 _ = editor_ns['_'] = eval(currentLine, editor_ns)
210 211 if _ is not None:
211 212 write(repr(_)+'\n')
212 213 doc['console'].value += '>>> '
213 214 _status = "main"
214 215 except IndentationError:
215 216 doc['console'].value += '... '
216 217 _status = "block"
217 218 except SyntaxError as msg:
218 219 if str(msg) == 'invalid syntax : triple string end not found' or \
219 220 str(msg).startswith('Unbalanced bracket'):
220 221 doc['console'].value += '... '
221 222 _status = "3string"
222 223 elif str(msg) == 'eval() argument must be an expression':
223 224 try:
224 225 exec(currentLine, editor_ns)
225 226 except:
226 227 traceback.print_exc()
227 228 doc['console'].value += '>>> '
228 229 _status = "main"
229 230 elif str(msg) == 'decorator expects function':
230 231 doc['console'].value += '... '
231 232 _status = "block"
232 233 else:
233 234 traceback.print_exc()
234 235 doc['console'].value += '>>> '
235 236 _status = "main"
236 237 except:
237 238 traceback.print_exc()
238 239 doc['console'].value += '>>> '
239 240 _status = "main"
240 241 elif currentLine == "": # end of block
241 242 block = src[src.rfind('>>>') + 4:].splitlines()
242 243 block = [block[0]] + [b[4:] for b in block[1:]]
243 244 block_src = '\n'.join(block)
244 245 # status must be set before executing code in globals()
245 246 _status = "main"
246 247 try:
247 248 _ = exec(block_src, editor_ns)
248 249 if _ is not None:
249 250 print(repr(_))
250 251 except:
251 252 traceback.print_exc()
252 253 doc['console'].value += '>>> '
253 254 else:
254 255 doc['console'].value += '... '
255 256
256 257 cursorToEnd()
257 258 event.preventDefault()
258 259
259 260 def myKeyDown(event):
260 261 global _status, current
261 262 if event.keyCode == 37: # left arrow
262 263 sel = get_col(doc['console'])
263 264 if sel < 5:
264 265 event.preventDefault()
265 266 event.stopPropagation()
266 267 elif event.keyCode == 36: # line start
267 268 pos = doc['console'].selectionStart
268 269 col = get_col(doc['console'])
269 270 doc['console'].setSelectionRange(pos - col + 4, pos - col + 4)
270 271 event.preventDefault()
271 272 elif event.keyCode == 38: # up
272 273 if current > 0:
273 274 pos = doc['console'].selectionStart
274 275 col = get_col(doc['console'])
275 276 # remove current line
276 277 doc['console'].value = doc['console'].value[:pos - col + 4]
277 278 current -= 1
278 279 doc['console'].value += history[current]
279 280 event.preventDefault()
280 281 elif event.keyCode == 40: # down
281 282 if current < len(history) - 1:
282 283 pos = doc['console'].selectionStart
283 284 col = get_col(doc['console'])
284 285 # remove current line
285 286 doc['console'].value = doc['console'].value[:pos - col + 4]
286 287 current += 1
287 288 doc['console'].value += history[current]
288 289 event.preventDefault()
289 290 elif event.keyCode == 8: # backspace
290 291 src = doc['console'].value
291 292 lstart = src.rfind('\n')
292 293 if (lstart == -1 and len(src) < 5) or (len(src) - lstart < 6):
293 294 event.preventDefault()
294 295 event.stopPropagation()
295 296
296 297
297 298 doc['console'].bind('keypress', myKeyPress)
298 299 doc['console'].bind('keydown', myKeyDown)
299 300 doc['console'].bind('click', cursorToEnd)
300 301 v = sys.implementation.version
301 302 doc['console'].value = "Brython %s.%s.%s on %s %s\n>>> " % (
302 303 v[0], v[1], v[2], window.navigator.appName, window.navigator.appVersion)
303 304 #doc['console'].value += 'Type "copyright", "credits" or "license" for more information.'
304 305 doc['console'].focus()
305 306 cursorToEnd()
306 307
307 308
308 309
309 310
@@ -1,171 +1,174
1 1 Rails.application.routes.draw do
2 2 resources :tags
3 3 get "sources/direct_edit"
4 4
5 5 root :to => 'main#login'
6 6
7 7 #logins
8 8 match 'login/login', to: 'login#login', via: [:get,:post]
9 9
10 10
11 11 resources :contests
12 12
13 13 resources :sites
14 14
15 15 resources :test
16 16
17 17 resources :messages do
18 18 collection do
19 19 get 'console'
20 20 end
21 21 end
22 22
23 23 resources :announcements do
24 24 member do
25 25 get 'toggle','toggle_front'
26 26 end
27 27 end
28 28
29 29 resources :problems do
30 30 member do
31 31 get 'toggle'
32 32 get 'toggle_test'
33 33 get 'toggle_view_testcase'
34 34 get 'stat'
35 35 end
36 36 collection do
37 37 get 'turn_all_off'
38 38 get 'turn_all_on'
39 39 get 'import'
40 40 get 'manage'
41 41 get 'quick_create'
42 42 post 'do_manage'
43 + post 'do_import'
43 44 end
44 45 end
45 46
46 47 resources :groups do
47 48 member do
48 49 post 'add_user', to: 'groups#add_user', as: 'add_user'
49 50 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
50 51 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
51 52 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
52 53 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
53 54 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
54 55 end
55 56 collection do
56 57
57 58 end
58 59 end
59 60
60 61 resources :testcases, only: [] do
61 62 member do
62 63 get 'download_input'
63 64 get 'download_sol'
64 65 end
65 66 collection do
66 67 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
67 68 end
68 69 end
69 70
70 71 resources :grader_configuration, controller: 'configurations'
71 72
72 73 resources :users do
73 74 member do
74 75 get 'toggle_activate', 'toggle_enable'
75 76 get 'stat'
76 77 end
77 78 end
78 79
79 80 resources :submissions do
80 81 member do
81 82 get 'download'
82 83 get 'compiler_msg'
83 84 get 'rejudge'
85 + get 'source'
84 86 end
85 87 collection do
86 88 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
87 89 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
88 90 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
89 91 end
90 92 end
91 93
92 94
93 95 #user admin
94 96 resources :user_admin do
95 97 collection do
96 98 match 'bulk_manage', via: [:get, :post]
97 99 get 'bulk_mail'
98 100 get 'user_stat'
99 101 get 'import'
100 102 get 'new_list'
101 103 get 'admin'
102 104 get 'active'
103 105 get 'mass_mailing'
104 106 post 'grant_admin'
105 107 match 'create_from_list', via: [:get, :post]
106 108 match 'random_all_passwords', via: [:get, :post]
107 109 end
108 110 member do
109 111 get 'clear_last_ip'
110 112 end
111 113 end
112 114
113 115 resources :contest_management, only: [:index] do
114 116 collection do
115 117 get 'user_stat'
116 118 get 'clear_stat'
117 119 get 'clear_all_stat'
120 + get 'change_contest_mode'
118 121 end
119 122 end
120 123
121 124 #get 'user_admin', to: 'user_admin#index'
122 125 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
123 126 #post 'user_admin', to: 'user_admin#create'
124 127 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
125 128
126 129 #singular resource
127 130 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
128 131 #report
129 132 resource :report, only: [], controller: 'report' do
130 133 get 'login'
131 134 get 'multiple_login'
132 135 get 'problem_hof/:id', action: 'problem_hof'
133 136 get 'current_score'
134 137 get 'max_score'
135 138 post 'show_max_score'
136 139 end
137 140 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
138 141 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
139 142 #get "report/login"
140 143 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
141 144 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
142 145
143 146 resource :main, only: [], controller: 'main' do
144 147 get 'list'
145 148 get 'submission(/:id)', action: 'submission', as: 'main_submission'
146 149 post 'submit'
147 150 get 'announcements'
148 151 get 'help'
149 152 end
150 153 #main
151 154 #get "main/list"
152 155 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
153 156 #post 'main/submit', to: 'main#submit'
154 157 #get 'main/announcements', to: 'main#announcements'
155 158
156 159
157 160 #
158 161 get 'tasks/view/:file.:ext' => 'tasks#view'
159 162 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
160 163 get 'heartbeat/:id/edit' => 'heartbeat#edit'
161 164
162 165 #grader
163 166 get 'graders/list', to: 'graders#list', as: 'grader_list'
164 167
165 168
166 169 # See how all your routes lay out with "rake routes"
167 170
168 171 # This is a legacy wild controller route that's not recommended for RESTful applications.
169 172 # Note: This route will make all actions in every controller accessible via GET requests.
170 173 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
171 174 end
You need to be logged in to leave comments. Login now