Description:
- fix ssl, we no longer check SSL for the API call to chula
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r585:0d6ca27c4e8d - - 4 files changed: 8 inserted, 7 deleted

@@ -1,380 +1,381
1 require 'digest/sha1'
1 require 'digest/sha1'
2 require 'net/pop'
2 require 'net/pop'
3 require 'net/https'
3 require 'net/https'
4 require 'net/http'
4 require 'net/http'
5 require 'json'
5 require 'json'
6
6
7 class User < ActiveRecord::Base
7 class User < ActiveRecord::Base
8
8
9 has_and_belongs_to_many :roles
9 has_and_belongs_to_many :roles
10
10
11 has_many :test_requests, :order => "submitted_at DESC"
11 has_many :test_requests, :order => "submitted_at DESC"
12
12
13 has_many :messages,
13 has_many :messages,
14 :class_name => "Message",
14 :class_name => "Message",
15 :foreign_key => "sender_id",
15 :foreign_key => "sender_id",
16 :order => 'created_at DESC'
16 :order => 'created_at DESC'
17
17
18 has_many :replied_messages,
18 has_many :replied_messages,
19 :class_name => "Message",
19 :class_name => "Message",
20 :foreign_key => "receiver_id",
20 :foreign_key => "receiver_id",
21 :order => 'created_at DESC'
21 :order => 'created_at DESC'
22
22
23 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
23 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
24
24
25 belongs_to :site
25 belongs_to :site
26 belongs_to :country
26 belongs_to :country
27
27
28 has_and_belongs_to_many :contests, :uniq => true, :order => 'name'
28 has_and_belongs_to_many :contests, :uniq => true, :order => 'name'
29
29
30 scope :activated_users, :conditions => {:activated => true}
30 scope :activated_users, :conditions => {:activated => true}
31
31
32 validates_presence_of :login
32 validates_presence_of :login
33 validates_uniqueness_of :login
33 validates_uniqueness_of :login
34 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
34 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
35 validates_length_of :login, :within => 3..30
35 validates_length_of :login, :within => 3..30
36
36
37 validates_presence_of :full_name
37 validates_presence_of :full_name
38 validates_length_of :full_name, :minimum => 1
38 validates_length_of :full_name, :minimum => 1
39
39
40 validates_presence_of :password, :if => :password_required?
40 validates_presence_of :password, :if => :password_required?
41 validates_length_of :password, :within => 4..20, :if => :password_required?
41 validates_length_of :password, :within => 4..20, :if => :password_required?
42 validates_confirmation_of :password, :if => :password_required?
42 validates_confirmation_of :password, :if => :password_required?
43
43
44 validates_format_of :email,
44 validates_format_of :email,
45 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
45 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
46 :if => :email_validation?
46 :if => :email_validation?
47 validate :uniqueness_of_email_from_activated_users,
47 validate :uniqueness_of_email_from_activated_users,
48 :if => :email_validation?
48 :if => :email_validation?
49 validate :enough_time_interval_between_same_email_registrations,
49 validate :enough_time_interval_between_same_email_registrations,
50 :if => :email_validation?
50 :if => :email_validation?
51
51
52 # these are for ytopc
52 # these are for ytopc
53 # disable for now
53 # disable for now
54 #validates_presence_of :province
54 #validates_presence_of :province
55
55
56 attr_accessor :password
56 attr_accessor :password
57
57
58 before_save :encrypt_new_password
58 before_save :encrypt_new_password
59 before_save :assign_default_site
59 before_save :assign_default_site
60 before_save :assign_default_contest
60 before_save :assign_default_contest
61
61
62 # this is for will_paginate
62 # this is for will_paginate
63 cattr_reader :per_page
63 cattr_reader :per_page
64 @@per_page = 50
64 @@per_page = 50
65
65
66 def self.authenticate(login, password)
66 def self.authenticate(login, password)
67 user = find_by_login(login)
67 user = find_by_login(login)
68 if user
68 if user
69 return user if user.authenticated?(password)
69 return user if user.authenticated?(password)
70 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
70 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
71 user.password = password
71 user.password = password
72 user.save
72 user.save
73 return user
73 return user
74 end
74 end
75 end
75 end
76 end
76 end
77
77
78 def authenticated?(password)
78 def authenticated?(password)
79 if self.activated
79 if self.activated
80 hashed_password == User.encrypt(password,self.salt)
80 hashed_password == User.encrypt(password,self.salt)
81 else
81 else
82 false
82 false
83 end
83 end
84 end
84 end
85
85
86 def authenticated_by_pop3?(password)
86 def authenticated_by_pop3?(password)
87 Net::POP3.enable_ssl
87 Net::POP3.enable_ssl
88 pop = Net::POP3.new('pops.it.chula.ac.th')
88 pop = Net::POP3.new('pops.it.chula.ac.th')
89 authen = true
89 authen = true
90 begin
90 begin
91 pop.start(login, password)
91 pop.start(login, password)
92 pop.finish
92 pop.finish
93 return true
93 return true
94 rescue
94 rescue
95 return false
95 return false
96 end
96 end
97 end
97 end
98
98
99 def authenticated_by_cucas?(password)
99 def authenticated_by_cucas?(password)
100 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
100 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
101 appid = '41508763e340d5858c00f8c1a0f5a2bb'
101 appid = '41508763e340d5858c00f8c1a0f5a2bb'
102 appsecret ='d9cbb5863091dbe186fded85722a1e31'
102 appsecret ='d9cbb5863091dbe186fded85722a1e31'
103 post_args = {
103 post_args = {
104 'appid' => appid,
104 'appid' => appid,
105 'appsecret' => appsecret,
105 'appsecret' => appsecret,
106 'username' => login,
106 'username' => login,
107 'password' => password
107 'password' => password
108 }
108 }
109
109
110 #simple call
110 #simple call
111 begin
111 begin
112 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
112 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
113 http.use_ssl = true
113 http.use_ssl = true
114 + http.verify_mode = OpenSSL::SSL::VERIFY_NONE
114 result = [ ]
115 result = [ ]
115 http.start do |http|
116 http.start do |http|
116 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
117 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
117 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
118 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
118 resp = http.request(req,param)
119 resp = http.request(req,param)
119 result = JSON.parse resp.body
120 result = JSON.parse resp.body
120 end
121 end
121 return true if result["type"] == "beanStudent"
122 return true if result["type"] == "beanStudent"
122 - rescue
123 + rescue => e
123 return false
124 return false
124 end
125 end
125 return false
126 return false
126 end
127 end
127
128
128 def admin?
129 def admin?
129 self.roles.detect {|r| r.name == 'admin' }
130 self.roles.detect {|r| r.name == 'admin' }
130 end
131 end
131
132
132 def email_for_editing
133 def email_for_editing
133 if self.email==nil
134 if self.email==nil
134 "(unknown)"
135 "(unknown)"
135 elsif self.email==''
136 elsif self.email==''
136 "(blank)"
137 "(blank)"
137 else
138 else
138 self.email
139 self.email
139 end
140 end
140 end
141 end
141
142
142 def email_for_editing=(e)
143 def email_for_editing=(e)
143 self.email=e
144 self.email=e
144 end
145 end
145
146
146 def alias_for_editing
147 def alias_for_editing
147 if self.alias==nil
148 if self.alias==nil
148 "(unknown)"
149 "(unknown)"
149 elsif self.alias==''
150 elsif self.alias==''
150 "(blank)"
151 "(blank)"
151 else
152 else
152 self.alias
153 self.alias
153 end
154 end
154 end
155 end
155
156
156 def alias_for_editing=(e)
157 def alias_for_editing=(e)
157 self.alias=e
158 self.alias=e
158 end
159 end
159
160
160 def activation_key
161 def activation_key
161 if self.hashed_password==nil
162 if self.hashed_password==nil
162 encrypt_new_password
163 encrypt_new_password
163 end
164 end
164 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
165 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
165 end
166 end
166
167
167 def verify_activation_key(key)
168 def verify_activation_key(key)
168 key == activation_key
169 key == activation_key
169 end
170 end
170
171
171 def self.random_password(length=5)
172 def self.random_password(length=5)
172 chars = 'abcdefghjkmnopqrstuvwxyz'
173 chars = 'abcdefghjkmnopqrstuvwxyz'
173 password = ''
174 password = ''
174 length.times { password << chars[rand(chars.length - 1)] }
175 length.times { password << chars[rand(chars.length - 1)] }
175 password
176 password
176 end
177 end
177
178
178 def self.find_non_admin_with_prefix(prefix='')
179 def self.find_non_admin_with_prefix(prefix='')
179 users = User.find(:all)
180 users = User.find(:all)
180 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
181 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
181 end
182 end
182
183
183 # Contest information
184 # Contest information
184
185
185 def self.find_users_with_no_contest()
186 def self.find_users_with_no_contest()
186 users = User.find(:all)
187 users = User.find(:all)
187 return users.find_all { |u| u.contests.length == 0 }
188 return users.find_all { |u| u.contests.length == 0 }
188 end
189 end
189
190
190
191
191 def contest_time_left
192 def contest_time_left
192 if GraderConfiguration.contest_mode?
193 if GraderConfiguration.contest_mode?
193 return nil if site==nil
194 return nil if site==nil
194 return site.time_left
195 return site.time_left
195 elsif GraderConfiguration.indv_contest_mode?
196 elsif GraderConfiguration.indv_contest_mode?
196 time_limit = GraderConfiguration.contest_time_limit
197 time_limit = GraderConfiguration.contest_time_limit
197 if time_limit == nil
198 if time_limit == nil
198 return nil
199 return nil
199 end
200 end
200 if contest_stat==nil or contest_stat.started_at==nil
201 if contest_stat==nil or contest_stat.started_at==nil
201 return (Time.now.gmtime + time_limit) - Time.now.gmtime
202 return (Time.now.gmtime + time_limit) - Time.now.gmtime
202 else
203 else
203 finish_time = contest_stat.started_at + time_limit
204 finish_time = contest_stat.started_at + time_limit
204 current_time = Time.now.gmtime
205 current_time = Time.now.gmtime
205 if current_time > finish_time
206 if current_time > finish_time
206 return 0
207 return 0
207 else
208 else
208 return finish_time - current_time
209 return finish_time - current_time
209 end
210 end
210 end
211 end
211 else
212 else
212 return nil
213 return nil
213 end
214 end
214 end
215 end
215
216
216 def contest_finished?
217 def contest_finished?
217 if GraderConfiguration.contest_mode?
218 if GraderConfiguration.contest_mode?
218 return false if site==nil
219 return false if site==nil
219 return site.finished?
220 return site.finished?
220 elsif GraderConfiguration.indv_contest_mode?
221 elsif GraderConfiguration.indv_contest_mode?
221 return false if self.contest_stat(true)==nil
222 return false if self.contest_stat(true)==nil
222 return contest_time_left == 0
223 return contest_time_left == 0
223 else
224 else
224 return false
225 return false
225 end
226 end
226 end
227 end
227
228
228 def contest_started?
229 def contest_started?
229 if GraderConfiguration.indv_contest_mode?
230 if GraderConfiguration.indv_contest_mode?
230 stat = self.contest_stat
231 stat = self.contest_stat
231 return ((stat != nil) and (stat.started_at != nil))
232 return ((stat != nil) and (stat.started_at != nil))
232 elsif GraderConfiguration.contest_mode?
233 elsif GraderConfiguration.contest_mode?
233 return true if site==nil
234 return true if site==nil
234 return site.started
235 return site.started
235 else
236 else
236 return true
237 return true
237 end
238 end
238 end
239 end
239
240
240 def update_start_time
241 def update_start_time
241 stat = self.contest_stat
242 stat = self.contest_stat
242 - if (stat.nil?) or (stat.started_at.nil?)
243 + if stat.nil? or stat.started_at.nil?
243 stat ||= UserContestStat.new(:user => self)
244 stat ||= UserContestStat.new(:user => self)
244 stat.started_at = Time.now.gmtime
245 stat.started_at = Time.now.gmtime
245 stat.save
246 stat.save
246 end
247 end
247 end
248 end
248
249
249 def problem_in_user_contests?(problem)
250 def problem_in_user_contests?(problem)
250 problem_contests = problem.contests.all
251 problem_contests = problem.contests.all
251
252
252 if problem_contests.length == 0 # this is public contest
253 if problem_contests.length == 0 # this is public contest
253 return true
254 return true
254 end
255 end
255
256
256 contests.each do |contest|
257 contests.each do |contest|
257 if problem_contests.find {|c| c.id == contest.id }
258 if problem_contests.find {|c| c.id == contest.id }
258 return true
259 return true
259 end
260 end
260 end
261 end
261 return false
262 return false
262 end
263 end
263
264
264 def available_problems_group_by_contests
265 def available_problems_group_by_contests
265 contest_problems = []
266 contest_problems = []
266 pin = {}
267 pin = {}
267 contests.enabled.each do |contest|
268 contests.enabled.each do |contest|
268 available_problems = contest.problems.available
269 available_problems = contest.problems.available
269 contest_problems << {
270 contest_problems << {
270 :contest => contest,
271 :contest => contest,
271 :problems => available_problems
272 :problems => available_problems
272 }
273 }
273 available_problems.each {|p| pin[p.id] = true}
274 available_problems.each {|p| pin[p.id] = true}
274 end
275 end
275 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
276 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
276 contest_problems << {
277 contest_problems << {
277 :contest => nil,
278 :contest => nil,
278 :problems => other_avaiable_problems
279 :problems => other_avaiable_problems
279 }
280 }
280 return contest_problems
281 return contest_problems
281 end
282 end
282
283
283 def available_problems
284 def available_problems
284 if not GraderConfiguration.multicontests?
285 if not GraderConfiguration.multicontests?
285 return Problem.find_available_problems
286 return Problem.find_available_problems
286 else
287 else
287 contest_problems = []
288 contest_problems = []
288 pin = {}
289 pin = {}
289 contests.enabled.each do |contest|
290 contests.enabled.each do |contest|
290 contest.problems.available.each do |problem|
291 contest.problems.available.each do |problem|
291 if not pin.has_key? problem.id
292 if not pin.has_key? problem.id
292 contest_problems << problem
293 contest_problems << problem
293 end
294 end
294 pin[problem.id] = true
295 pin[problem.id] = true
295 end
296 end
296 end
297 end
297 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
298 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
298 return contest_problems + other_avaiable_problems
299 return contest_problems + other_avaiable_problems
299 end
300 end
300 end
301 end
301
302
302 def can_view_problem?(problem)
303 def can_view_problem?(problem)
303 if not GraderConfiguration.multicontests?
304 if not GraderConfiguration.multicontests?
304 return problem.available
305 return problem.available
305 else
306 else
306 return problem_in_user_contests? problem
307 return problem_in_user_contests? problem
307 end
308 end
308 end
309 end
309
310
310 def self.clear_last_login
311 def self.clear_last_login
311 User.update_all(:last_ip => nil)
312 User.update_all(:last_ip => nil)
312 end
313 end
313
314
314 protected
315 protected
315 def encrypt_new_password
316 def encrypt_new_password
316 return if password.blank?
317 return if password.blank?
317 self.salt = (10+rand(90)).to_s
318 self.salt = (10+rand(90)).to_s
318 self.hashed_password = User.encrypt(self.password,self.salt)
319 self.hashed_password = User.encrypt(self.password,self.salt)
319 end
320 end
320
321
321 def assign_default_site
322 def assign_default_site
322 # have to catch error when migrating (because self.site is not available).
323 # have to catch error when migrating (because self.site is not available).
323 begin
324 begin
324 if self.site==nil
325 if self.site==nil
325 self.site = Site.find_by_name('default')
326 self.site = Site.find_by_name('default')
326 if self.site==nil
327 if self.site==nil
327 self.site = Site.find(1) # when 'default has be renamed'
328 self.site = Site.find(1) # when 'default has be renamed'
328 end
329 end
329 end
330 end
330 rescue
331 rescue
331 end
332 end
332 end
333 end
333
334
334 def assign_default_contest
335 def assign_default_contest
335 # have to catch error when migrating (because self.site is not available).
336 # have to catch error when migrating (because self.site is not available).
336 begin
337 begin
337 if self.contests.length == 0
338 if self.contests.length == 0
338 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
339 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
339 if default_contest
340 if default_contest
340 self.contests = [default_contest]
341 self.contests = [default_contest]
341 end
342 end
342 end
343 end
343 rescue
344 rescue
344 end
345 end
345 end
346 end
346
347
347 def password_required?
348 def password_required?
348 self.hashed_password.blank? || !self.password.blank?
349 self.hashed_password.blank? || !self.password.blank?
349 end
350 end
350
351
351 def self.encrypt(string,salt)
352 def self.encrypt(string,salt)
352 Digest::SHA1.hexdigest(salt + string)
353 Digest::SHA1.hexdigest(salt + string)
353 end
354 end
354
355
355 def uniqueness_of_email_from_activated_users
356 def uniqueness_of_email_from_activated_users
356 user = User.activated_users.find_by_email(self.email)
357 user = User.activated_users.find_by_email(self.email)
357 if user and (user.login != self.login)
358 if user and (user.login != self.login)
358 self.errors.add(:base,"Email has already been taken")
359 self.errors.add(:base,"Email has already been taken")
359 end
360 end
360 end
361 end
361
362
362 def enough_time_interval_between_same_email_registrations
363 def enough_time_interval_between_same_email_registrations
363 return if !self.new_record?
364 return if !self.new_record?
364 return if self.activated
365 return if self.activated
365 open_user = User.find_by_email(self.email,
366 open_user = User.find_by_email(self.email,
366 :order => 'created_at DESC')
367 :order => 'created_at DESC')
367 if open_user and open_user.created_at and
368 if open_user and open_user.created_at and
368 (open_user.created_at > Time.now.gmtime - 5.minutes)
369 (open_user.created_at > Time.now.gmtime - 5.minutes)
369 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
370 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
370 end
371 end
371 end
372 end
372
373
373 def email_validation?
374 def email_validation?
374 begin
375 begin
375 return VALIDATE_USER_EMAILS
376 return VALIDATE_USER_EMAILS
376 rescue
377 rescue
377 return false
378 return false
378 end
379 end
379 end
380 end
380 end
381 end
@@ -1,11 +1,11
1
1
2 %td= grader.host
2 %td= grader.host
3 %td= grader.pid
3 %td= grader.pid
4 %td= grader.mode
4 %td= grader.mode
5 - %td= grader.updated_at.strftime("%H:%M:%S") unless grader.updated_at.nil?
5 + %td= grader.updated_at.strftime("%H:%M:%S") if grader.updated_at
6 %td= grader.task_type
6 %td= grader.task_type
7 %td
7 %td
8 - - if grader.task_id.nil?
8 + - unless grader.task_id
9 idle
9 idle
10 - else
10 - else
11 = link_to "#{grader.task_id}", :action => 'view', :id => grader.task_id, :type => grader.task_type
11 = link_to "#{grader.task_id}", :action => 'view', :id => grader.task_id, :type => grader.task_type
@@ -1,26 +1,26
1
1
2 - if submission.nil?
2 - if submission.nil?
3 = "-"
3 = "-"
4 - else
4 - else
5 - if submission.graded_at.nil?
5 - if submission.graded_at.nil?
6 =t 'main.submitted_at'
6 =t 'main.submitted_at'
7 = format_short_time(submission.submitted_at.localtime)
7 = format_short_time(submission.submitted_at.localtime)
8 - else
8 - else
9 = t 'main.graded_at'
9 = t 'main.graded_at'
10 = "#{format_short_time(submission.graded_at.localtime)}, "
10 = "#{format_short_time(submission.graded_at.localtime)}, "
11 - if GraderConfiguration['ui.show_score']
11 - if GraderConfiguration['ui.show_score']
12 = t 'main.score'
12 = t 'main.score'
13 = "#{(submission.points*100/submission.problem.full_score).to_i} "
13 = "#{(submission.points*100/submission.problem.full_score).to_i} "
14 = " ["
14 = " ["
15 %tt
15 %tt
16 = submission.grader_comment
16 = submission.grader_comment
17 = "]"
17 = "]"
18 - if GraderConfiguration.show_grading_result
18 - if GraderConfiguration.show_grading_result
19 = " | "
19 = " | "
20 = link_to '[detailed result]', :action => 'result', :id => submission.id
20 = link_to '[detailed result]', :action => 'result', :id => submission.id
21 = " | "
21 = " | "
22 = link_to("[#{t 'main.cmp_msg'}]", {:action => 'compiler_msg', :id => submission.id}, {:popup => true})
22 = link_to("[#{t 'main.cmp_msg'}]", {:action => 'compiler_msg', :id => submission.id}, {:popup => true})
23 = " | "
23 = " | "
24 = link_to("[#{t 'main.src_link'}]",{:action => 'source', :id => submission.id})
24 = link_to("[#{t 'main.src_link'}]",{:action => 'source', :id => submission.id})
25 - //= " | "
25 + = " | "
26 - //= link_to "[#{t 'main.submissions_link'}]", main_submission_path(submission.problem.id)
26 + = link_to "[#{t 'main.submissions_link'}]", :action => 'submission', :id => problem_name
@@ -1,61 +1,61
1 /- content_for :header do
1 /- content_for :header do
2 / = javascript_include_tag 'local_jquery'
2 / = javascript_include_tag 'local_jquery'
3 / = stylesheet_link_tag 'tablesorter-theme.cafe'
3 / = stylesheet_link_tag 'tablesorter-theme.cafe'
4
4
5 %script{:type=>"text/javascript"}
5 %script{:type=>"text/javascript"}
6 $(function () {
6 $(function () {
7 $('#since_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
7 $('#since_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
8 $('#until_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
8 $('#until_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
9 /$('#my_table').tablesorter({widgets: ['zebra']});
9 /$('#my_table').tablesorter({widgets: ['zebra']});
10 });
10 });
11
11
12 %h1 User grading results
12 %h1 User grading results
13 %h2= params[:action] == 'user_stat' ? "Show scores from latest submission" : "Show max scores in submission range"
13 %h2= params[:action] == 'user_stat' ? "Show scores from latest submission" : "Show max scores in submission range"
14
14
15
15
16 - if @problem and @problem.errors
16 - if @problem and @problem.errors
17 =error_messages_for 'problem'
17 =error_messages_for 'problem'
18
18
19 = render partial: 'submission_range'
19 = render partial: 'submission_range'
20
20
21 - if params[:action] == 'user_stat'
21 - if params[:action] == 'user_stat'
22 %h3 Latest score
22 %h3 Latest score
23 = link_to '[download csv with all problems]', controller: :user_admin, action: :user_stat, commit: 'download csv'
23 = link_to '[download csv with all problems]', controller: :user_admin, action: :user_stat, commit: 'download csv'
24 - else
24 - else
25 %h3 Max score
25 %h3 Max score
26 = link_to '[Show only latest submissions]', controller: :user_admin, action: :user_stat
26 = link_to '[Show only latest submissions]', controller: :user_admin, action: :user_stat
27 = link_to '[download csv with all problems]', controller: :user_admin, action: :user_stat_max, commit: 'download csv'
27 = link_to '[download csv with all problems]', controller: :user_admin, action: :user_stat_max, commit: 'download csv'
28
28
29 %table.table.sortable.table-striped.table-bordered
29 %table.table.sortable.table-striped.table-bordered
30 %thead
30 %thead
31 %tr
31 %tr
32 %th Login
32 %th Login
33 %th Name
33 %th Name
34 %th Activated?
34 %th Activated?
35 %th Logged_in
35 %th Logged_in
36 %th Contest(s)
36 %th Contest(s)
37 %th Remark
37 %th Remark
38 - @problems.each do |p|
38 - @problems.each do |p|
39 %th.text-right= p.name
39 %th.text-right= p.name
40 %th.text-right Total
40 %th.text-right Total
41 %th.text-right Passed
41 %th.text-right Passed
42 %tbody
42 %tbody
43 - @scorearray.each do |sc|
43 - @scorearray.each do |sc|
44 %tr
44 %tr
45 - total,num_passed = 0,0
45 - total,num_passed = 0,0
46 - sc.each_index do |i|
46 - sc.each_index do |i|
47 - if i == 0
47 - if i == 0
48 %td= link_to sc[i].login, controller: 'users', action: 'profile', id: sc[i]
48 %td= link_to sc[i].login, controller: 'users', action: 'profile', id: sc[i]
49 %td= sc[i].full_name
49 %td= sc[i].full_name
50 %td= sc[i].activated
50 %td= sc[i].activated
51 - %td= sc[i].try(:contest_stat).try(:started_at)!=nil ? 'yes' : 'no'
51 + %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no'
52 %td= sc[i].contests.collect {|c| c.name}.join(', ')
52 %td= sc[i].contests.collect {|c| c.name}.join(', ')
53 %td= sc[i].remark
53 %td= sc[i].remark
54 - else
54 - else
55 %td.text-right= sc[i][0]
55 %td.text-right= sc[i][0]
56 - total += sc[i][0]
56 - total += sc[i][0]
57 - num_passed += 1 if sc[i][1]
57 - num_passed += 1 if sc[i][1]
58 %td.text-right= total
58 %td.text-right= total
59 %td.text-right= num_passed
59 %td.text-right= num_passed
60 :javascript
60 :javascript
61 $.bootstrapSortable(true,'reversed')
61 $.bootstrapSortable(true,'reversed')
You need to be logged in to leave comments. Login now