Description:
allow ta to set model submission
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r856:9a927c70d5aa - - 4 files changed: 13 inserted, 10 deleted

@@ -1,501 +1,501
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_many :logins
26 26
27 27 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
28 28
29 29 belongs_to :site
30 30 belongs_to :country
31 31
32 32 has_and_belongs_to_many :contests, -> { order(:name)}
33 33
34 34 scope :activated_users, -> {where activated: true}
35 35
36 36 validates_presence_of :login
37 37 validates_uniqueness_of :login
38 38 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
39 39 validates_length_of :login, :within => 3..30
40 40
41 41 validates_presence_of :full_name
42 42 validates_length_of :full_name, :minimum => 1
43 43
44 44 validates_presence_of :password, :if => :password_required?
45 45 validates_length_of :password, :within => 4..50, :if => :password_required?
46 46 validates_confirmation_of :password, :if => :password_required?
47 47
48 48 validates_format_of :email,
49 49 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
50 50 :if => :email_validation?
51 51 validate :uniqueness_of_email_from_activated_users,
52 52 :if => :email_validation?
53 53 validate :enough_time_interval_between_same_email_registrations,
54 54 :if => :email_validation?
55 55
56 56 # these are for ytopc
57 57 # disable for now
58 58 #validates_presence_of :province
59 59
60 60 attr_accessor :password
61 61
62 62 before_save :encrypt_new_password
63 63 before_save :assign_default_site
64 64 before_save :assign_default_contest
65 65
66 66 # this is for will_paginate
67 67 cattr_reader :per_page
68 68 @@per_page = 50
69 69
70 70 def self.authenticate(login, password)
71 71 user = find_by_login(login)
72 72 if user
73 73 return user if user.authenticated?(password)
74 74 if user.authenticated_by_cucas?(password)
75 75 user.password = password
76 76 user.save
77 77 return user
78 78 end
79 79 end
80 80 end
81 81
82 82
83 83 def authenticated?(password)
84 84 if self.activated
85 85 hashed_password == User.encrypt(password,self.salt)
86 86 else
87 87 false
88 88 end
89 89 end
90 90
91 91 def login_with_name
92 92 "[#{login}] #{full_name}"
93 93 end
94 94
95 95 def authenticated_by_cucas?(password)
96 96 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
97 97 appid = '41508763e340d5858c00f8c1a0f5a2bb'
98 98 appsecret ='d9cbb5863091dbe186fded85722a1e31'
99 99 post_args = {
100 100 'appid' => appid,
101 101 'appsecret' => appsecret,
102 102 'username' => login,
103 103 'password' => password
104 104 }
105 105
106 106 #simple call
107 107 begin
108 108 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
109 109 http.use_ssl = true
110 110 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
111 111 result = [ ]
112 112 http.start do |http|
113 113 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
114 114 #req = Net::HTTP::Post.new('/appX/prod/?q=studentAuthenticate')
115 115 #req = Net::HTTP::Post.new('/app2/prod/api/?q=studentAuthenticate')
116 116 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
117 117 resp = http.request(req,param)
118 118 result = JSON.parse resp.body
119 119 puts result
120 120 end
121 121 return true if result["type"] == "beanStudent"
122 122 rescue => e
123 123 puts e
124 124 puts e.message
125 125 return false
126 126 end
127 127 return false
128 128 end
129 129
130 130 def admin?
131 131 has_role?('admin')
132 132 end
133 133
134 134 def has_role?(role)
135 - self.roles.where(name: role).count > 0
135 + self.roles.where(name: [role,'admin']).count > 0
136 136 end
137 137
138 138 def email_for_editing
139 139 if self.email==nil
140 140 "(unknown)"
141 141 elsif self.email==''
142 142 "(blank)"
143 143 else
144 144 self.email
145 145 end
146 146 end
147 147
148 148 def email_for_editing=(e)
149 149 self.email=e
150 150 end
151 151
152 152 def alias_for_editing
153 153 if self.alias==nil
154 154 "(unknown)"
155 155 elsif self.alias==''
156 156 "(blank)"
157 157 else
158 158 self.alias
159 159 end
160 160 end
161 161
162 162 def alias_for_editing=(e)
163 163 self.alias=e
164 164 end
165 165
166 166 def activation_key
167 167 if self.hashed_password==nil
168 168 encrypt_new_password
169 169 end
170 170 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
171 171 end
172 172
173 173 def verify_activation_key(key)
174 174 key == activation_key
175 175 end
176 176
177 177 def self.random_password(length=5)
178 178 chars = 'abcdefghjkmnopqrstuvwxyz'
179 179 password = ''
180 180 length.times { password << chars[rand(chars.length - 1)] }
181 181 password
182 182 end
183 183
184 184 # Contest information
185 185
186 186 def self.find_users_with_no_contest()
187 187 users = User.all
188 188 return users.find_all { |u| u.contests.length == 0 }
189 189 end
190 190
191 191
192 192 def contest_time_left
193 193 if GraderConfiguration.contest_mode?
194 194 return nil if site==nil
195 195 return site.time_left
196 196 elsif GraderConfiguration.indv_contest_mode?
197 197 time_limit = GraderConfiguration.contest_time_limit
198 198 if time_limit == nil
199 199 return nil
200 200 end
201 201 if contest_stat==nil or contest_stat.started_at==nil
202 202 return (Time.now.gmtime + time_limit) - Time.now.gmtime
203 203 else
204 204 finish_time = contest_stat.started_at + time_limit
205 205 current_time = Time.now.gmtime
206 206 if current_time > finish_time
207 207 return 0
208 208 else
209 209 return finish_time - current_time
210 210 end
211 211 end
212 212 else
213 213 return nil
214 214 end
215 215 end
216 216
217 217 def contest_finished?
218 218 if GraderConfiguration.contest_mode?
219 219 return false if site==nil
220 220 return site.finished?
221 221 elsif GraderConfiguration.indv_contest_mode?
222 222 return false if self.contest_stat==nil
223 223 return contest_time_left == 0
224 224 else
225 225 return false
226 226 end
227 227 end
228 228
229 229 def contest_started?
230 230 if GraderConfiguration.indv_contest_mode?
231 231 stat = self.contest_stat
232 232 return ((stat != nil) and (stat.started_at != nil))
233 233 elsif GraderConfiguration.contest_mode?
234 234 return true if site==nil
235 235 return site.started
236 236 else
237 237 return true
238 238 end
239 239 end
240 240
241 241 def update_start_time
242 242 stat = self.contest_stat
243 243 if stat.nil? or stat.started_at.nil?
244 244 stat ||= UserContestStat.new(:user => self)
245 245 stat.started_at = Time.now.gmtime
246 246 stat.save
247 247 end
248 248 end
249 249
250 250 def problem_in_user_contests?(problem)
251 251 problem_contests = problem.contests.all
252 252
253 253 if problem_contests.length == 0 # this is public contest
254 254 return true
255 255 end
256 256
257 257 contests.each do |contest|
258 258 if problem_contests.find {|c| c.id == contest.id }
259 259 return true
260 260 end
261 261 end
262 262 return false
263 263 end
264 264
265 265 def available_problems_group_by_contests
266 266 contest_problems = []
267 267 pin = {}
268 268 contests.enabled.each do |contest|
269 269 available_problems = contest.problems.available
270 270 contest_problems << {
271 271 :contest => contest,
272 272 :problems => available_problems
273 273 }
274 274 available_problems.each {|p| pin[p.id] = true}
275 275 end
276 276 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
277 277 contest_problems << {
278 278 :contest => nil,
279 279 :problems => other_avaiable_problems
280 280 }
281 281 return contest_problems
282 282 end
283 283
284 284 def solve_all_available_problems?
285 285 available_problems.each do |p|
286 286 u = self
287 287 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
288 288 return false if !p or !sub or sub.points < p.full_score
289 289 end
290 290 return true
291 291 end
292 292
293 293 #get a list of available problem
294 294 def available_problems
295 295 # first, we check if this is normal mode
296 296 if not GraderConfiguration.multicontests?
297 297
298 298 #if this is a normal mode
299 299 #we show problem based on problem_group, if the config said so
300 300 if GraderConfiguration.use_problem_group?
301 301 return available_problems_in_group
302 302 else
303 303 return Problem.available_problems
304 304 end
305 305 else
306 306 #this is multi contest mode
307 307 contest_problems = []
308 308 pin = {}
309 309 contests.enabled.each do |contest|
310 310 contest.problems.available.each do |problem|
311 311 if not pin.has_key? problem.id
312 312 contest_problems << problem
313 313 end
314 314 pin[problem.id] = true
315 315 end
316 316 end
317 317 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
318 318 return contest_problems + other_avaiable_problems
319 319 end
320 320 end
321 321
322 322 # new feature, get list of available problem in all enabled group that the user belongs to
323 323 def available_problems_in_group
324 324 problem = []
325 325 self.groups.where(enabled: true).each do |group|
326 326 group.problems.where(available: true).each { |p| problem << p }
327 327 end
328 328 problem.uniq!
329 329 if problem
330 330 problem.sort! do |a,b|
331 331 case
332 332 when a.date_added < b.date_added
333 333 1
334 334 when a.date_added > b.date_added
335 335 -1
336 336 else
337 337 a.name <=> b.name
338 338 end
339 339 end
340 340 return problem
341 341 else
342 342 return []
343 343 end
344 344 end
345 345
346 346 #check if the user has the right to view that problem
347 347 #this also consider group based problem policy
348 348 def can_view_problem?(problem)
349 349 return true if admin?
350 350 return available_problems.include? problem
351 351 end
352 352
353 353 def self.clear_last_login
354 354 User.update_all(:last_ip => nil)
355 355 end
356 356
357 357 #create multiple user, one per lines of input
358 358 def self.create_from_list(lines)
359 359 error_logins = []
360 360 first_error = nil
361 361 created_users = []
362 362
363 363 lines.split("\n").each do |line|
364 364 #split with large limit, this will cause consecutive ',' to be result in a blank
365 365 items = line.chomp.split(',',1000)
366 366 if items.length>=2
367 367 login = items[0]
368 368 full_name = items[1]
369 369 remark =''
370 370 user_alias = ''
371 371
372 372 added_random_password = false
373 373 added_password = false
374 374
375 375 #given password?
376 376 if items.length >= 3
377 377 if items[2].chomp(" ").length > 0
378 378 password = items[2].chomp(" ")
379 379 added_password = true
380 380 end
381 381 else
382 382 password = random_password
383 383 added_random_password=true;
384 384 end
385 385
386 386 #given alias?
387 387 if items.length>= 4 and items[3].chomp(" ").length > 0;
388 388 user_alias = items[3].chomp(" ")
389 389 else
390 390 user_alias = login
391 391 end
392 392
393 393 #given remark?
394 394 has_remark = false
395 395 if items.length>=5
396 396 remark = items[4].strip;
397 397 has_remark = true
398 398 end
399 399
400 400 user = User.find_by_login(login)
401 401 if (user)
402 402 user.full_name = full_name
403 403 user.remark = remark if has_remark
404 404 user.password = password if added_password || added_random_password
405 405 else
406 406 #create a random password if none are given
407 407 password = random_password unless password
408 408 user = User.new({:login => login,
409 409 :full_name => full_name,
410 410 :password => password,
411 411 :password_confirmation => password,
412 412 :alias => user_alias,
413 413 :remark => remark})
414 414 end
415 415 user.activated = true
416 416
417 417 if user.save
418 418 created_users << user
419 419 else
420 420 error_logins << "'#{login}'"
421 421 first_error = user.errors.full_messages.to_sentence unless first_error
422 422 end
423 423 end
424 424 end
425 425
426 426 return {error_logins: error_logins, first_error: first_error, created_users: created_users}
427 427
428 428 end
429 429
430 430 def self.find_non_admin_with_prefix(prefix='')
431 431 users = User.all
432 432 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
433 433 end
434 434
435 435 protected
436 436 def encrypt_new_password
437 437 return if password.blank?
438 438 self.salt = (10+rand(90)).to_s
439 439 self.hashed_password = User.encrypt(self.password,self.salt)
440 440 end
441 441
442 442 def assign_default_site
443 443 # have to catch error when migrating (because self.site is not available).
444 444 begin
445 445 if self.site==nil
446 446 self.site = Site.find_by_name('default')
447 447 if self.site==nil
448 448 self.site = Site.find(1) # when 'default has be renamed'
449 449 end
450 450 end
451 451 rescue
452 452 end
453 453 end
454 454
455 455 def assign_default_contest
456 456 # have to catch error when migrating (because self.site is not available).
457 457 begin
458 458 if self.contests.length == 0
459 459 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
460 460 if default_contest
461 461 self.contests = [default_contest]
462 462 end
463 463 end
464 464 rescue
465 465 end
466 466 end
467 467
468 468 def password_required?
469 469 self.hashed_password.blank? || !self.password.blank?
470 470 end
471 471
472 472 def self.encrypt(string,salt)
473 473 Digest::SHA1.hexdigest(salt + string)
474 474 end
475 475
476 476 def uniqueness_of_email_from_activated_users
477 477 user = User.activated_users.find_by_email(self.email)
478 478 if user and (user.login != self.login)
479 479 self.errors.add(:base,"Email has already been taken")
480 480 end
481 481 end
482 482
483 483 def enough_time_interval_between_same_email_registrations
484 484 return if !self.new_record?
485 485 return if self.activated
486 486 open_user = User.find_by_email(self.email,
487 487 :order => 'created_at DESC')
488 488 if open_user and open_user.created_at and
489 489 (open_user.created_at > Time.now.gmtime - 5.minutes)
490 490 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
491 491 end
492 492 end
493 493
494 494 def email_validation?
495 495 begin
496 496 return VALIDATE_USER_EMAILS
497 497 rescue
498 498 return false
499 499 end
500 500 end
501 501 end
@@ -1,154 +1,156
1 1 :css
2 2 .hof_user { color: orangered; font-style: italic; }
3 3 .hof_language { color: green; font-style: italic; }
4 4 .hof_value { color: deeppink;font-style: italic; }
5 5 .info_param { font-weight: bold;text-align: right; }
6 6 .tooltip {
7 7 font-family: Verdana,sans-serif;
8 8 font-weight: normal;
9 9 text-align: left;
10 10 font-size: 1.0em;
11 11 color: black;
12 12 line-height: 1.1;
13 13 display: none;
14 14 min-width: 20em;
15 15 position: absolute;
16 16 left: 25px;
17 17 bottom: 5px;
18 18 border: 1px solid;
19 19 padding: 5px;
20 20 background-color: #FFF;
21 21 word-wrap: break-word;
22 22 z-index: 9999;
23 23 overflow: auto;
24 24 }
25 25
26 26
27 27 .container-fluid
28 28 .row
29 29 .col-md-8
30 30 .card
31 31 .card-body
32 32 %h2.card-title Submission History
33 33 %canvas#chart{height: '50px'}
34 34
35 35 .col-md-4
36 36 .card
37 37 .card-body
38 38 %h2.card-title General Info
39 39 .row
40 40 .col-sm-6
41 41 Subs
42 42 .col-sm-6
43 43 = @summary[:count]
44 44 .row
45 45 .col-sm-6
46 46 Solved/Attempted User
47 47 .col-sm-6
48 48 #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
49 49 .row
50 50 .col-md-4
51 51 .card
52 52 .card-body
53 53 %h2.card-title Model submission
54 54 %table.table.table-hover
55 55 %thead
56 56 %tr
57 - %th #Sub
57 + %th #Sub (lang)
58 58 %th Author
59 59 %tbody
60 60 - @model_subs.each do |sub|
61 61 %tr
62 - %td= link_to "##{sub.id}", submission_path(sub)
62 + %td
63 + = link_to "##{sub.id}", submission_path(sub)
64 + = "(#{sub.language.pretty_name})"
63 65 %td= sub.user.full_name
64 66 .col-md-8
65 67 - if @best
66 68 .card
67 69 .card-body
68 70 %h2.card-title Top Submissions
69 71 %table.table.table-hover
70 72 %thead
71 73 %tr
72 74 %th Language
73 75 %th Best runtime (ms)
74 76 %th Best memory (kbytes)
75 77 %th Shortest Code (bytes)
76 78 %th First solver
77 79 %tbody
78 80 %tr.bg-warning
79 81 %td
80 82 Overall
81 83 %td
82 84 by #{link_to @best[:runtime][:user], stat_user_path(@best[:runtime][:user_id])}
83 85 %br
84 86 using <span class="text-success">#{@best[:runtime][:lang]}</span>
85 87 %br
86 88 with <span class="text-success">#{@best[:runtime][:value] * 1000} milliseconds</span>
87 89 %br= link_to "#" + @best[:runtime][:sub_id].to_s, submission_path(@best[:runtime][:sub_id])
88 90 %td
89 91 by #{link_to @best[:memory][:user], stat_user_path(@best[:memory][:user_id])}
90 92 %br
91 93 using <span class="text-success">#{@best[:memory][:lang]}</span>
92 94 %br
93 95 with <span class="text-success">#{number_with_delimiter(@best[:memory][:value])} kbytes </span>
94 96 %br= link_to "#" + @best[:memory][:sub_id].to_s, submission_path(@best[:memory][:sub_id])
95 97 %td
96 98 by #{link_to @best[:length][:user], stat_user_path(@best[:length][:user_id])}
97 99 %br
98 100 using <span class="text-success">#{@best[:length][:lang]}</span>
99 101 %br
100 102 with <span class="text-success">#{@best[:length][:value]} bytes</span>
101 103 %br= link_to "#" + @best[:length][:sub_id].to_s, submission_path(@best[:length][:sub_id])
102 104 %td
103 105 - if @best[:first][:user] != '(NULL)'
104 106 #{link_to @best[:first][:user], stat_user_path(@best[:first][:user_id])} is the first solver
105 107 %br
106 108 using <span class="text-success">#{@best[:first][:lang]}</span>
107 109 %br
108 110 on <span class="text-success">#{@best[:first][:value]}</span>
109 111 %br= link_to "#" + @best[:first][:sub_id].to_s, submission_path( @best[:first][:sub_id])
110 112 - else
111 113 no first solver
112 114 - @by_lang.each do |lang,value|
113 115 %tr
114 116 %td= lang
115 117 %td
116 118 = link_to value[:runtime][:user], stat_user_path(value[:runtime][:user_id])
117 119 %br
118 120 = "#{(value[:runtime][:value] * 1000).to_i} @"
119 121 = link_to "#" + value[:runtime][:sub_id].to_s, submission_path( value[:runtime][:sub_id])
120 122 %td
121 123 = link_to value[:memory][:user], stat_user_path( value[:memory][:user_id])
122 124 %br
123 125 = "#{number_with_delimiter(value[:memory][:value])} @"
124 126 = link_to "#" + value[:memory][:sub_id].to_s, submission_path(value[:memory][:sub_id])
125 127 %td
126 128 = link_to value[:length][:user], stat_user_path(value[:length][:user_id])
127 129 %br
128 130 = "#{value[:length][:value]} @"
129 131 = link_to "#" + value[:length][:sub_id].to_s, submission_path(value[:length][:sub_id])
130 132 %td
131 133 - if value[:first][:user] != '(NULL)' #TODO: i know... this is wrong...
132 134 = link_to value[:first][:user], stat_user_path(value[:first][:user_id])
133 135 %br
134 136 = "#{value[:first][:value]} @"
135 137 = link_to "#" + value[:first][:sub_id].to_s, submission_path( value[:first][:sub_id])
136 138
137 139 %script{src:"https://cdn.jsdelivr.net/npm/chart.js"}
138 140 :javascript
139 141 data = #{@chart_dataset}
140 142 config = {
141 143 type: 'bar',
142 144 data: data,
143 145 options: {
144 146 plugins: {
145 147 legend: {
146 148 display: false
147 149 },
148 150 },
149 151 }
150 152 }
151 153 Chart.defaults.font.size = 15
152 154 //Chart.defaults.font.family = 'Sarabun Light'
153 155 chart = new Chart($('#chart'),config)
154 156
@@ -1,29 +1,30
1 1
2 2 /- if params[:id]
3 3 / %h1 Tasks Hall of Fame
4 4 / = link_to('[back to All-Time Hall of Fame]', action: 'problem_hof', id: nil )
5 5 /- else
6 6 / %h1 All-Time Hall of Fame
7 7
8 8 .panel.panel-info
9 9 .panel-heading
10 10 Select Task
11 11 .panel-body
12 12 .form-inline
13 13 = select 'report',
14 14 'problem_id',
15 15 @problems.collect {|p| ["[#{p.name}] #{p.full_name}", problem_hof_report_path(p)]},
16 16 {:selected => problem_hof_report_path(@problem)},
17 17 { class: 'select2 form-control' }
18 18 %button.btn.btn-primary.btn-sm.go-button#problem_go{data: {source: "#report_problem_id"}} Go
19 19
20 20
21 21 - unless params[:id]
22 22 /=render partial: 'all_time_hof'
23 23 Please select a problem.
24 24 - else
25 - %h1 [#{Problem.find(params[:id]).name}] #{Problem.find(params[:id]).full_name}
26 - -# %h2 Submission History
27 - -# =render partial: 'application/bar_graph', locals: { histogram: @histogram }
25 + %h1
26 + [#{Problem.find(params[:id]).name}] #{Problem.find(params[:id]).full_name}
27 + - if @current_user.has_role?('ta')
28 + %a{href:stat_problem_path(@problem)} (stat)
28 29 =render partial: 'task_hof'
29 30
@@ -1,128 +1,128
1 1 %h1= "Submission: #{@submission.id}"
2 2
3 3 %textarea#data{style: "display:none;"}
4 4 :preserve
5 5 #{@submission.source}
6 6
7 7 //%div.highlight{:style => "border: 1px solid black;"}
8 8 //=@formatted_code.html_safe
9 9
10 10
11 11 .containter
12 12 .row
13 13 .col-md-7
14 14 %h2 Source Code
15 15 .col-md-5
16 16 %h2 Stat
17 17 .row
18 18 .col-md-7
19 19 %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" }
20 20 :javascript
21 21 e = ace.edit("editor")
22 22 e.setOptions({ maxLines: Infinity })
23 23 e.setValue($("#data").text())
24 24 e.gotoLine(1)
25 25 e.getSession().setMode("#{get_ace_mode(@submission.language)}")
26 26 e.setReadOnly(true)
27 27 .col-md-5
28 28 %table.table.table-striped
29 29 %tr
30 30 %td.text-right
31 31 %strong User
32 32 %td
33 33 - if @current_user.admin? ||@current_user == @submission.user
34 34 - if @submission.user
35 35 = link_to "#{@submission.user.login}", stat_user_path(@submission.user)
36 36 = @submission.user.full_name
37 37 - else
38 38 = "(n/a)"
39 39 - else
40 40 = '-- REDACTED --'
41 41 %tr
42 42 %td.text-right
43 43 %strong Task
44 44 %td
45 45 - if @submission.problem!=nil
46 46 = link_to "[#{@submission.problem.name}]", stat_problem_path(@submission.problem)
47 47 = @submission.problem.full_name
48 48 = link_to_description_if_any "[download] <span class='glyphicon glyphicon-file'></span>".html_safe, @submission.problem
49 49 - else
50 50 = "(n/a)"
51 51 %tr
52 52 %td.text-right
53 53 %strong Tries
54 54 %td= @submission.number
55 55 %tr
56 56 %td.text-right
57 57 %strong Language
58 58 %td= @submission.language.pretty_name
59 59 %tr
60 60 %td.text-right
61 61 %strong Submitted
62 62 %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
63 63 %tr
64 64 %td.text-right
65 65 %strong Graded
66 66 - if @submission.graded_at
67 67 %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
68 68 - else
69 69 %td -
70 70 %tr
71 71 %td.text-right
72 72 %strong Points
73 73 %td #{@submission.points}/#{@submission.try(:problem).try(:full_score)}
74 74 %tr
75 75 %td.text-right
76 76 %strong Comment
77 77 %td #{@submission.grader_comment}
78 78 %tr
79 79 %td.text-right
80 80 %strong Runtime (s)
81 81 %td #{@submission.max_runtime}
82 82 %tr
83 83 %td.text-right
84 84 %strong Memory (kb)
85 85 %td #{@submission.peak_memory}
86 86 %tr
87 87 %td.text-right
88 88 %strong Compiler result
89 89 %td
90 90 %button.btn.btn-info.btn-xs{type: 'button', data: {toggle: 'modal', target: '#compiler'}}
91 91 view
92 92 %tr
93 93 %td.text-right
94 94 %strong Grading Task Status
95 95 %td
96 96 = @task.status_str if @task
97 - - if session[:admin]
97 + - if @current_user.admin?
98 98 = link_to "rejudge", rejudge_submission_path, data: {remote: true}, class: 'btn btn-info btn-xs'
99 - - if session[:admin]
99 + - if @current_user.has_role?('ta')
100 100 %tr
101 101 %td.text-right
102 102 %strong IP
103 103 %td #{@submission.ip_address}
104 104 %tr
105 105 %td.text-right
106 106 %strong Model solution
107 107 %td
108 108 - if @submission.tag_model?
109 109 YES
110 - - if session[:admin]
110 + - if @current_user.has_role?('ta')
111 111 = link_to "remove model status", set_tag_submission_path(@submission, tag: :default), class: 'btn btn-warning btn-xs'
112 112 - else
113 113 No
114 - - if session[:admin]
114 + - if @current_user.has_role?('ta')
115 115 = link_to "set as model solution", set_tag_submission_path(@submission, tag: :model), class: 'btn btn-success btn-xs'
116 116
117 117
118 118 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
119 119 .modal-dialog.modal-lg{role:'document'}
120 120 .modal-content
121 121 .modal-header
122 122 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
123 123 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
124 124 %h4 Compiler message
125 125 .modal-body
126 126 %pre#compiler_msg= @submission.compiler_message
127 127 .modal-footer
128 128 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
You need to be logged in to leave comments. Login now