Description:
merge with java
Commit status:
[Not Reviewed]
References:
merge algo
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r416:3378c5a77746 - - 24 files changed: 292 inserted, 84 deleted

@@ -0,0 +1,6
1 + //= require jquery
2 + //= require jquery_ujs
3 + //= require jquery.ui.all
4 + //= require jquery.ui.datepicker
5 + //= require jquery.ui.slider
6 + //= require jquery-ui-timepicker-addon
@@ -0,0 +1,3
1 + # Place all the behaviors and hooks related to the matching controller here.
2 + # All this logic will automatically be available in application.js.
3 + # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
@@ -0,0 +1,3
1 + // Place all the styles related to the report controller here.
2 + // They will automatically be included in application.css.
3 + // You can use Sass (SCSS) here: http://sass-lang.com/
@@ -0,0 +1,2
1 + module ReportHelper
2 + end
@@ -0,0 +1,3
1 + class Login < ActiveRecord::Base
2 + attr_accessible :ip_address, :logged_in_at, :user_id
3 + end
@@ -0,0 +1,23
1 +
2 + = form_tag({session: :url }) do
3 + .submitbox
4 + %table
5 + %tr
6 + %td{colspan: 6, style: 'font-weight: bold'}= title
7 + %tr
8 + %td{style: 'width: 120px; font-weight: bold'}= param_text
9 + %td{align: 'right'} since:
10 + %td= text_field_tag 'since_datetime'
11 + %tr
12 + %td
13 + %td{align: 'right'} until:
14 + %td= text_field_tag 'until_datetime'
15 + %tr
16 + %td
17 + %td
18 + %td Blank mean no condition
19 + %tr
20 + %td
21 + %td
22 + %td= submit_tag 'query'
23 +
@@ -0,0 +1,33
1 + - content_for :header do
2 + = javascript_include_tag 'new'
3 +
4 + %script{:type=>"text/javascript"}
5 + $(function () {
6 + $('#since_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
7 + $('#until_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
8 + });
9 +
10 +
11 + %h1 Login status
12 +
13 +
14 + =render partial: 'report_menu'
15 + =render partial: 'date_range', locals: {param_text: 'Login date range:', title: 'Query login stat in the range' }
16 +
17 + %table.info
18 + %thead
19 + %tr.info-head
20 + %th login
21 + %th full name
22 + %th login count
23 + %th earliest
24 + %th latest
25 + %tbody
26 + - @logins.each do |l|
27 + %tr{class: cycle('info-even','info-odd')}
28 + %td= l[:login]
29 + %td= l[:full_name]
30 + %td= l[:count]
31 + %td= l[:min] ? l[:min].in_time_zone.strftime('%Y-%m-%d %H:%M') : ''
32 + %td= l[:max] ? l[:max].in_time_zone.strftime('%Y-%m-%d %H:%M') : ''
33 +
@@ -0,0 +1,41
1 + - content_for :header do
2 + = javascript_include_tag 'new'
3 +
4 + %script{:type=>"text/javascript"}
5 + $(function () {
6 + $('#since_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
7 + $('#until_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
8 + });
9 +
10 +
11 + %h1 Login status
12 +
13 +
14 +
15 +
16 + =render partial: 'report_menu'
17 + =render partial: 'date_range', locals: {param_text: 'Submission date range:', title: 'Query submission stat in the range' }
18 +
19 + %table.info
20 + %thead
21 + %tr.info-head
22 + %th login
23 + %th full name
24 + %th total submissions
25 + %th submissions
26 + %tbody
27 + - @submissions.each do |user_id,data|
28 + %tr{class: cycle('info-even','info-odd')}
29 + %td= data[:login]
30 + %td= data[:full_name]
31 + %td= data[:count]
32 + %td
33 + - data[:sub].each do |prob_id,sub_data|
34 + = "#{sub_data[:prob_name]}: ["
35 + - st = []
36 + - sub_data[:sub_ids].each do |id|
37 + - st << link_to(id, controller: 'graders' , action: 'submission', id: id)
38 + = raw st.join ', '
39 + = ']'
40 + %br/
41 +
@@ -0,0 +1,10
1 + class CreateLogins < ActiveRecord::Migration
2 + def change
3 + create_table :logins do |t|
4 + t.string :user_id
5 + t.string :ip_address
6 +
7 + t.timestamps
8 + end
9 + end
10 + end
@@ -0,0 +1,5
1 + require 'spec_helper'
2 +
3 + describe Login do
4 + pending "add some examples to (or delete) #{__FILE__}"
5 + end
@@ -1,50 +1,56
1 1 source 'https://rubygems.org'
2 2
3 - gem 'rails', '3.2.8'
3 + gem 'rails', '3.2.19'
4 4
5 5 # Bundle edge Rails instead:
6 6 # gem 'rails', :git => 'git://github.com/rails/rails.git'
7 7
8 8 gem 'mysql2'
9 9
10 10 # Gems used only for assets and not required
11 11 # in production environments by default.
12 12 group :assets do
13 13 gem 'sass-rails', '~> 3.2.3'
14 14 gem 'coffee-rails', '~> 3.2.1'
15 15
16 16 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
17 17 # gem 'therubyracer', :platforms => :ruby
18 18
19 19 gem 'uglifier', '>= 1.0.3'
20 20 end
21 21
22 22 gem 'prototype-rails'
23 23
24 24 # To use ActiveModel has_secure_password
25 25 # gem 'bcrypt-ruby', '~> 3.0.0'
26 26
27 27 # To use Jbuilder templates for JSON
28 28 # gem 'jbuilder'
29 29
30 30 # Use unicorn as the app server
31 31 # gem 'unicorn'
32 32
33 33 # Deploy with Capistrano
34 34 # gem 'capistrano'
35 35
36 36 # To use debugger
37 37 # gem 'debugger'
38 + #
39 +
40 + gem 'jquery-rails'
41 + gem 'jquery-ui-sass-rails'
42 + gem 'jquery-timepicker-addon-rails'
43 +
38 44
39 45 gem "haml"
40 46 gem "mail"
41 47 gem "rdiscount"
42 48 gem "test-unit"
43 49 gem 'will_paginate', '~> 3.0.0'
44 50 gem 'dynamic_form'
45 51 gem 'in_place_editing'
46 52 gem 'verification', :git => 'git://github.com/sikachu/verification.git'
47 53
48 54 group :test, :development do
49 55 gem "rspec-rails", "~> 2.0"
50 56 end
@@ -1,147 +1,166
1 1 GIT
2 2 remote: git://github.com/sikachu/verification.git
3 - revision: 344ad2535da3dc9671872628ff9c79d6f59af9da
3 + revision: 76eaf51b13276ecae54bd9cd115832595d2ff56d
4 4 specs:
5 5 verification (1.0.3)
6 - actionpack (>= 3.0.0, < 3.3.0)
7 - activesupport (>= 3.0.0, < 3.3.0)
6 + actionpack (>= 3.0.0, < 5.0)
7 + activesupport (>= 3.0.0, < 5.0)
8 8
9 9 GEM
10 10 remote: https://rubygems.org/
11 11 specs:
12 - actionmailer (3.2.8)
13 - actionpack (= 3.2.8)
14 - mail (~> 2.4.4)
15 - actionpack (3.2.8)
16 - activemodel (= 3.2.8)
17 - activesupport (= 3.2.8)
12 + actionmailer (3.2.19)
13 + actionpack (= 3.2.19)
14 + mail (~> 2.5.4)
15 + actionpack (3.2.19)
16 + activemodel (= 3.2.19)
17 + activesupport (= 3.2.19)
18 18 builder (~> 3.0.0)
19 19 erubis (~> 2.7.0)
20 20 journey (~> 1.0.4)
21 - rack (~> 1.4.0)
21 + rack (~> 1.4.5)
22 22 rack-cache (~> 1.2)
23 23 rack-test (~> 0.6.1)
24 - sprockets (~> 2.1.3)
25 - activemodel (3.2.8)
26 - activesupport (= 3.2.8)
24 + sprockets (~> 2.2.1)
25 + activemodel (3.2.19)
26 + activesupport (= 3.2.19)
27 27 builder (~> 3.0.0)
28 - activerecord (3.2.8)
29 - activemodel (= 3.2.8)
30 - activesupport (= 3.2.8)
28 + activerecord (3.2.19)
29 + activemodel (= 3.2.19)
30 + activesupport (= 3.2.19)
31 31 arel (~> 3.0.2)
32 32 tzinfo (~> 0.3.29)
33 - activeresource (3.2.8)
34 - activemodel (= 3.2.8)
35 - activesupport (= 3.2.8)
36 - activesupport (3.2.8)
37 - i18n (~> 0.6)
33 + activeresource (3.2.19)
34 + activemodel (= 3.2.19)
35 + activesupport (= 3.2.19)
36 + activesupport (3.2.19)
37 + i18n (~> 0.6, >= 0.6.4)
38 38 multi_json (~> 1.0)
39 - arel (3.0.2)
40 - builder (3.0.3)
39 + arel (3.0.3)
40 + builder (3.0.4)
41 41 coffee-rails (3.2.2)
42 42 coffee-script (>= 2.2.0)
43 43 railties (~> 3.2.0)
44 - coffee-script (2.2.0)
44 + coffee-script (2.3.0)
45 45 coffee-script-source
46 46 execjs
47 - coffee-script-source (1.3.3)
48 - diff-lcs (1.1.3)
47 + coffee-script-source (1.7.1)
48 + diff-lcs (1.2.5)
49 49 dynamic_form (1.1.4)
50 50 erubis (2.7.0)
51 - execjs (1.4.0)
52 - multi_json (~> 1.0)
53 - haml (3.1.7)
54 - hike (1.2.1)
55 - i18n (0.6.1)
51 + execjs (2.2.1)
52 + haml (4.0.5)
53 + tilt
54 + hike (1.2.3)
55 + i18n (0.6.11)
56 56 in_place_editing (1.2.0)
57 57 journey (1.0.4)
58 - json (1.7.5)
59 - mail (2.4.4)
60 - i18n (>= 0.4.0)
58 + jquery-rails (3.1.1)
59 + railties (>= 3.0, < 5.0)
60 + thor (>= 0.14, < 2.0)
61 + jquery-timepicker-addon-rails (1.4.1)
62 + railties (>= 3.1)
63 + jquery-ui-rails (4.0.3)
64 + jquery-rails
65 + railties (>= 3.1.0)
66 + jquery-ui-sass-rails (4.0.3.0)
67 + jquery-rails
68 + jquery-ui-rails (= 4.0.3)
69 + railties (>= 3.1.0)
70 + json (1.8.1)
71 + mail (2.5.4)
61 72 mime-types (~> 1.16)
62 73 treetop (~> 1.4.8)
63 - mime-types (1.19)
64 - multi_json (1.3.6)
65 - mysql2 (0.3.11)
66 - polyglot (0.3.3)
74 + mime-types (1.25.1)
75 + multi_json (1.10.1)
76 + mysql2 (0.3.16)
77 + polyglot (0.3.5)
78 + power_assert (0.1.3)
67 79 prototype-rails (3.2.1)
68 80 rails (~> 3.2)
69 - rack (1.4.1)
81 + rack (1.4.5)
70 82 rack-cache (1.2)
71 83 rack (>= 0.4)
72 - rack-ssl (1.3.2)
84 + rack-ssl (1.3.4)
73 85 rack
74 86 rack-test (0.6.2)
75 87 rack (>= 1.0)
76 - rails (3.2.8)
77 - actionmailer (= 3.2.8)
78 - actionpack (= 3.2.8)
79 - activerecord (= 3.2.8)
80 - activeresource (= 3.2.8)
81 - activesupport (= 3.2.8)
88 + rails (3.2.19)
89 + actionmailer (= 3.2.19)
90 + actionpack (= 3.2.19)
91 + activerecord (= 3.2.19)
92 + activeresource (= 3.2.19)
93 + activesupport (= 3.2.19)
82 94 bundler (~> 1.0)
83 - railties (= 3.2.8)
84 - railties (3.2.8)
85 - actionpack (= 3.2.8)
86 - activesupport (= 3.2.8)
95 + railties (= 3.2.19)
96 + railties (3.2.19)
97 + actionpack (= 3.2.19)
98 + activesupport (= 3.2.19)
87 99 rack-ssl (~> 1.3.2)
88 100 rake (>= 0.8.7)
89 101 rdoc (~> 3.4)
90 102 thor (>= 0.14.6, < 2.0)
91 - rake (0.9.2.2)
92 - rdiscount (1.6.8)
93 - rdoc (3.12)
103 + rake (10.3.2)
104 + rdiscount (2.1.7.1)
105 + rdoc (3.12.2)
94 106 json (~> 1.4)
95 - rspec (2.11.0)
96 - rspec-core (~> 2.11.0)
97 - rspec-expectations (~> 2.11.0)
98 - rspec-mocks (~> 2.11.0)
99 - rspec-core (2.11.1)
100 - rspec-expectations (2.11.3)
101 - diff-lcs (~> 1.1.3)
102 - rspec-mocks (2.11.3)
103 - rspec-rails (2.11.0)
107 + rspec-collection_matchers (1.0.0)
108 + rspec-expectations (>= 2.99.0.beta1)
109 + rspec-core (2.99.2)
110 + rspec-expectations (2.99.2)
111 + diff-lcs (>= 1.1.3, < 2.0)
112 + rspec-mocks (2.99.2)
113 + rspec-rails (2.99.0)
104 114 actionpack (>= 3.0)
115 + activemodel (>= 3.0)
105 116 activesupport (>= 3.0)
106 117 railties (>= 3.0)
107 - rspec (~> 2.11.0)
108 - sass (3.2.1)
109 - sass-rails (3.2.5)
118 + rspec-collection_matchers
119 + rspec-core (~> 2.99.0)
120 + rspec-expectations (~> 2.99.0)
121 + rspec-mocks (~> 2.99.0)
122 + sass (3.4.1)
123 + sass-rails (3.2.6)
110 124 railties (~> 3.2.0)
111 125 sass (>= 3.1.10)
112 126 tilt (~> 1.3)
113 - sprockets (2.1.3)
127 + sprockets (2.2.2)
114 128 hike (~> 1.2)
129 + multi_json (~> 1.0)
115 130 rack (~> 1.0)
116 131 tilt (~> 1.1, != 1.3.0)
117 - test-unit (2.5.2)
118 - thor (0.16.0)
119 - tilt (1.3.3)
120 - treetop (1.4.10)
132 + test-unit (3.0.1)
133 + power_assert
134 + thor (0.19.1)
135 + tilt (1.4.1)
136 + treetop (1.4.15)
121 137 polyglot
122 138 polyglot (>= 0.3.1)
123 - tzinfo (0.3.33)
124 - uglifier (1.3.0)
139 + tzinfo (0.3.41)
140 + uglifier (2.5.3)
125 141 execjs (>= 0.3.0)
126 - multi_json (~> 1.0, >= 1.0.2)
127 - will_paginate (3.0.3)
142 + json (>= 1.8.0)
143 + will_paginate (3.0.7)
128 144
129 145 PLATFORMS
130 146 ruby
131 147
132 148 DEPENDENCIES
133 149 coffee-rails (~> 3.2.1)
134 150 dynamic_form
135 151 haml
136 152 in_place_editing
153 + jquery-rails
154 + jquery-timepicker-addon-rails
155 + jquery-ui-sass-rails
137 156 mail
138 157 mysql2
139 158 prototype-rails
140 - rails (= 3.2.8)
159 + rails (= 3.2.19)
141 160 rdiscount
142 161 rspec-rails (~> 2.0)
143 162 sass-rails (~> 3.2.3)
144 163 test-unit
145 164 uglifier (>= 1.0.3)
146 165 verification!
147 166 will_paginate (~> 3.0.0)
@@ -1,96 +1,104
1 +
2 + @import jquery.ui.core
3 + @import jquery.ui.theme
4 + @import jquery.ui.datepicker
5 + @import jquery.ui.slider
6 + @import jquery-ui-timepicker-addon
7 +
8 +
1 9 body
2 10 background: white image-url("topbg.jpg") repeat-x top center
3 11 font-size: 13px
4 12 font-family: Tahoma, "sans-serif"
5 13 margin: 10px
6 14 padding: 10px
7 15
8 16
9 17 input
10 18 font-family: Tahoma, "sans-serif"
11 19
12 20
13 21 h1
14 22 font-size: 24px
15 23 color: #334488
16 24 line-height: 2em
17 25
18 26
19 27 h2
20 28 font-size: 18px
21 29 color: #5566bb
22 30 line-height: 1.5em
23 31
24 32
25 33 hr
26 34 border-top: 1px solid #dddddd
27 35 border-bottom: 1px solid #eeeeee
28 36
29 37
30 38 a
31 39 color: #6666cc
32 40 text-decoration: none
33 41
34 42 &:link, &:visited
35 43 color: #6666cc
36 44 text-decoration: none
37 45
38 46 &:hover, &:focus
39 47 color: #111166
40 48 text-decoration: none
41 49
42 50
43 51 div
44 52 &.userbar
45 53 line-height: 1.5em
46 54 text-align: right
47 55 font-size: 12px
48 56
49 57 &.title
50 58 padding: 10px 0px
51 59 line-height: 1.5em
52 60 font-size: 13px
53 61
54 62 span.contest-over-msg
55 63 font-size: 15px
56 64 color: red
57 65
58 66 table
59 67 width: 100%
60 68 font-weight: bold
61 69
62 70 td
63 71 &.left-col
64 72 text-align: left
65 73 vertical-align: top
66 74 color: #444444
67 75
68 76 &.right-col
69 77 text-align: right
70 78 vertical-align: top
71 79 font-size: 18px
72 80 color: #116699
73 81
74 82
75 83 table.info
76 84 margin: 10px 0
77 85 border: 1px solid #666666
78 86 border-collapse: collapse
79 87 font-size: 12px
80 88
81 89 th
82 90 border: 1px solid #666666
83 91 line-height: 1.5em
84 92 padding: 0 0.5em
85 93
86 94 td
87 95 border-left: 1px solid #666666
88 96 border-right: 1px solid #666666
89 97 line-height: 1.5em
90 98 padding: 0 0.5em
91 99
92 100
93 101 tr
94 102 &.info-head
95 103 background: #777777
96 104 color: white
@@ -197,97 +205,97
197 205 .announcementbox
198 206 margin: 10px 0px
199 207 background: #bbddee
200 208 padding: 1px
201 209
202 210 span.title
203 211 font-weight: bold
204 212 color: #224455
205 213 padding-left: 10px
206 214 line-height: 1.6em
207 215
208 216 .announcement
209 217 margin: 2px
210 218 background: white
211 219 padding: 1px
212 220 padding-left: 10px
213 221 padding-right: 10px
214 222 padding-top: 5px
215 223 padding-bottom: 5px
216 224
217 225
218 226 .announcement p
219 227 font-size: 12px
220 228 margin: 2px
221 229
222 230
223 231 .pub-info
224 232 text-align: right
225 233 font-style: italic
226 234 font-size: 9px
227 235
228 236 p
229 237 text-align: right
230 238 font-style: italic
231 239 font-size: 9px
232 240
233 241
234 242 .announcement
235 243 .toggles
236 244 font-weight: normal
237 245 float: right
238 246 font-size: 80%
239 247
240 248 .announcement-title
241 249 font-weight: bold
242 250
243 251
244 252 div
245 253 &.message
246 254 margin: 10px 0 0
247 255
248 256 div
249 257 &.message
250 258 margin: 0 0 0 30px
251 259
252 260 &.body
253 261 border: 2px solid #dddddd
254 262 background: #fff8f8
255 263 padding-left: 5px
256 264
257 265 &.reply-body
258 266 border: 2px solid #bbbbbb
259 267 background: #fffff8
260 268 padding-left: 5px
261 269
262 270 &.stat
263 271 font-size: 10px
264 272 line-height: 1.75em
265 273 padding: 0 5px
266 274 color: #333333
267 275 background: #dddddd
268 276 font-weight: bold
269 277
270 278 &.message div.stat
271 279 font-size: 10px
272 280 line-height: 1.75em
273 281 padding: 0 5px
274 282 color: #444444
275 283 background: #bbbbbb
276 284 font-weight: bold
277 285
278 286 &.contest-title
279 287 color: white
280 288 text-align: center
281 289 line-height: 2em
282 290
283 291 &.registration-desc, &.test-desc
284 292 border: 1px dotted gray
285 293 background: #f5f5f5
286 294 padding: 5px
287 295 margin: 10px 0
288 296 font-size: 12px
289 297 line-height: 1.5em
290 298
291 299 h2.contest-title
292 300 margin-top: 5px
293 - margin-bottom: 5px No newline at end of file
301 + margin-bottom: 5px
@@ -1,51 +1,54
1 1 class LoginController < ApplicationController
2 2
3 3 def index
4 4 # show login screen
5 5 reset_session
6 6 redirect_to :controller => 'main', :action => 'login'
7 7 end
8 8
9 9 def login
10 10 if user = User.authenticate(params[:login], params[:password])
11 11 session[:user_id] = user.id
12 12 session[:admin] = user.admin?
13 13
14 14 # clear forced logout flag for multicontests contest change
15 15 if GraderConfiguration.multicontests?
16 16 contest_stat = user.contest_stat
17 17 if contest_stat.respond_to? :forced_logout
18 18 if contest_stat.forced_logout
19 19 contest_stat.forced_logout = false
20 20 contest_stat.save
21 21 end
22 22 end
23 23 end
24 24
25 + #save login information
26 + Login.create(user_id: user.id, ip_address: request.remote_ip)
27 +
25 28 redirect_to :controller => 'main', :action => 'list'
26 29 else
27 30 flash[:notice] = 'Wrong password'
28 31 redirect_to :controller => 'main', :action => 'login'
29 32 end
30 33 end
31 34
32 35 def site_login
33 36 begin
34 37 site = Site.find(params[:login][:site_id])
35 38 rescue ActiveRecord::RecordNotFound
36 39 site = nil
37 40 end
38 41 if site==nil
39 42 flash[:notice] = 'Wrong site'
40 43 redirect_to :controller => 'main', :action => 'login' and return
41 44 end
42 45 if (site.password) and (site.password == params[:login][:password])
43 46 session[:site_id] = site.id
44 47 redirect_to :controller => 'site', :action => 'index'
45 48 else
46 49 flash[:notice] = 'Wrong site password'
47 50 redirect_to :controller => 'site', :action => 'login'
48 51 end
49 52 end
50 53
51 54 end
@@ -1,111 +1,112
1 1 # Methods added to this helper will be available to all templates in the application.
2 2 module ApplicationHelper
3 3
4 4 def user_header
5 5 menu_items = ''
6 6 user = User.find(session[:user_id])
7 7
8 8 if (user!=nil) and (session[:admin])
9 9 # admin menu
10 10 menu_items << "<b>Administrative task:</b> "
11 11 append_to menu_items, '[Announcements]', 'announcements', 'index'
12 12 append_to menu_items, '[Msg console]', 'messages', 'console'
13 13 append_to menu_items, '[Problems]', 'problems', 'index'
14 14 append_to menu_items, '[Users]', 'user_admin', 'index'
15 15 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
16 + append_to menu_items, '[Report]', 'report', 'login_stat'
16 17 append_to menu_items, '[Graders]', 'graders', 'list'
17 18 append_to menu_items, '[Contests]', 'contest_management', 'index'
18 19 append_to menu_items, '[Sites]', 'sites', 'index'
19 20 append_to menu_items, '[System config]', 'configurations', 'index'
20 21 menu_items << "<br/>"
21 22 end
22 23
23 24 # main page
24 25 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
25 26 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
26 27
27 28 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
28 29 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
29 30 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
30 31 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
31 32 end
32 33 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
33 34
34 35 if GraderConfiguration['system.user_setting_enabled']
35 36 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
36 37 end
37 38 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
38 39
39 40 menu_items.html_safe
40 41 end
41 42
42 43 def append_to(option,label, controller, action)
43 44 option << ' ' if option!=''
44 45 option << link_to_unless_current(label,
45 46 :controller => controller,
46 47 :action => action)
47 48 end
48 49
49 50 def format_short_time(time)
50 51 now = Time.now.gmtime
51 52 st = ''
52 53 if (time.yday != now.yday) or
53 54 (time.year != now.year)
54 55 st = time.strftime("%x ")
55 56 end
56 57 st + time.strftime("%X")
57 58 end
58 59
59 60 def format_short_duration(duration)
60 61 return '' if duration==nil
61 62 d = duration.to_f
62 63 return Time.at(d).gmtime.strftime("%X")
63 64 end
64 65
65 66 def read_textfile(fname,max_size=2048)
66 67 begin
67 68 File.open(fname).read(max_size)
68 69 rescue
69 70 nil
70 71 end
71 72 end
72 73
73 74 def user_title_bar(user)
74 75 header = ''
75 76 time_left = ''
76 77
77 78 #
78 79 # if the contest is over
79 80 if GraderConfiguration.time_limit_mode?
80 81 if user.contest_finished?
81 82 header = <<CONTEST_OVER
82 83 <tr><td colspan="2" align="center">
83 84 <span class="contest-over-msg">THE CONTEST IS OVER</span>
84 85 </td></tr>
85 86 CONTEST_OVER
86 87 end
87 88 if !user.contest_started?
88 89 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
89 90 else
90 91 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
91 92 " #{format_short_duration(user.contest_time_left)}"
92 93 end
93 94 end
94 95
95 96 #
96 97 # if the contest is in the anaysis mode
97 98 if GraderConfiguration.analysis_mode?
98 99 header = <<ANALYSISMODE
99 100 <tr><td colspan="2" align="center">
100 101 <span class="contest-over-msg">ANALYSIS MODE</span>
101 102 </td></tr>
102 103 ANALYSISMODE
103 104 end
104 105
105 106 contest_name = GraderConfiguration['contest.name']
106 107
107 108 #
108 109 # build real title bar
109 110 result = <<TITLEBAR
110 111 <div class="title">
111 112 <table>
@@ -1,114 +1,121
1 1 class Problem < ActiveRecord::Base
2 2
3 3 belongs_to :description
4 4 has_and_belongs_to_many :contests, :uniq => true
5 5 has_many :test_pairs, :dependent => :delete_all
6 6
7 7 validates_presence_of :name
8 8 validates_format_of :name, :with => /^\w+$/
9 9 validates_presence_of :full_name
10 10
11 11 scope :available, :conditions => {:available => true}
12 12
13 13 DEFAULT_TIME_LIMIT = 1
14 14 DEFAULT_MEMORY_LIMIT = 32
15 15
16 16 def self.find_available_problems
17 - Problem.available.all(:order => "date_added DESC")
17 + Problem.available.all(:order => "date_added DESC, name ASC")
18 18 end
19 19
20 20 def self.create_from_import_form_params(params, old_problem=nil)
21 21 org_problem = old_problem || Problem.new
22 22 import_params, problem = Problem.extract_params_and_check(params,
23 23 org_problem)
24 24
25 25 if !problem.errors.empty?
26 26 return problem, 'Error importing'
27 27 end
28 28
29 29 problem.full_score = 100
30 30 problem.date_added = Time.new
31 31 problem.test_allowed = true
32 32 problem.output_only = false
33 33 problem.available = false
34 34
35 35 if not problem.save
36 36 return problem, 'Error importing'
37 37 end
38 38
39 39 import_to_db = params.has_key? :import_to_db
40 40
41 41 importer = TestdataImporter.new(problem)
42 42
43 43 if not importer.import_from_file(import_params[:file],
44 44 import_params[:time_limit],
45 45 import_params[:memory_limit],
46 + import_params[:checker_name],
46 47 import_to_db)
47 48 problem.errors.add_to_base('Import error.')
48 49 end
49 50
50 51 return problem, importer.log_msg
51 52 end
52 53
53 54 def self.download_file_basedir
54 55 return "#{Rails.root}/data/tasks"
55 56 end
56 57
57 58 protected
58 59
59 60 def self.to_i_or_default(st, default)
60 61 if st!=''
61 62 result = st.to_i
62 63 end
63 64 result ||= default
64 65 end
65 66
66 67 def self.to_f_or_default(st, default)
67 68 if st!=''
68 69 result = st.to_f
69 70 end
70 71 result ||= default
71 72 end
72 73
73 74 def self.extract_params_and_check(params, problem)
74 75 time_limit = Problem.to_f_or_default(params[:time_limit],
75 76 DEFAULT_TIME_LIMIT)
76 77 memory_limit = Problem.to_i_or_default(params[:memory_limit],
77 78 DEFAULT_MEMORY_LIMIT)
78 79
79 80 if time_limit<=0 or time_limit >60
80 81 problem.errors.add_to_base('Time limit out of range.')
81 82 end
82 83
83 84 if memory_limit==0 and params[:memory_limit]!='0'
84 85 problem.errors.add_to_base('Memory limit format errors.')
85 86 elsif memory_limit<=0 or memory_limit >512
86 87 problem.errors.add_to_base('Memory limit out of range.')
87 88 end
88 89
89 90 if params[:file]==nil or params[:file]==''
90 91 problem.errors.add_to_base('No testdata file.')
91 92 end
92 93
94 + checker_name = 'text'
95 + if ['text','float'].include? params[:checker]
96 + checker_name = params[:checker]
97 + end
98 +
93 99 file = params[:file]
94 100
95 101 if !problem.errors.empty?
96 102 return nil, problem
97 103 end
98 104
99 105 problem.name = params[:name]
100 106 if params[:full_name]!=''
101 107 problem.full_name = params[:full_name]
102 108 else
103 109 problem.full_name = params[:name]
104 110 end
105 111
106 112 return [{
107 113 :time_limit => time_limit,
108 114 :memory_limit => memory_limit,
109 - :file => file
115 + :file => file,
116 + :checker_name => checker_name
110 117 },
111 118 problem]
112 119 end
113 120
114 121 end
@@ -1,207 +1,216
1 1 require 'digest/sha1'
2 2 require 'net/pop'
3 + require 'net/https'
4 + require 'net/http'
3 5 require 'json'
4 6
5 7 class User < ActiveRecord::Base
6 8
7 9 has_and_belongs_to_many :roles
8 10
9 11 has_many :test_requests, :order => "submitted_at DESC"
10 12
11 13 has_many :messages,
12 14 :class_name => "Message",
13 15 :foreign_key => "sender_id",
14 16 :order => 'created_at DESC'
15 17
16 18 has_many :replied_messages,
17 19 :class_name => "Message",
18 20 :foreign_key => "receiver_id",
19 21 :order => 'created_at DESC'
20 22
21 23 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
22 24
23 25 belongs_to :site
24 26 belongs_to :country
25 27
26 28 has_and_belongs_to_many :contests, :uniq => true, :order => 'name'
27 29
28 30 scope :activated_users, :conditions => {:activated => true}
29 31
30 32 validates_presence_of :login
31 33 validates_uniqueness_of :login
32 34 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
33 35 validates_length_of :login, :within => 3..30
34 36
35 37 validates_presence_of :full_name
36 38 validates_length_of :full_name, :minimum => 1
37 39
38 40 validates_presence_of :password, :if => :password_required?
39 41 validates_length_of :password, :within => 4..20, :if => :password_required?
40 42 validates_confirmation_of :password, :if => :password_required?
41 43
42 44 validates_format_of :email,
43 45 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
44 46 :if => :email_validation?
45 47 validate :uniqueness_of_email_from_activated_users,
46 48 :if => :email_validation?
47 49 validate :enough_time_interval_between_same_email_registrations,
48 50 :if => :email_validation?
49 51
50 52 # these are for ytopc
51 53 # disable for now
52 54 #validates_presence_of :province
53 55
54 56 attr_accessor :password
55 57
56 58 before_save :encrypt_new_password
57 59 before_save :assign_default_site
58 60 before_save :assign_default_contest
59 61
60 62 # this is for will_paginate
61 63 cattr_reader :per_page
62 64 @@per_page = 50
63 65
64 66 def self.authenticate(login, password)
65 67 user = find_by_login(login)
66 68 if user
67 69 return user if user.authenticated?(password)
68 70 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
69 71 user.password = password
70 72 user.save
71 73 return user
72 74 end
73 75 end
74 76 end
75 77
76 78 def authenticated?(password)
77 79 if self.activated
78 80 hashed_password == User.encrypt(password,self.salt)
79 81 else
80 82 false
81 83 end
82 84 end
83 85
84 86 def authenticated_by_pop3?(password)
85 87 Net::POP3.enable_ssl
86 88 pop = Net::POP3.new('pops.it.chula.ac.th')
87 89 authen = true
88 90 begin
89 91 pop.start(login, password)
90 92 pop.finish
91 93 return true
92 94 rescue
93 95 return false
94 96 end
95 97 end
96 98
97 99 def authenticated_by_cucas?(password)
98 100 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
99 101 appid = '41508763e340d5858c00f8c1a0f5a2bb'
100 102 appsecret ='d9cbb5863091dbe186fded85722a1e31'
101 103 post_args = {
102 104 'appid' => appid,
103 105 'appsecret' => appsecret,
104 106 'username' => login,
105 107 'password' => password
106 108 }
107 109
108 110 #simple call
109 111 begin
110 - resp = Net::HTTP.post_form(url, post_args)
112 + http = Net::HTTP.new('www.cas.chula.ac.th', 443)
113 + http.use_ssl = true
114 + result = [ ]
115 + http.start do |http|
116 + req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
117 + param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
118 + resp = http.request(req,param)
111 119 result = JSON.parse resp.body
120 + end
112 121 return true if result["type"] == "beanStudent"
113 122 rescue
114 123 return false
115 124 end
116 125 return false
117 126 end
118 127
119 128 def admin?
120 129 self.roles.detect {|r| r.name == 'admin' }
121 130 end
122 131
123 132 def email_for_editing
124 133 if self.email==nil
125 134 "(unknown)"
126 135 elsif self.email==''
127 136 "(blank)"
128 137 else
129 138 self.email
130 139 end
131 140 end
132 141
133 142 def email_for_editing=(e)
134 143 self.email=e
135 144 end
136 145
137 146 def alias_for_editing
138 147 if self.alias==nil
139 148 "(unknown)"
140 149 elsif self.alias==''
141 150 "(blank)"
142 151 else
143 152 self.alias
144 153 end
145 154 end
146 155
147 156 def alias_for_editing=(e)
148 157 self.alias=e
149 158 end
150 159
151 160 def activation_key
152 161 if self.hashed_password==nil
153 162 encrypt_new_password
154 163 end
155 164 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
156 165 end
157 166
158 167 def verify_activation_key(key)
159 168 key == activation_key
160 169 end
161 170
162 171 def self.random_password(length=5)
163 172 chars = 'abcdefghjkmnopqrstuvwxyz'
164 173 password = ''
165 174 length.times { password << chars[rand(chars.length - 1)] }
166 175 password
167 176 end
168 177
169 178 def self.find_non_admin_with_prefix(prefix='')
170 179 users = User.find(:all)
171 180 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
172 181 end
173 182
174 183 # Contest information
175 184
176 185 def self.find_users_with_no_contest()
177 186 users = User.find(:all)
178 187 return users.find_all { |u| u.contests.length == 0 }
179 188 end
180 189
181 190
182 191 def contest_time_left
183 192 if GraderConfiguration.contest_mode?
184 193 return nil if site==nil
185 194 return site.time_left
186 195 elsif GraderConfiguration.indv_contest_mode?
187 196 time_limit = GraderConfiguration.contest_time_limit
188 197 if time_limit == nil
189 198 return nil
190 199 end
191 200 if contest_stat==nil or contest_stat.started_at==nil
192 201 return (Time.now.gmtime + time_limit) - Time.now.gmtime
193 202 else
194 203 finish_time = contest_stat.started_at + time_limit
195 204 current_time = Time.now.gmtime
196 205 if current_time > finish_time
197 206 return 0
198 207 else
199 208 return finish_time - current_time
200 209 end
201 210 end
202 211 else
203 212 return nil
204 213 end
205 214 end
206 215
207 216 def contest_finished?
@@ -1,21 +1,23
1 1 <!DOCTYPE html>
2 2 <html>
3 3 <head>
4 4 <title><%= GraderConfiguration['contest.name'] %></title>
5 5 <%= stylesheet_link_tag "application", :media => "all" %>
6 6 <%= javascript_include_tag "application" %>
7 7 <%= csrf_meta_tags %>
8 + <%= content_for :header %>
8 9 <%= yield :head %>
10 +
9 11 </head>
10 12 <body>
11 13
12 14 <div class="userbar">
13 15 <%= user_header %>
14 16 </div>
15 17
16 18 <%= content_tag(:p,flash[:notice],:style => "color:green") if flash[:notice]!=nil %>
17 19
18 20 <%= yield %>
19 21
20 22 </body>
21 23 </html>
@@ -1,18 +1,18
1 1 <tr class="info-<%= (problem_counter%2==0) ? "even" : "odd" %>">
2 2 <td>
3 3 <%= "#{problem_counter+1}" %>
4 4 </td>
5 5 <td>
6 - <%= "#{problem.full_name} (#{problem.name})" %>
6 + <%= "(#{problem.name}) #{problem.full_name}" %>
7 7 <%= link_to_description_if_any "[#{t 'main.problem_desc'}]", problem %>
8 8 </td>
9 9 <td align="center">
10 10 <%= @prob_submissions[problem.id][:count] %>
11 11 </td>
12 12 <td>
13 13 <%= render :partial => 'submission_short',
14 14 :locals => {
15 15 :submission => @prob_submissions[problem.id][:submission],
16 16 :problem_name => problem.name }%>
17 17 </td>
18 18 </tr>
@@ -1,58 +1,71
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]', :action => 'list'
7 7
8 8 - if @problem and @problem.errors
9 9 =error_messages_for 'problem'
10 10
11 11 = form_tag({:action => 'do_import'}, :multipart => true) do
12 12 .submitbox
13 13 %table
14 14 %tr
15 15 %td Name:
16 16 %td= text_field_tag 'name'
17 17 %tr
18 18 %td Full name:
19 19 %td
20 20 = text_field_tag 'full_name'
21 21 %span{:class => 'help'} Leave blank to use the same value as the name above.
22 22 %tr
23 23 %td Testdata file:
24 24 %td= file_field_tag 'file'
25 25 %tr
26 26 %td
27 27 %td
28 28 %span{:class => 'help'}
29 29 In .zip, .tgz, tar.gz, .tar format.
30 30 It should includes inputs (e.g., 1.in, 2a.in, 2b.in)
31 31 and solutions (e.g., 1.sol, 2a.sol, 2b.sol).
32 32 %br/
33 33 You may put task description in *.html for raw html
34 34 and *.md or *.markdown for markdown.
35 + %br/
36 + You may also put a pdf file for the task description
37 + %tr
38 + %td Checker:
39 + %td= select_tag 'checker', options_for_select([['Text checker','text'],['Float checker','float']], 'text')
40 + %tr
41 + %td
42 + %td
43 + %span{:class => 'help'}
44 + "Text" checker checks if the text (including numbers) is the same, ignoring any whitespace
45 + %br/
46 + "Float" checker checks if all numbers is within EPSILON error using formula |a-b| < EPSILON * max(|a|,|b|)
47 +
35 48 - if @allow_test_pair_import
36 49 %tr
37 50 %td
38 51 %td
39 52 = check_box_tag 'import_to_db'
40 53 Import test data to database (for a test-pair task)
41 54 %tr
42 55 %td Time limit:
43 56 %td
44 57 = text_field_tag 'time_limit'
45 58 %span{:class => 'help'} In seconds. Leave blank to use 1 sec.
46 59 %tr
47 60 %td Memory limit:
48 61 %td
49 62 = text_field_tag 'memory_limit'
50 63 %span{:class => 'help'} In MB. Leave blank to use 32MB.
51 64 %tr
52 65 %td
53 66 %td= submit_tag 'Import problem'
54 67
55 68 - if @log
56 69 %h3 Import log
57 70 %pre.import-log
58 71 = @log
@@ -1,68 +1,70
1 1 CafeGrader::Application.routes.draw do
2 + get "report/login"
3 +
2 4 resources :contests
3 5
4 6 resources :announcements
5 7 resources :sites
6 8
7 9 # The priority is based upon order of creation:
8 10 # first created -> highest priority.
9 11
10 12 # Sample of regular route:
11 13 # match 'products/:id' => 'catalog#view'
12 14 # Keep in mind you can assign values other than :controller and :action
13 15
14 16 # Sample of named route:
15 17 # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
16 18 # This route can be invoked with purchase_url(:id => product.id)
17 19
18 20 # Sample resource route (maps HTTP verbs to controller actions automatically):
19 21 # resources :products
20 22
21 23 # Sample resource route with options:
22 24 # resources :products do
23 25 # member do
24 26 # get 'short'
25 27 # post 'toggle'
26 28 # end
27 29 #
28 30 # collection do
29 31 # get 'sold'
30 32 # end
31 33 # end
32 34
33 35 # Sample resource route with sub-resources:
34 36 # resources :products do
35 37 # resources :comments, :sales
36 38 # resource :seller
37 39 # end
38 40
39 41 # Sample resource route with more complex sub-resources
40 42 # resources :products do
41 43 # resources :comments
42 44 # resources :sales do
43 45 # get 'recent', :on => :collection
44 46 # end
45 47 # end
46 48
47 49 # Sample resource route within a namespace:
48 50 # namespace :admin do
49 51 # # Directs /admin/products/* to Admin::ProductsController
50 52 # # (app/controllers/admin/products_controller.rb)
51 53 # resources :products
52 54 # end
53 55
54 56 # You can have the root of your site routed with "root"
55 57 # just remember to delete public/index.html.
56 58 # root :to => 'welcome#index'
57 59
58 60 root :to => 'main#login'
59 61
60 62 match 'tasks/view/:file.:ext' => 'tasks#view'
61 63 match 'tasks/download/:id/:file.:ext' => 'tasks#download'
62 64
63 65 # See how all your routes lay out with "rake routes"
64 66
65 67 # This is a legacy wild controller route that's not recommended for RESTful applications.
66 68 # Note: This route will make all actions in every controller accessible via GET requests.
67 69 match ':controller(/:action(/:id))(.:format)'
68 70 end
@@ -1,184 +1,191
1 1 # encoding: UTF-8
2 2 # This file is auto-generated from the current state of the database. Instead
3 3 # of editing this file, please use the migrations feature of Active Record to
4 4 # incrementally modify your database, and then regenerate this schema definition.
5 5 #
6 6 # Note that this schema.rb definition is the authoritative source for your
7 7 # database schema. If you need to create the application database on another
8 8 # system, you should be using db:schema:load, not running all the migrations
9 9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 10 # you'll amass, the slower it'll run and the greater likelihood for issues).
11 11 #
12 12 # It's strongly recommended to check this file into your version control system.
13 13
14 - ActiveRecord::Schema.define(:version => 20140823031747) do
14 + ActiveRecord::Schema.define(:version => 20140826095949) do
15 15
16 16 create_table "announcements", :force => true do |t|
17 17 t.string "author"
18 18 t.text "body"
19 19 t.boolean "published"
20 20 t.datetime "created_at", :null => false
21 21 t.datetime "updated_at", :null => false
22 22 t.boolean "frontpage", :default => false
23 23 t.boolean "contest_only", :default => false
24 24 t.string "title"
25 25 t.string "notes"
26 26 end
27 27
28 28 create_table "contests", :force => true do |t|
29 29 t.string "title"
30 30 t.boolean "enabled"
31 31 t.datetime "created_at", :null => false
32 32 t.datetime "updated_at", :null => false
33 33 t.string "name"
34 34 end
35 35
36 36 create_table "contests_problems", :id => false, :force => true do |t|
37 37 t.integer "contest_id"
38 38 t.integer "problem_id"
39 39 end
40 40
41 41 create_table "contests_users", :id => false, :force => true do |t|
42 42 t.integer "contest_id"
43 43 t.integer "user_id"
44 44 end
45 45
46 46 create_table "countries", :force => true do |t|
47 47 t.string "name"
48 48 t.datetime "created_at", :null => false
49 49 t.datetime "updated_at", :null => false
50 50 end
51 51
52 52 create_table "descriptions", :force => true do |t|
53 53 t.text "body"
54 54 t.boolean "markdowned"
55 55 t.datetime "created_at", :null => false
56 56 t.datetime "updated_at", :null => false
57 57 end
58 58
59 59 create_table "grader_configurations", :force => true do |t|
60 60 t.string "key"
61 61 t.string "value_type"
62 62 t.string "value"
63 63 t.datetime "created_at", :null => false
64 64 t.datetime "updated_at", :null => false
65 65 t.text "description"
66 66 end
67 67
68 68 create_table "grader_processes", :force => true do |t|
69 69 t.string "host", :limit => 20
70 70 t.integer "pid"
71 71 t.string "mode"
72 72 t.boolean "active"
73 73 t.datetime "created_at", :null => false
74 74 t.datetime "updated_at", :null => false
75 75 t.integer "task_id"
76 76 t.string "task_type"
77 77 t.boolean "terminated"
78 78 end
79 79
80 80 add_index "grader_processes", ["host", "pid"], :name => "index_grader_processes_on_ip_and_pid"
81 81
82 82 create_table "languages", :force => true do |t|
83 83 t.string "name", :limit => 10
84 84 t.string "pretty_name"
85 85 t.string "ext", :limit => 10
86 86 t.string "common_ext"
87 87 end
88 88
89 + create_table "logins", :force => true do |t|
90 + t.string "user_id"
91 + t.string "ip_address"
92 + t.datetime "created_at", :null => false
93 + t.datetime "updated_at", :null => false
94 + end
95 +
89 96 create_table "messages", :force => true do |t|
90 97 t.integer "sender_id"
91 98 t.integer "receiver_id"
92 99 t.integer "replying_message_id"
93 100 t.text "body"
94 101 t.boolean "replied"
95 102 t.datetime "created_at", :null => false
96 103 t.datetime "updated_at", :null => false
97 104 end
98 105
99 106 create_table "problems", :force => true do |t|
100 107 t.string "name", :limit => 30
101 108 t.string "full_name"
102 109 t.integer "full_score"
103 110 t.date "date_added"
104 111 t.boolean "available"
105 112 t.string "url"
106 113 t.integer "description_id"
107 114 t.boolean "test_allowed"
108 115 t.boolean "output_only"
109 116 t.string "description_filename"
110 117 end
111 118
112 119 create_table "rights", :force => true do |t|
113 120 t.string "name"
114 121 t.string "controller"
115 122 t.string "action"
116 123 end
117 124
118 125 create_table "rights_roles", :id => false, :force => true do |t|
119 126 t.integer "right_id"
120 127 t.integer "role_id"
121 128 end
122 129
123 130 add_index "rights_roles", ["role_id"], :name => "index_rights_roles_on_role_id"
124 131
125 132 create_table "roles", :force => true do |t|
126 133 t.string "name"
127 134 end
128 135
129 136 create_table "roles_users", :id => false, :force => true do |t|
130 137 t.integer "role_id"
131 138 t.integer "user_id"
132 139 end
133 140
134 141 add_index "roles_users", ["user_id"], :name => "index_roles_users_on_user_id"
135 142
136 143 create_table "sessions", :force => true do |t|
137 144 t.string "session_id"
138 145 t.text "data"
139 146 t.datetime "updated_at"
140 147 end
141 148
142 149 add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
143 150 add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
144 151
145 152 create_table "sites", :force => true do |t|
146 153 t.string "name"
147 154 t.boolean "started"
148 155 t.datetime "start_time"
149 156 t.datetime "created_at", :null => false
150 157 t.datetime "updated_at", :null => false
151 158 t.integer "country_id"
152 159 t.string "password"
153 160 end
154 161
155 162 create_table "submissions", :force => true do |t|
156 163 t.integer "user_id"
157 164 t.integer "problem_id"
158 165 t.integer "language_id"
159 166 t.text "source"
160 167 t.binary "binary"
161 168 t.datetime "submitted_at"
162 169 t.datetime "compiled_at"
163 170 t.text "compiler_message"
164 171 t.datetime "graded_at"
165 172 t.integer "points"
166 173 t.text "grader_comment"
167 174 t.integer "number"
168 175 t.string "source_filename"
169 176 t.float "max_runtime"
170 177 t.integer "peak_memory"
171 178 t.integer "effective_code_length"
172 179 end
173 180
174 181 add_index "submissions", ["user_id", "problem_id", "number"], :name => "index_submissions_on_user_id_and_problem_id_and_number", :unique => true
175 182 add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id"
176 183
177 184 create_table "tasks", :force => true do |t|
178 185 t.integer "submission_id"
179 186 t.datetime "created_at"
180 187 t.integer "status"
181 188 t.datetime "updated_at"
182 189 end
183 190
184 191 create_table "test_pairs", :force => true do |t|
@@ -1,58 +1,58
1 1 module GraderScript
2 2
3 3 def self.grader_control_enabled?
4 4 if defined? GRADER_ROOT_DIR
5 5 GRADER_ROOT_DIR != ''
6 6 else
7 7 false
8 8 end
9 9 end
10 10
11 11 def self.raw_dir
12 12 File.join GRADER_ROOT_DIR, "raw"
13 13 end
14 14
15 15 def self.call_grader(params)
16 16 if GraderScript.grader_control_enabled?
17 17 cmd = File.join(GRADER_ROOT_DIR, "scripts/grader") + " " + params
18 18 system(cmd)
19 19 end
20 20 end
21 21
22 22 def self.stop_grader(pid)
23 23 GraderScript.call_grader "stop #{pid}"
24 24 end
25 25
26 26 def self.stop_graders(pids)
27 27 pid_str = (pids.map { |process| process.pid.to_s }).join ' '
28 28 GraderScript.call_grader "stop #{pid_str}"
29 29 end
30 30
31 31 def self.start_grader(env)
32 32 GraderScript.call_grader "#{env} queue &"
33 33 GraderScript.call_grader "#{env} test_request &"
34 34 end
35 35
36 36 def self.call_import_problem(problem_name,
37 37 problem_dir,
38 38 time_limit=1,
39 39 memory_limit=32,
40 40 checker_name='text')
41 41 if GraderScript.grader_control_enabled?
42 42 cur_dir = `pwd`.chomp
43 43 Dir.chdir(GRADER_ROOT_DIR)
44 44
45 45 script_name = File.join(GRADER_ROOT_DIR, "scripts/import_problem")
46 46 cmd = "#{script_name} #{problem_name} #{problem_dir} #{checker_name}" +
47 47 " -t #{time_limit} -m #{memory_limit}"
48 48
49 49 output = `#{cmd}`
50 50
51 51 Dir.chdir(cur_dir)
52 52
53 - return output
53 + return "import CMD: #{cmd}\n" + output
54 54 end
55 55 return ''
56 56 end
57 57
58 58 end
@@ -1,118 +1,120
1 1 require 'tmpdir'
2 2
3 3 class TestdataImporter
4 4
5 5 attr :log_msg
6 6
7 7 def initialize(problem)
8 8 @problem = problem
9 9 end
10 10
11 11 def import_from_file(tempfile,
12 12 time_limit,
13 13 memory_limit,
14 + checker_name='text',
14 15 import_to_db=false)
15 16
16 17 dirname = extract(tempfile)
17 18 return false if not dirname
18 19 if not import_to_db
19 20 @log_msg = GraderScript.call_import_problem(@problem.name,
20 21 dirname,
21 22 time_limit,
22 - memory_limit)
23 + memory_limit,
24 + checker_name)
23 25 else
24 26 # Import test data to test pairs.
25 27
26 28 @problem.test_pairs.clear
27 29 if import_test_pairs(dirname)
28 30 test_pair_count = TestPair.count :conditions => "problem_id = #{@problem.id}"
29 31 @log_msg = "Importing test pair successful. (#{test_pair_count} test pairs imported)"
30 32 else
31 33 @log_msg = "Importing test pair failed. (0 test pairs imported)"
32 34 end
33 35 end
34 36
35 37 @log_msg << import_problem_description(dirname)
36 38 @log_msg << import_problem_pdf(dirname)
37 39 @log_msg << import_full_score(dirname)
38 40
39 41 return true
40 42 end
41 43
42 44 protected
43 45
44 46 def self.long_ext(filename)
45 47 i = filename.index('.')
46 48 len = filename.length
47 49 return filename.slice(i..len)
48 50 end
49 51
50 52 def extract(tempfile)
51 53 testdata_filename = save_testdata_file(tempfile)
52 54 ext = TestdataImporter.long_ext(tempfile.original_filename)
53 55
54 56 extract_dir = File.join(GraderScript.raw_dir, @problem.name)
55 57 if File.exists? extract_dir
56 58 backup_count = 0
57 59 begin
58 60 backup_count += 1
59 61 backup_dirname = "#{extract_dir}.backup.#{backup_count}"
60 62 end while File.exists? backup_dirname
61 63 File.rename(extract_dir, backup_dirname)
62 64 end
63 65 Dir.mkdir extract_dir
64 66
65 67 if ext=='.tar.gz' or ext=='.tgz'
66 68 cmd = "tar -zxvf #{testdata_filename} -C #{extract_dir}"
67 69 elsif ext=='.tar'
68 70 cmd = "tar -xvf #{testdata_filename} -C #{extract_dir}"
69 71 elsif ext=='.zip'
70 72 cmd = "unzip -o #{testdata_filename} -d #{extract_dir}"
71 73 else
72 74 return nil
73 75 end
74 76
75 77 system(cmd)
76 78
77 79 files = Dir["#{extract_dir}/**/*1*.in"]
78 80 return nil if files.length==0
79 81
80 82 File.delete(testdata_filename)
81 83
82 84 return File.dirname(files[0])
83 85 end
84 86
85 87 def save_testdata_file(tempfile)
86 88 ext = TestdataImporter.long_ext(tempfile.original_filename)
87 89 testdata_filename = File.join(Dir.tmpdir,"#{@problem.name}#{ext}")
88 90
89 91 return nil if tempfile==""
90 92
91 93 if tempfile.instance_of?(Tempfile)
92 94 tempfile.close
93 95 FileUtils.move(tempfile.path,testdata_filename)
94 96 else
95 97 File.open(testdata_filename, "wb") do |f|
96 98 f.write(tempfile.read)
97 99 end
98 100 end
99 101
100 102 return testdata_filename
101 103 end
102 104
103 105 def import_test_pairs(dirname)
104 106 test_num = 1
105 107 while FileTest.exists? "#{dirname}/#{test_num}.in"
106 108 in_filename = "#{dirname}/#{test_num}.in"
107 109 sol_filename = "#{dirname}/#{test_num}.sol"
108 110
109 111 break if not FileTest.exists? sol_filename
110 112
111 113 test_pair = TestPair.new(:input => open(in_filename).read,
112 114 :solution => open(sol_filename).read,
113 115 :problem => @problem)
114 116 break if not test_pair.save
115 117
116 118 test_num += 1
117 119 end
118 120 return test_num > 1
You need to be logged in to leave comments. Login now