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: 294 inserted, 86 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,293 +1,301
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
97 105
98 106 &.info-odd
99 107 background: #eeeeee
100 108
101 109 &.info-even
102 110 background: #fcfcfc
103 111
104 112 =basicbox
105 113 background: #eeeeff
106 114 border: 1px dotted #99aaee
107 115 padding: 5px
108 116 margin: 10px 0px
109 117 color: black
110 118 font-size: 13px
111 119
112 120 .infobox
113 121 +basicbox
114 122
115 123 .submitbox
116 124 +basicbox
117 125
118 126 .errorExplanation
119 127 border: 1px dotted gray
120 128 color: #bb2222
121 129 padding: 5px 15px 5px 15px
122 130 margin-bottom: 5px
123 131 background-color: white
124 132 font-weight: normal
125 133
126 134 h2
127 135 color: #cc1111
128 136 font-weight: bold
129 137
130 138
131 139 table.uinfo
132 140 border-collapse: collapse
133 141 border: 1px solid black
134 142 font-size: 13px
135 143
136 144
137 145 td.uinfo
138 146 vertical-align: top
139 147 border: 1px solid black
140 148 padding: 5px
141 149
142 150
143 151 th.uinfo
144 152 background: lightgreen
145 153 vertical-align: top
146 154 text-align: right
147 155 border: 1px solid black
148 156 padding: 5px
149 157
150 158
151 159 div
152 160 &.compilermsgbody
153 161 font-family: monospace
154 162
155 163 &.task-menu
156 164 text-align: center
157 165 font-size: 13px
158 166 line-height: 1.75em
159 167 font-weight: bold
160 168 border-top: 1px dashed gray
161 169 border-bottom: 1px dashed gray
162 170 margin-top: 2px
163 171 margin-bottom: 4px
164 172
165 173
166 174 table.taskdesc
167 175 border: 2px solid #dddddd
168 176 border-collapse: collapse
169 177 margin: 10px auto
170 178 width: 90%
171 179 font-size: 13px
172 180
173 181 p
174 182 font-size: 13px
175 183
176 184 tr.name
177 185 border: 2px solid #dddddd
178 186 background: #dddddd
179 187 color: #333333
180 188 font-weight: bold
181 189 font-size: 14px
182 190 line-height: 1.5em
183 191 text-align: center
184 192
185 193 td
186 194 &.desc-odd
187 195 padding: 5px
188 196 padding-left: 20px
189 197 background: #fefeee
190 198
191 199 &.desc-even
192 200 padding: 5px
193 201 padding-left: 20px
194 202 background: #feeefe
195 203
196 204
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,133 +1,134
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>
112 113 #{header}
113 114 <tr>
114 115 <td class="left-col">
115 116 #{user.full_name}<br/>
116 117 #{t 'title_bar.current_time'} #{format_short_time(Time.new)}
117 118 #{time_left}
118 119 <br/>
119 120 </td>
120 121 <td class="right-col">#{contest_name}</td>
121 122 </tr>
122 123 </table>
123 124 </div>
124 125 TITLEBAR
125 126 result.html_safe
126 127 end
127 128
128 129 def markdown(text)
129 130 markdown = RDiscount.new(text)
130 131 markdown.to_html.html_safe
131 132 end
132 133
133 134 end
@@ -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,303 +1,312
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 - def authenticated_by_pop3?(password)
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)
111 - result = JSON.parse resp.body
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)
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?
208 217 if GraderConfiguration.contest_mode?
209 218 return false if site==nil
210 219 return site.finished?
211 220 elsif GraderConfiguration.indv_contest_mode?
212 221 return false if self.contest_stat(true)==nil
213 222 return contest_time_left == 0
214 223 else
215 224 return false
216 225 end
217 226 end
218 227
219 228 def contest_started?
220 229 if GraderConfiguration.indv_contest_mode?
221 230 stat = self.contest_stat
222 231 return ((stat != nil) and (stat.started_at != nil))
223 232 elsif GraderConfiguration.contest_mode?
224 233 return true if site==nil
225 234 return site.started
226 235 else
227 236 return true
228 237 end
229 238 end
230 239
231 240 def update_start_time
232 241 stat = self.contest_stat
233 242 if stat == nil or stat.started_at == nil
234 243 stat ||= UserContestStat.new(:user => self)
235 244 stat.started_at = Time.now.gmtime
236 245 stat.save
237 246 end
238 247 end
239 248
240 249 def problem_in_user_contests?(problem)
241 250 problem_contests = problem.contests.all
242 251
243 252 if problem_contests.length == 0 # this is public contest
244 253 return true
245 254 end
246 255
247 256 contests.each do |contest|
248 257 if problem_contests.find {|c| c.id == contest.id }
249 258 return true
250 259 end
251 260 end
252 261 return false
253 262 end
254 263
255 264 def available_problems_group_by_contests
256 265 contest_problems = []
257 266 pin = {}
258 267 contests.enabled.each do |contest|
259 268 available_problems = contest.problems.available
260 269 contest_problems << {
261 270 :contest => contest,
262 271 :problems => available_problems
263 272 }
264 273 available_problems.each {|p| pin[p.id] = true}
265 274 end
266 275 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
267 276 contest_problems << {
268 277 :contest => nil,
269 278 :problems => other_avaiable_problems
270 279 }
271 280 return contest_problems
272 281 end
273 282
274 283 def available_problems
275 284 if not GraderConfiguration.multicontests?
276 285 return Problem.find_available_problems
277 286 else
278 287 contest_problems = []
279 288 pin = {}
280 289 contests.enabled.each do |contest|
281 290 contest.problems.available.each do |problem|
282 291 if not pin.has_key? problem.id
283 292 contest_problems << problem
284 293 end
285 294 pin[problem.id] = true
286 295 end
287 296 end
288 297 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
289 298 return contest_problems + other_avaiable_problems
290 299 end
291 300 end
292 301
293 302 def can_view_problem?(problem)
294 303 if not GraderConfiguration.multicontests?
295 304 return problem.available
296 305 else
297 306 return problem_in_user_contests? problem
298 307 end
299 308 end
300 309
301 310 protected
302 311 def encrypt_new_password
303 312 return if password.blank?
@@ -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,238 +1,245
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|
185 192 t.integer "problem_id"
186 193 t.text "input", :limit => 16777215
187 194 t.text "solution", :limit => 16777215
188 195 t.datetime "created_at", :null => false
189 196 t.datetime "updated_at", :null => false
190 197 end
191 198
192 199 create_table "test_requests", :force => true do |t|
193 200 t.integer "user_id"
194 201 t.integer "problem_id"
195 202 t.integer "submission_id"
196 203 t.string "input_file_name"
197 204 t.string "output_file_name"
198 205 t.string "running_stat"
199 206 t.integer "status"
200 207 t.datetime "updated_at", :null => false
201 208 t.datetime "submitted_at"
202 209 t.datetime "compiled_at"
203 210 t.text "compiler_message"
204 211 t.datetime "graded_at"
205 212 t.string "grader_comment"
206 213 t.datetime "created_at", :null => false
207 214 t.float "running_time"
208 215 t.string "exit_status"
209 216 t.integer "memory_usage"
210 217 end
211 218
212 219 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
213 220
214 221 create_table "user_contest_stats", :force => true do |t|
215 222 t.integer "user_id"
216 223 t.datetime "started_at"
217 224 t.datetime "created_at", :null => false
218 225 t.datetime "updated_at", :null => false
219 226 t.boolean "forced_logout"
220 227 end
221 228
222 229 create_table "users", :force => true do |t|
223 230 t.string "login", :limit => 50
224 231 t.string "full_name"
225 232 t.string "hashed_password"
226 233 t.string "salt", :limit => 5
227 234 t.string "alias"
228 235 t.string "email"
229 236 t.integer "site_id"
230 237 t.integer "country_id"
231 238 t.boolean "activated", :default => false
232 239 t.datetime "created_at"
233 240 t.datetime "updated_at"
234 241 end
235 242
236 243 add_index "users", ["login"], :name => "index_users_on_login", :unique => true
237 244
238 245 end
@@ -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,188 +1,190
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
119 121 end
120 122
121 123 def import_problem_description(dirname)
122 124 html_files = Dir["#{dirname}/*.html"]
123 125 markdown_files = Dir["#{dirname}/*.md"] + Dir["#{dirname}/*.markdown"]
124 126 if (html_files.length != 0) or (markdown_files.length != 0)
125 127 description = @problem.description || Description.new
126 128
127 129 if html_files.length != 0
128 130 filename = html_files[0]
129 131 description.markdowned = false
130 132 else
131 133 filename = markdown_files[0]
132 134 description.markdowned = true
133 135 end
134 136
135 137 description.body = open(filename).read
136 138 description.save
137 139 @problem.description = description
138 140 @problem.save
139 141 return "\nProblem description imported from #{filename}."
140 142 else
141 143 return ''
142 144 end
143 145 end
144 146
145 147 def import_problem_pdf(dirname)
146 148 pdf_files = Dir["#{dirname}/*.pdf"]
147 149 puts "CHECKING... #{dirname}"
148 150 if pdf_files.length != 0
149 151 puts "HAS PDF FILE"
150 152 filename = pdf_files[0]
151 153
152 154 @problem.save if not @problem.id
153 155 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
154 156 if not FileTest.exists? out_dirname
155 157 Dir.mkdir out_dirname
156 158 end
157 159
158 160 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
159 161
160 162 if FileTest.exists? out_filename
161 163 File.delete out_filename
162 164 end
163 165
164 166 File.rename(filename, out_filename)
165 167 @problem.description_filename = "#{@problem.name}.pdf"
166 168 @problem.save
167 169 return "\nProblem pdf imported from #{filename}."
168 170 else
169 171 return ""
170 172 end
171 173 end
172 174
173 175 #just set the full score to the total number of test case
174 176 #it is not perfect but works on most normal use case
175 177 def import_full_score(dirname)
176 178 num = 0
177 179 loop do
178 180 num += 1
179 181 in_file = Dir["#{dirname}/#{num}*.in"]
180 182 break if in_file.length == 0
181 183 end
182 184 full_score = (num - 1) * 10
183 185 @problem.full_score = full_score
184 186 @problem.save
185 187 return "\nFull score is set to #{full_score}."
186 188 end
187 189
188 190 end
You need to be logged in to leave comments. Login now