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

r788:3241620ac7e7 - - 10 files changed: 452 inserted, 323 deleted

@@ -0,0 +1,61
1 + - content_for :header do
2 + = javascript_include_tag 'local_jquery'
3 +
4 + %h1 Submissions detail
5 +
6 + .row
7 + .col-md-4
8 + = render partial: 'shared/problem_select'
9 + .col-md-4
10 + = render partial: 'shared/problem_select'
11 + .col-md-4
12 + = render partial: 'shared/user_select'
13 +
14 +
15 + .row
16 + .col-12
17 + %table.table.table-hover.table-condense.datatable
18 + -#
19 + %thead
20 + %tr
21 + %th User
22 + %th Problem
23 + %th Subbmission ID
24 + %th Submit Time
25 + %th Score
26 + %th detail
27 + %tbody
28 + - @submissions.each do |s|
29 + %tr
30 + %td= s.user.login
31 + %td= s.problem.long_name
32 + %td= s.id
33 + %td= s.submitted_at
34 + %td= s.points
35 + %td= s.grader_comment
36 +
37 +
38 + :javascript
39 + $('.datatable').DataTable({
40 + columns: [
41 + {title: 'User', data: 'login'},
42 + {title: 'Problem', data: 'problem.long_name'},
43 + {title: 'Sub ID', data: 'id'},
44 + {title: 'Submit at', data: 'submitted_at'},
45 + {title: 'Points', data: 'points'},
46 + {title: 'detail', data: 'grader_comment'},
47 + ],
48 + ajax: {
49 + url: '#{submission_query_report_path}',
50 + type: 'POST',
51 + data: (d) => {
52 + d.since_datetime = '1123'
53 + d.until_datetime = 'xxx'
54 + },
55 + dataType: 'json',
56 + beforeSend: (request) => {
57 + request.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
58 + },
59 + }, //end ajax
60 + 'pageLength': 50
61 + });
@@ -0,0 +1,15
1 + json.draw params['draw']&.to_i
2 + json.recordsTotal @recordsTotal
3 + json.recordsFiltered @recordsFiltered
4 + json.data do
5 + json.array! @submissions do |sub|
6 + json.extact! sub, :id, :points, :grader_comment
7 + json.submitted_at sub.submitted_at
8 + json.problem do
9 + json.long_name sub.problem&.long_name
10 + end
11 + json.user do
12 + json.login sub.user&.login
13 + end
14 + end
15 + end
@@ -0,0 +1,10
1 + .panel.panel-primary
2 + .panel-heading
3 + Problems
4 + .panel-body
5 + %p
6 + Select problem(s) that we wish to know the score.
7 + = label_tag :problem_id, "Problems"
8 + = select_tag 'problem_id[]',
9 + options_for_select(Problem.all.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},params[:problem_id]),
10 + { class: 'select2 form-control', multiple: "true" }
@@ -0,0 +1,12
1 + .panel.panel-primary
2 + .panel-heading
3 + Users
4 + .panel-body
5 + .radio
6 + %label
7 + = radio_button_tag 'users', 'all', (params[:users] == "all")
8 + All users
9 + .radio
10 + %label
11 + = radio_button_tag 'users', 'enabled', (params[:users] == "enabled")
12 + Only enabled users
@@ -1,341 +1,336
1 1 GEM
2 2 remote: https://rubygems.org/
3 3 specs:
4 4 RubyInline (3.12.4)
5 5 ZenTest (~> 4.3)
6 6 ZenTest (4.11.2)
7 7 ace-rails-ap (4.2)
8 - actioncable (5.2.3)
9 - actionpack (= 5.2.3)
8 + actioncable (5.2.4.2)
9 + actionpack (= 5.2.4.2)
10 10 nio4r (~> 2.0)
11 11 websocket-driver (>= 0.6.1)
12 - actionmailer (5.2.3)
13 - actionpack (= 5.2.3)
14 - actionview (= 5.2.3)
15 - activejob (= 5.2.3)
12 + actionmailer (5.2.4.2)
13 + actionpack (= 5.2.4.2)
14 + actionview (= 5.2.4.2)
15 + activejob (= 5.2.4.2)
16 16 mail (~> 2.5, >= 2.5.4)
17 17 rails-dom-testing (~> 2.0)
18 - actionpack (5.2.3)
19 - actionview (= 5.2.3)
20 - activesupport (= 5.2.3)
21 - rack (~> 2.0)
18 + actionpack (5.2.4.2)
19 + actionview (= 5.2.4.2)
20 + activesupport (= 5.2.4.2)
21 + rack (~> 2.0, >= 2.0.8)
22 22 rack-test (>= 0.6.3)
23 23 rails-dom-testing (~> 2.0)
24 24 rails-html-sanitizer (~> 1.0, >= 1.0.2)
25 - actionview (5.2.3)
26 - activesupport (= 5.2.3)
25 + actionview (5.2.4.2)
26 + activesupport (= 5.2.4.2)
27 27 builder (~> 3.1)
28 28 erubi (~> 1.4)
29 29 rails-dom-testing (~> 2.0)
30 30 rails-html-sanitizer (~> 1.0, >= 1.0.3)
31 - activejob (5.2.3)
32 - activesupport (= 5.2.3)
31 + activejob (5.2.4.2)
32 + activesupport (= 5.2.4.2)
33 33 globalid (>= 0.3.6)
34 - activemodel (5.2.3)
35 - activesupport (= 5.2.3)
36 - activerecord (5.2.3)
37 - activemodel (= 5.2.3)
38 - activesupport (= 5.2.3)
34 + activemodel (5.2.4.2)
35 + activesupport (= 5.2.4.2)
36 + activerecord (5.2.4.2)
37 + activemodel (= 5.2.4.2)
38 + activesupport (= 5.2.4.2)
39 39 arel (>= 9.0)
40 40 activerecord-session_store (1.1.3)
41 41 actionpack (>= 4.0)
42 42 activerecord (>= 4.0)
43 43 multi_json (~> 1.11, >= 1.11.2)
44 44 rack (>= 1.5.2, < 3)
45 45 railties (>= 4.0)
46 - activestorage (5.2.3)
47 - actionpack (= 5.2.3)
48 - activerecord (= 5.2.3)
46 + activestorage (5.2.4.2)
47 + actionpack (= 5.2.4.2)
48 + activerecord (= 5.2.4.2)
49 49 marcel (~> 0.3.1)
50 - activesupport (5.2.3)
50 + activesupport (5.2.4.2)
51 51 concurrent-ruby (~> 1.0, >= 1.0.2)
52 52 i18n (>= 0.7, < 2)
53 53 minitest (~> 5.1)
54 54 tzinfo (~> 1.1)
55 - addressable (2.6.0)
56 - public_suffix (>= 2.0.2, < 4.0)
55 + addressable (2.7.0)
56 + public_suffix (>= 2.0.2, < 5.0)
57 57 ansi (1.5.0)
58 58 arel (9.0.0)
59 - autoprefixer-rails (9.5.1.1)
59 + autoprefixer-rails (9.5.1)
60 60 execjs
61 61 best_in_place (3.0.3)
62 62 actionpack (>= 3.2)
63 63 railties (>= 3.2)
64 - bindex (0.7.0)
65 - bootsnap (1.4.4)
64 + bindex (0.8.1)
65 + bootsnap (1.4.6)
66 66 msgpack (~> 1.0)
67 67 bootstrap-datepicker-rails (1.8.0.1)
68 68 railties (>= 3.0)
69 69 bootstrap-sass (3.4.1)
70 70 autoprefixer-rails (>= 5.2.1)
71 71 sassc (>= 2.0.0)
72 72 bootstrap-switch-rails (3.3.4)
73 73 bootstrap-toggle-rails (2.2.1.0)
74 74 bootstrap3-datetimepicker-rails (4.17.47)
75 75 momentjs-rails (>= 2.8.1)
76 - builder (3.2.3)
76 + builder (3.2.4)
77 77 byebug (11.0.1)
78 - capybara (3.25.0)
78 + capybara (3.15.1)
79 79 addressable
80 80 mini_mime (>= 0.1.3)
81 81 nokogiri (~> 1.8)
82 82 rack (>= 1.6.0)
83 83 rack-test (>= 0.6.3)
84 - regexp_parser (~> 1.5)
84 + regexp_parser (~> 1.2)
85 85 xpath (~> 3.2)
86 - capybara-console (0.0.4)
87 - capybara
88 - rails (>= 3.0)
89 - childprocess (1.0.1)
90 - rake (< 13.0)
86 + childprocess (3.0.0)
91 87 coffee-rails (4.2.2)
92 88 coffee-script (>= 2.2.0)
93 89 railties (>= 4.0.0)
94 90 coffee-script (2.4.1)
95 91 coffee-script-source
96 92 execjs
97 93 coffee-script-source (1.12.2)
98 - concurrent-ruby (1.1.5)
99 - crass (1.0.4)
94 + concurrent-ruby (1.1.6)
95 + crass (1.0.6)
100 96 dynamic_form (1.1.4)
101 - erubi (1.8.0)
97 + erubi (1.9.0)
102 98 erubis (2.7.0)
103 99 execjs (2.7.0)
104 - ffi (1.11.1)
100 + ffi (1.10.0)
105 101 fuzzy-string-match (1.0.1)
106 102 RubyInline (>= 3.8.6)
107 103 globalid (0.4.2)
108 104 activesupport (>= 4.2.0)
109 - haml (5.1.0)
105 + haml (5.0.4)
110 106 temple (>= 0.8.0)
111 107 tilt
112 108 haml-rails (1.0.0)
113 109 actionpack (>= 4.0.1)
114 110 activesupport (>= 4.0.1)
115 111 haml (>= 4.0.6, < 6.0)
116 112 html2haml (>= 1.0.1)
117 113 railties (>= 4.0.1)
118 114 html2haml (2.2.0)
119 115 erubis (~> 2.7.0)
120 116 haml (>= 4.0, < 6)
121 117 nokogiri (>= 1.6.0)
122 118 ruby_parser (~> 3.5)
123 - i18n (1.6.0)
119 + i18n (1.8.2)
124 120 concurrent-ruby (~> 1.0)
125 121 in_place_editing (1.2.0)
126 - jbuilder (2.9.1)
127 - activesupport (>= 4.2.0)
122 + jbuilder (2.10.0)
123 + activesupport (>= 5.0.0)
128 124 jquery-countdown-rails (2.0.2)
129 125 jquery-datatables-rails (3.4.0)
130 126 actionpack (>= 3.1)
131 127 jquery-rails
132 128 railties (>= 3.1)
133 129 sass-rails
134 130 jquery-rails (4.3.3)
135 131 rails-dom-testing (>= 1, < 3)
136 132 railties (>= 4.2.0)
137 133 thor (>= 0.14, < 2.0)
138 134 jquery-tablesorter (1.26.1)
139 135 railties (>= 3.2, < 6)
140 136 jquery-timepicker-addon-rails (1.4.1)
141 137 railties (>= 3.1)
142 138 jquery-ui-rails (6.0.1)
143 139 railties (>= 3.2.16)
144 140 listen (3.1.5)
145 141 rb-fsevent (~> 0.9, >= 0.9.4)
146 142 rb-inotify (~> 0.9, >= 0.9.7)
147 143 ruby_dep (~> 1.2)
148 - loofah (2.2.3)
144 + loofah (2.4.0)
149 145 crass (~> 1.0.2)
150 146 nokogiri (>= 1.5.9)
151 147 mail (2.7.1)
152 148 mini_mime (>= 0.1.1)
153 149 marcel (0.3.3)
154 150 mimemagic (~> 0.3.2)
155 - method_source (0.9.2)
156 - mimemagic (0.3.3)
157 - mini_mime (1.0.1)
151 + method_source (1.0.0)
152 + mimemagic (0.3.4)
153 + mini_mime (1.0.2)
158 154 mini_portile2 (2.4.0)
159 - minitest (5.11.3)
155 + minitest (5.14.0)
160 156 minitest-reporters (1.3.6)
161 157 ansi
162 158 builder
163 159 minitest (>= 5.0)
164 160 ruby-progressbar
165 161 momentjs-rails (2.20.1)
166 162 railties (>= 3.1)
167 - msgpack (1.3.0)
163 + msgpack (1.3.3)
168 164 multi_json (1.13.1)
169 165 mysql2 (0.5.2)
170 - nio4r (2.3.1)
171 - nokogiri (1.10.3)
166 + nio4r (2.5.2)
167 + nokogiri (1.10.9)
172 168 mini_portile2 (~> 2.4.0)
173 - public_suffix (3.1.1)
174 - puma (4.0.0)
169 + public_suffix (4.0.3)
170 + puma (4.3.3)
175 171 nio4r (~> 2.0)
176 - rack (2.0.7)
172 + rack (2.2.2)
177 173 rack-test (1.1.0)
178 174 rack (>= 1.0, < 3)
179 - rails (5.2.3)
180 - actioncable (= 5.2.3)
181 - actionmailer (= 5.2.3)
182 - actionpack (= 5.2.3)
183 - actionview (= 5.2.3)
184 - activejob (= 5.2.3)
185 - activemodel (= 5.2.3)
186 - activerecord (= 5.2.3)
187 - activestorage (= 5.2.3)
188 - activesupport (= 5.2.3)
175 + rails (5.2.4.2)
176 + actioncable (= 5.2.4.2)
177 + actionmailer (= 5.2.4.2)
178 + actionpack (= 5.2.4.2)
179 + actionview (= 5.2.4.2)
180 + activejob (= 5.2.4.2)
181 + activemodel (= 5.2.4.2)
182 + activerecord (= 5.2.4.2)
183 + activestorage (= 5.2.4.2)
184 + activesupport (= 5.2.4.2)
189 185 bundler (>= 1.3.0)
190 - railties (= 5.2.3)
186 + railties (= 5.2.4.2)
191 187 sprockets-rails (>= 2.0.0)
192 188 rails-controller-testing (1.0.4)
193 189 actionpack (>= 5.0.1.x)
194 190 actionview (>= 5.0.1.x)
195 191 activesupport (>= 5.0.1.x)
196 192 rails-dom-testing (2.0.3)
197 193 activesupport (>= 4.2.0)
198 194 nokogiri (>= 1.6)
199 - rails-html-sanitizer (1.0.4)
200 - loofah (~> 2.2, >= 2.2.2)
195 + rails-html-sanitizer (1.3.0)
196 + loofah (~> 2.3)
201 197 rails_bootstrap_sortable (2.0.6)
202 198 momentjs-rails (>= 2.8.3)
203 - railties (5.2.3)
204 - actionpack (= 5.2.3)
205 - activesupport (= 5.2.3)
199 + railties (5.2.4.2)
200 + actionpack (= 5.2.4.2)
201 + activesupport (= 5.2.4.2)
206 202 method_source
207 203 rake (>= 0.8.7)
208 204 thor (>= 0.19.0, < 2.0)
209 - rake (12.3.2)
205 + rake (13.0.1)
210 206 rb-fsevent (0.10.3)
211 207 rb-inotify (0.10.0)
212 208 ffi (~> 1.0)
213 209 rdiscount (2.2.0.1)
214 - regexp_parser (1.5.1)
210 + regexp_parser (1.7.0)
215 211 rouge (3.3.0)
216 212 ruby-progressbar (1.10.0)
217 213 ruby_dep (1.5.0)
218 214 ruby_parser (3.13.1)
219 215 sexp_processor (~> 4.9)
220 - rubyzip (1.2.3)
216 + rubyzip (1.3.0)
221 217 sass (3.7.4)
222 218 sass-listen (~> 4.0.0)
223 219 sass-listen (4.0.0)
224 220 rb-fsevent (~> 0.9, >= 0.9.4)
225 221 rb-inotify (~> 0.9, >= 0.9.7)
226 222 sass-rails (5.0.7)
227 223 railties (>= 4.0.0, < 6)
228 224 sass (~> 3.1)
229 225 sprockets (>= 2.8, < 4.0)
230 226 sprockets-rails (>= 2.0, < 4.0)
231 227 tilt (>= 1.1, < 3)
232 - sassc (2.0.1)
228 + sassc (2.2.1)
233 229 ffi (~> 1.9)
234 - rake
235 - sassc-rails (2.1.1)
230 + sassc-rails (2.1.2)
236 231 railties (>= 4.0.0)
237 232 sassc (>= 2.0)
238 233 sprockets (> 3.0)
239 234 sprockets-rails
240 235 tilt
241 236 select2-rails (4.0.3)
242 237 thor (~> 0.14)
243 - selenium-webdriver (3.142.3)
244 - childprocess (>= 0.5, < 2.0)
245 - rubyzip (~> 1.2, >= 1.2.2)
238 + selenium-webdriver (3.142.7)
239 + childprocess (>= 0.5, < 4.0)
240 + rubyzip (>= 1.2.2)
246 241 sexp_processor (4.12.0)
247 - simple_form (4.1.0)
242 + simple_form (5.0.2)
248 243 actionpack (>= 5.0)
249 244 activemodel (>= 5.0)
250 - spring (2.1.0)
245 + spring (2.0.2)
246 + activesupport (>= 4.2)
251 247 spring-watcher-listen (2.0.1)
252 248 listen (>= 2.7, < 4.0)
253 249 spring (>= 1.2, < 3.0)
254 250 sprockets (3.7.2)
255 251 concurrent-ruby (~> 1.0)
256 252 rack (> 1, < 3)
257 253 sprockets-rails (3.2.1)
258 254 actionpack (>= 4.0)
259 255 activesupport (>= 4.0)
260 256 sprockets (>= 3.0.0)
261 257 sqlite3 (1.4.1)
262 258 temple (0.8.1)
263 259 thor (0.20.3)
264 260 thread_safe (0.3.6)
265 261 tilt (2.0.9)
266 - tzinfo (1.2.5)
262 + tzinfo (1.2.7)
267 263 thread_safe (~> 0.1)
268 264 uglifier (4.1.20)
269 265 execjs (>= 0.3.0, < 3)
270 266 web-console (3.7.0)
271 267 actionview (>= 5.0)
272 268 activemodel (>= 5.0)
273 269 bindex (>= 0.4.0)
274 270 railties (>= 5.0)
275 - webdriver (0.1.0)
271 + webdriver (0.5.0)
276 272 websocket-driver (0.7.1)
277 273 websocket-extensions (>= 0.1.0)
278 274 websocket-extensions (0.1.4)
279 275 will_paginate (3.0.12)
280 276 xpath (3.2.0)
281 277 nokogiri (~> 1.8)
282 278 yaml_db (0.7.0)
283 279 rails (>= 3.0)
284 280 rake (>= 0.8.7)
285 281
286 282 PLATFORMS
287 283 ruby
288 284
289 285 DEPENDENCIES
290 286 ace-rails-ap
291 287 activerecord-session_store
292 288 autoprefixer-rails
293 289 best_in_place (~> 3.0.1)
294 290 bootsnap (>= 1.1.0)
295 291 bootstrap-datepicker-rails
296 292 bootstrap-sass (~> 3.4.1)
297 293 bootstrap-switch-rails
298 294 bootstrap-toggle-rails
299 295 bootstrap3-datetimepicker-rails
300 296 byebug
301 297 capybara (>= 2.15)
302 - capybara-console
303 298 coffee-rails
304 299 dynamic_form
305 300 fuzzy-string-match
306 301 haml
307 302 haml-rails
308 303 in_place_editing
309 304 jbuilder (~> 2.5)
310 305 jquery-countdown-rails
311 306 jquery-datatables-rails
312 307 jquery-rails
313 308 jquery-tablesorter
314 309 jquery-timepicker-addon-rails
315 310 jquery-ui-rails
316 311 listen (>= 3.0.5, < 3.2)
317 312 mail
318 313 minitest-reporters
319 314 momentjs-rails
320 315 mysql2
321 316 puma
322 317 rails (~> 5.2)
323 318 rails-controller-testing
324 319 rails_bootstrap_sortable
325 320 rdiscount
326 321 rouge
327 322 sassc-rails
328 323 select2-rails
329 324 selenium-webdriver
330 325 simple_form
331 326 spring
332 327 spring-watcher-listen (~> 2.0.0)
333 328 sqlite3
334 329 uglifier
335 330 web-console (>= 3.3.0)
336 331 webdriver
337 332 will_paginate (~> 3.0.7)
338 333 yaml_db
339 334
340 335 BUNDLED WITH
341 - 1.17.2
336 + 1.16.2
@@ -1,167 +1,228
1 1 require 'ipaddr'
2 2
3 3 class ApplicationController < ActionController::Base
4 4 protect_from_forgery
5 5
6 6 before_action :current_user
7 7
8 8 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
9 9 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
10 10 WHITELIST_IGNORE_CONF_KEY = 'right.whitelist_ignore'
11 11 WHITELIST_IP_CONF_KEY = 'right.whitelist_ip'
12 12
13 13 #report and redirect for unauthorized activities
14 14 def unauthorized_redirect(notice = 'You are not authorized to view the page you requested')
15 15 flash[:notice] = notice
16 16 redirect_to login_main_path
17 17 end
18 18
19 19 # Returns the current logged-in user (if any).
20 20 def current_user
21 21 return nil unless session[:user_id]
22 22 @current_user ||= User.find(session[:user_id])
23 23 end
24 24
25 25 def admin_authorization
26 26 return false unless check_valid_login
27 27 user = User.includes(:roles).find(session[:user_id])
28 28 unless user.admin?
29 29 unauthorized_redirect
30 30 return false
31 31 end
32 32 return true
33 33 end
34 34
35 35 def authorization_by_roles(allowed_roles)
36 36 return false unless check_valid_login
37 37 user = User.find(session[:user_id])
38 38 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
39 39 unauthorized_redirect
40 40 return false
41 41 end
42 42 end
43 43
44 44 def testcase_authorization
45 45 #admin always has privileged
46 46 if @current_user.admin?
47 47 return true
48 48 end
49 49
50 50 unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
51 51 end
52 52
53 53
54 54 protected
55 55
56 56 #redirect to root (and also force logout)
57 57 #if the user is not logged_in or the system is in "ADMIN ONLY" mode
58 58 def check_valid_login
59 59 #check if logged in
60 60 unless session[:user_id]
61 61 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
62 62 unauthorized_redirect('You need to login but you cannot log in at this time')
63 63 else
64 64 unauthorized_redirect('You need to login')
65 65 end
66 66 return false
67 67 end
68 68
69 69 # check if run in single user mode
70 70 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
71 71 if @current_user==nil || (!@current_user.admin?)
72 72 unauthorized_redirect('You cannot log in at this time')
73 73 return false
74 74 end
75 75 end
76 76
77 77 # check if the user is enabled
78 78 unless @current_user.enabled? || @current_user.admin?
79 79 unauthorized_redirect 'Your account is disabled'
80 80 return false
81 81 end
82 82
83 83 # check if user ip is allowed
84 84 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
85 85 unless is_request_ip_allowed?
86 86 unauthorized_redirect 'Your IP is not allowed to login at this time.'
87 87 return false
88 88 end
89 89 end
90 90
91 91 if GraderConfiguration.multicontests?
92 92 return true if @current_user.admin?
93 93 begin
94 94 if @current_user.contest_stat(true).forced_logout
95 95 flash[:notice] = 'You have been automatically logged out.'
96 96 redirect_to :controller => 'main', :action => 'index'
97 97 end
98 98 rescue
99 99 end
100 100 end
101 101 return true
102 102 end
103 103
104 104 #redirect to root (and also force logout)
105 105 #if the user use different ip from the previous connection
106 106 # only applicable when MULTIPLE_IP_LOGIN options is false only
107 107 def authenticate_by_ip_address
108 108 #this assume that we have already authenticate normally
109 109 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
110 110 user = User.find(session[:user_id])
111 111 if (!user.admin? && user.last_ip && user.last_ip != request.remote_ip)
112 112 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
113 113 redirect_to :controller => 'main', :action => 'login'
114 114 return false
115 115 end
116 116 unless user.last_ip
117 117 user.last_ip = request.remote_ip
118 118 user.save
119 119 end
120 120 end
121 121 return true
122 122 end
123 123
124 124 def authorization
125 125 return false unless check_valid_login
126 126 user = User.find(session[:user_id])
127 127 unless user.roles.detect { |role|
128 128 role.rights.detect{ |right|
129 129 right.controller == self.class.controller_name and
130 130 (right.action == 'all' || right.action == action_name)
131 131 }
132 132 }
133 133 flash[:notice] = 'You are not authorized to view the page you requested'
134 134 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
135 135 redirect_to :controller => 'main', :action => 'login'
136 136 return false
137 137 end
138 138 end
139 139
140 140 def verify_time_limit
141 141 return true if session[:user_id]==nil
142 142 user = User.find(session[:user_id], :include => :site)
143 143 return true if user==nil || user.site == nil
144 144 if user.contest_finished?
145 145 flash[:notice] = 'Error: the contest you are participating is over.'
146 146 redirect_to :back
147 147 return false
148 148 end
149 149 return true
150 150 end
151 151
152 152 def is_request_ip_allowed?
153 153 unless GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
154 154 user_ip = IPAddr.new(request.remote_ip)
155 155
156 156 GraderConfiguration[WHITELIST_IP_CONF_KEY].delete(' ').split(',').each do |ips|
157 157 allow_ips = IPAddr.new(ips)
158 158 if allow_ips.include?(user_ip)
159 159 return true
160 160 end
161 161 end
162 162 return false
163 163 end
164 164 return true
165 165 end
166 166
167 + #function for datatable ajax query
168 + #return record,total_count,filter_count
169 + def process_query_record(record,
170 + total_count: nil,
171 + select: '',
172 + global_search: [],
173 + no_search: false,
174 + force_order: '',
175 + date_filter: '', date_param_since: 'date_since',date_param_until: 'date_until')
176 + arel_table = record.model.arel_table
177 +
178 + if !no_search && params['search']
179 + global_value = record.model.sanitize_sql(params['search']['value'].strip.downcase)
180 + if !global_value.blank?
181 + global_value.split.each do |value|
182 + global_where = global_search.map{|f| "LOWER(#{f}) like '%#{value}%'"}.join(' OR ')
183 + record = record.where(global_where)
184 + end
185 + end
186 +
187 + params['columns'].each do |i, col|
188 + if !col['search']['value'].blank?
189 + record = record.where(arel_table[col['name']].lower.matches("%#{col['search']['value'].strip.downcase}%"))
190 + end
191 + end
192 + end
193 +
194 + if !date_filter.blank?
195 + date_since = Time.parse( params[:date_since] ) rescue Time.new(1,1,1)
196 + date_until = Time.parse( params[:date_until] ) rescue Time.zone.now()
197 + date_range = date_since..date_until
198 + record = record.where(date_filter.to_sym => date_range)
199 + end
200 +
201 + if force_order.blank?
202 + if params['order']
203 + params['order'].each do |i, o|
204 + colName = params['columns'][o['column']]['name']
205 + colName = "#{record.model.table_name}.#{colName}" if colName.upcase == 'ID'
206 + record = record.order("#{colName} #{o['dir'].casecmp('desc') != 0 ? 'ASC' : 'DESC'}") unless colName.blank?
207 + end
208 + end
209 + else
210 + record = record.order(force_order)
211 + end
212 +
213 + filterCount = record.count(record.model.primary_key)
214 + # if .group() is used, filterCount might be like {id_1: count_1, id_2: count_2, ...}
215 + # so we should count the result again..
216 + if filterCount.is_a? Hash
217 + filterCount = filterCount.count
218 + end
219 +
220 + record = record.offset(params['start'] || 0).limit(params['length'] || 100)
221 + if (!select.blank?)
222 + record = record.select(select)
223 + end
224 +
225 + return record, total_count || record.model.count, filterCount
226 + end
227 +
167 228 end
@@ -1,527 +1,523
1 1 require 'csv'
2 2
3 3 class ReportController < ApplicationController
4 4
5 5 before_action :check_valid_login
6 6
7 7 before_action :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score, :current_score]
8 8
9 9 before_action(only: [:problem_hof]) { |c|
10 10 return false unless check_valid_login
11 11
12 12 admin_authorization unless GraderConfiguration["right.user_view_submission"]
13 13 }
14 14
15 15 def max_score
16 16 end
17 17
18 18 def current_score
19 19 @problems = Problem.available_problems
20 20 if params[:group_id]
21 21 @group = Group.find(params[:group_id])
22 22 @users = @group.users.where(enabled: true)
23 23 else
24 24 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
25 25 end
26 26 @scorearray = calculate_max_score(@problems, @users,0,0,true)
27 27
28 28 #rencer accordingly
29 29 if params[:button] == 'download' then
30 30 csv = gen_csv_from_scorearray(@scorearray,@problems)
31 31 send_data csv, filename: 'max_score.csv'
32 32 else
33 33 #render template: 'user_admin/user_stat'
34 34 render 'current_score'
35 35 end
36 36 end
37 37
38 38 def show_max_score
39 39 #process parameters
40 40 #problems
41 41 @problems = []
42 42 if params[:problem_id]
43 43 params[:problem_id].each do |id|
44 44 next unless id.strip != ""
45 45 pid = Problem.find_by_id(id.to_i)
46 46 @problems << pid if pid
47 47 end
48 48 end
49 49
50 50 #users
51 51 @users = if params[:users] == "all" then
52 52 User.includes(:contests).includes(:contest_stat)
53 53 else
54 54 User.includes(:contests).includes(:contest_stat).where(enabled: true)
55 55 end
56 56
57 57 #set up range from param
58 58 @since_id = params.fetch(:from_id, 0).to_i
59 59 @until_id = params.fetch(:to_id, 0).to_i
60 60 @since_id = nil if @since_id == 0
61 61 @until_id = nil if @until_id == 0
62 62
63 63 #calculate the routine
64 64 @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
65 65
66 66 #rencer accordingly
67 67 if params[:button] == 'download' then
68 68 csv = gen_csv_from_scorearray(@scorearray,@problems)
69 69 send_data csv, filename: 'max_score.csv'
70 70 else
71 71 #render template: 'user_admin/user_stat'
72 72 render 'max_score'
73 73 end
74 74
75 75 end
76 76
77 77 def score
78 78 if params[:commit] == 'download csv'
79 79 @problems = Problem.all
80 80 else
81 81 @problems = Problem.available_problems
82 82 end
83 83 @users = User.includes(:contests, :contest_stat).where(enabled: true)
84 84 @scorearray = Array.new
85 85 @users.each do |u|
86 86 ustat = Array.new
87 87 ustat[0] = u
88 88 @problems.each do |p|
89 89 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
90 90 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
91 91 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
92 92 else
93 93 ustat << [0,false]
94 94 end
95 95 end
96 96 @scorearray << ustat
97 97 end
98 98 if params[:commit] == 'download csv' then
99 99 csv = gen_csv_from_scorearray(@scorearray,@problems)
100 100 send_data csv, filename: 'last_score.csv'
101 101 else
102 102 render template: 'user_admin/user_stat'
103 103 end
104 104
105 105 end
106 106
107 107 def login_stat
108 108 @logins = Array.new
109 109
110 110 date_and_time = '%Y-%m-%d %H:%M'
111 111 begin
112 112 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
113 113 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
114 114 rescue
115 115 @since_time = DateTime.new(1000,1,1)
116 116 end
117 117 begin
118 118 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
119 119 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
120 120 rescue
121 121 @until_time = DateTime.new(3000,1,1)
122 122 end
123 123
124 124 User.all.each do |user|
125 125 @logins << { id: user.id,
126 126 login: user.login,
127 127 full_name: user.full_name,
128 128 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
129 129 user.id,@since_time,@until_time)
130 130 .count(:id),
131 131 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
132 132 user.id,@since_time,@until_time)
133 133 .minimum(:created_at),
134 134 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
135 135 user.id,@since_time,@until_time)
136 136 .maximum(:created_at),
137 137 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
138 138 user.id,@since_time,@until_time)
139 139 .select(:ip_address).uniq
140 140
141 141 }
142 142 end
143 143 end
144 144
145 - def submission_stat
146 -
145 + def submission
147 146 date_and_time = '%Y-%m-%d %H:%M'
148 147 begin
149 148 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
150 149 rescue
151 150 @since_time = DateTime.new(1000,1,1)
152 151 end
153 152 begin
154 153 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
155 154 rescue
156 155 @until_time = DateTime.new(3000,1,1)
157 156 end
158 157
159 - @submissions = {}
160 -
161 - User.find_each do |user|
162 - @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
163 - end
158 + @submissions = Submission
159 + .joins(:problem).joins(:user)
160 + .where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time)
161 + end
164 162
165 - Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
166 - if @submissions[s.user_id]
167 - if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
168 - a = Problem.find_by_id(s.problem_id)
169 - @submissions[s.user_id][:sub][s.problem_id] =
170 - { prob_name: (a ? a.full_name : '(NULL)'),
171 - sub_ids: [s.id] }
172 - else
173 - @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
174 - end
175 - @submissions[s.user_id][:count] += 1
176 - end
177 - end
163 + def submission_query
164 + @submissions = Submission
165 + .joins(:problem).joins(:user)
166 + .where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time)
167 +
168 + @submissions, @recordsTotal, @recordsFiltered = process_query_record( @submissions,
169 + global_search: ['user.login','user.full_name','problem.name','problem.full_name','points'],
170 + date_filter: 'submitted_at',
171 + date_param_since: 'since_datetime',
172 + date_param_until: 'until_datetime',
173 + )
178 174 end
179 175
180 176 def problem_hof
181 177 # gen problem list
182 178 @user = User.find(session[:user_id])
183 179 @problems = @user.available_problems
184 180
185 181 # get selected problems or the default
186 182 if params[:id]
187 183 begin
188 184 @problem = Problem.available.find(params[:id])
189 185 rescue
190 186 redirect_to action: :problem_hof
191 187 flash[:notice] = 'Error: submissions for that problem are not viewable.'
192 188 return
193 189 end
194 190 end
195 191
196 192 return unless @problem
197 193
198 194 @by_lang = {} #aggregrate by language
199 195
200 196 range =65
201 197 @histogram = { data: Array.new(range,0), summary: {} }
202 198 @summary = {count: 0, solve: 0, attempt: 0}
203 199 user = Hash.new(0)
204 200 Submission.where(problem_id: @problem.id).find_each do |sub|
205 201 #histogram
206 202 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
207 203 @histogram[:data][d.to_i] += 1 if d < range
208 204
209 205 next unless sub.points
210 206 @summary[:count] += 1
211 207 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
212 208
213 209 lang = Language.find_by_id(sub.language_id)
214 210 next unless lang
215 211 next unless sub.points >= @problem.full_score
216 212
217 213 #initialize
218 214 unless @by_lang.has_key?(lang.pretty_name)
219 215 @by_lang[lang.pretty_name] = {
220 216 runtime: { avail: false, value: 2**30-1 },
221 217 memory: { avail: false, value: 2**30-1 },
222 218 length: { avail: false, value: 2**30-1 },
223 219 first: { avail: false, value: DateTime.new(3000,1,1) }
224 220 }
225 221 end
226 222
227 223 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
228 224 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
229 225 end
230 226
231 227 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
232 228 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
233 229 end
234 230
235 231 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and sub.user and
236 232 !sub.user.admin?
237 233 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
238 234 end
239 235
240 236 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
241 237 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
242 238 end
243 239 end
244 240
245 241 #process user_id
246 242 @by_lang.each do |lang,prop|
247 243 prop.each do |k,v|
248 244 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
249 245 end
250 246 end
251 247
252 248 #sum into best
253 249 if @by_lang and @by_lang.first
254 250 @best = @by_lang.first[1].clone
255 251 @by_lang.each do |lang,prop|
256 252 if @best[:runtime][:value] >= prop[:runtime][:value]
257 253 @best[:runtime] = prop[:runtime]
258 254 @best[:runtime][:lang] = lang
259 255 end
260 256 if @best[:memory][:value] >= prop[:memory][:value]
261 257 @best[:memory] = prop[:memory]
262 258 @best[:memory][:lang] = lang
263 259 end
264 260 if @best[:length][:value] >= prop[:length][:value]
265 261 @best[:length] = prop[:length]
266 262 @best[:length][:lang] = lang
267 263 end
268 264 if @best[:first][:value] >= prop[:first][:value]
269 265 @best[:first] = prop[:first]
270 266 @best[:first][:lang] = lang
271 267 end
272 268 end
273 269 end
274 270
275 271 @histogram[:summary][:max] = [@histogram[:data].max,1].max
276 272 @summary[:attempt] = user.count
277 273 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
278 274 end
279 275
280 276 def stuck #report struggling user,problem
281 277 # init
282 278 user,problem = nil
283 279 solve = true
284 280 tries = 0
285 281 @struggle = Array.new
286 282 record = {}
287 283 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
288 284 next unless sub.problem and sub.user
289 285 if user != sub.user_id or problem != sub.problem_id
290 286 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
291 287 record = {user: sub.user, problem: sub.problem}
292 288 user,problem = sub.user_id, sub.problem_id
293 289 solve = false
294 290 tries = 0
295 291 end
296 292 if sub.points >= sub.problem.full_score
297 293 solve = true
298 294 else
299 295 tries += 1
300 296 end
301 297 end
302 298 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
303 299 @struggle = @struggle[0..50]
304 300 end
305 301
306 302
307 303 def multiple_login
308 304 #user with multiple IP
309 305 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
310 306 last,count = 0,0
311 307 first = 0
312 308 @users = []
313 309 raw.each do |r|
314 310 if last != r.user.login
315 311 count = 1
316 312 last = r.user.login
317 313 first = r
318 314 else
319 315 @users << first if count == 1
320 316 @users << r
321 317 count += 1
322 318 end
323 319 end
324 320
325 321 #IP with multiple user
326 322 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
327 323 last,count = 0,0
328 324 first = 0
329 325 @ip = []
330 326 raw.each do |r|
331 327 if last != r.ip_address
332 328 count = 1
333 329 last = r.ip_address
334 330 first = r
335 331 else
336 332 @ip << first if count == 1
337 333 @ip << r
338 334 count += 1
339 335 end
340 336 end
341 337 end
342 338
343 339 def cheat_report
344 340 date_and_time = '%Y-%m-%d %H:%M'
345 341 begin
346 342 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
347 343 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
348 344 rescue
349 345 @since_time = Time.zone.now.ago( 90.minutes)
350 346 end
351 347 begin
352 348 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
353 349 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
354 350 rescue
355 351 @until_time = Time.zone.now
356 352 end
357 353
358 354 #multi login
359 355 @ml = Login.joins(:user).where("logins.created_at >= ? and logins.created_at <= ?",@since_time,@until_time).select('users.login,count(distinct ip_address) as count,users.full_name').group("users.id").having("count > 1")
360 356
361 357 st = <<-SQL
362 358 SELECT l2.*
363 359 FROM logins l2 INNER JOIN
364 360 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
365 361 FROM logins l
366 362 INNER JOIN users u ON l.user_id = u.id
367 363 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
368 364 GROUP BY u.id
369 365 HAVING count > 1
370 366 ) ml ON l2.user_id = ml.id
371 367 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
372 368 UNION
373 369 SELECT l2.*
374 370 FROM logins l2 INNER JOIN
375 371 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
376 372 FROM logins l
377 373 INNER JOIN users u ON l.user_id = u.id
378 374 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
379 375 GROUP BY l.ip_address
380 376 HAVING count > 1
381 377 ) ml on ml.ip_address = l2.ip_address
382 378 INNER JOIN users u ON l2.user_id = u.id
383 379 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
384 380 ORDER BY ip_address,created_at
385 381 SQL
386 382 @mld = Login.find_by_sql(st)
387 383
388 384 st = <<-SQL
389 385 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
390 386 FROM submissions s INNER JOIN
391 387 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
392 388 FROM logins l
393 389 INNER JOIN users u ON l.user_id = u.id
394 390 WHERE l.created_at >= ? and l.created_at <= ?
395 391 GROUP BY u.id
396 392 HAVING count > 1
397 393 ) ml ON s.user_id = ml.id
398 394 WHERE s.submitted_at >= ? and s.submitted_at <= ?
399 395 UNION
400 396 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
401 397 FROM submissions s INNER JOIN
402 398 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
403 399 FROM logins l
404 400 INNER JOIN users u ON l.user_id = u.id
405 401 WHERE l.created_at >= ? and l.created_at <= ?
406 402 GROUP BY l.ip_address
407 403 HAVING count > 1
408 404 ) ml on ml.ip_address = s.ip_address
409 405 WHERE s.submitted_at >= ? and s.submitted_at <= ?
410 406 ORDER BY ip_address,submitted_at
411 407 SQL
412 408 @subs = Submission.joins(:problem).find_by_sql([st,@since_time,@until_time,
413 409 @since_time,@until_time,
414 410 @since_time,@until_time,
415 411 @since_time,@until_time])
416 412
417 413 end
418 414
419 415 def cheat_scruntinize
420 416 #convert date & time
421 417 date_and_time = '%Y-%m-%d %H:%M'
422 418 begin
423 419 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
424 420 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
425 421 rescue
426 422 @since_time = Time.zone.now.ago( 90.minutes)
427 423 end
428 424 begin
429 425 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
430 426 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
431 427 rescue
432 428 @until_time = Time.zone.now
433 429 end
434 430
435 431 #convert sid
436 432 @sid = params[:SID].split(/[,\s]/) if params[:SID]
437 433 unless @sid and @sid.size > 0
438 434 return
439 435 redirect_to actoin: :cheat_scruntinize
440 436 flash[:notice] = 'Please enter at least 1 student id'
441 437 end
442 438 mark = Array.new(@sid.size,'?')
443 439 condition = "(u.login = " + mark.join(' OR u.login = ') + ')'
444 440
445 441 @st = <<-SQL
446 442 SELECT l.created_at as submitted_at ,-1 as id,u.login,u.full_name,l.ip_address,"" as problem_id,"" as points,l.user_id
447 443 FROM logins l INNER JOIN users u on l.user_id = u.id
448 444 WHERE l.created_at >= ? AND l.created_at <= ? AND #{condition}
449 445 UNION
450 446 SELECT s.submitted_at,s.id,u.login,u.full_name,s.ip_address,s.problem_id,s.points,s.user_id
451 447 FROM submissions s INNER JOIN users u ON s.user_id = u.id
452 448 WHERE s.submitted_at >= ? AND s.submitted_at <= ? AND #{condition}
453 449 ORDER BY submitted_at
454 450 SQL
455 451
456 452 p = [@st,@since_time,@until_time] + @sid + [@since_time,@until_time] + @sid
457 453 @logs = Submission.joins(:problem).find_by_sql(p)
458 454
459 455
460 456
461 457
462 458
463 459 end
464 460
465 461 protected
466 462
467 463 def calculate_max_score(problems, users,since_id,until_id, get_last_score = false)
468 464 #scorearray[i] = user #i's user stat where i is the index (not id)
469 465 scorearray = Array.new
470 466 users.each do |u|
471 467 ustat = Array.new
472 468 ustat[0] = u
473 469 problems.each do |p|
474 470 unless get_last_score
475 471 #get max score
476 472 max_points = 0
477 473 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
478 474 max_points = sub.points if sub and sub.points and (sub.points > max_points)
479 475 end
480 476 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
481 477 else
482 478 #get latest score
483 479 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
484 480 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
485 481 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
486 482 else
487 483 ustat << [0,false]
488 484 end
489 485 end
490 486 end
491 487 scorearray << ustat
492 488 end
493 489 return scorearray
494 490 end
495 491
496 492 def gen_csv_from_scorearray(scorearray,problem)
497 493 CSV.generate do |csv|
498 494 #add header
499 495 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
500 496 problem.each { |p| header << p.name }
501 497 header += ['Total','Passed']
502 498 csv << header
503 499 #add data
504 500 scorearray.each do |sc|
505 501 total = num_passed = 0
506 502 row = Array.new
507 503 sc.each_index do |i|
508 504 if i == 0
509 505 row << sc[i].login
510 506 row << sc[i].full_name
511 507 row << sc[i].activated
512 508 row << (sc[i].try(:contest_stat).try(:started_at)!=nil ? 'yes' : 'no')
513 509 row << sc[i].contests.collect {|c| c.name}.join(', ')
514 510 else
515 511 row << sc[i][0]
516 512 total += sc[i][0]
517 513 num_passed += 1 if sc[i][1]
518 514 end
519 515 end
520 516 row << total
521 517 row << num_passed
522 518 csv << row
523 519 end
524 520 end
525 521 end
526 522
527 523 end
@@ -1,204 +1,206
1 1 Rails.application.routes.draw do
2 2 resources :tags
3 3 get "sources/direct_edit"
4 4
5 5 root :to => 'main#login'
6 6
7 7 #logins
8 8 match 'login/login', to: 'login#login', via: [:get,:post]
9 9
10 10 resources :contests
11 11 resources :sites
12 12 resources :test
13 13
14 14 resources :messages do
15 15 member do
16 16 get 'hide'
17 17 post 'reply'
18 18 end
19 19 collection do
20 20 get 'console'
21 21 get 'list_all'
22 22 end
23 23 end
24 24
25 25 resources :announcements do
26 26 member do
27 27 get 'toggle','toggle_front'
28 28 end
29 29 end
30 30
31 31 resources :problems do
32 32 member do
33 33 get 'toggle'
34 34 get 'toggle_test'
35 35 get 'toggle_view_testcase'
36 36 get 'stat'
37 37 end
38 38 collection do
39 39 get 'turn_all_off'
40 40 get 'turn_all_on'
41 41 get 'import'
42 42 get 'manage'
43 43 get 'quick_create'
44 44 post 'do_manage'
45 45 post 'do_import'
46 46 end
47 47 end
48 48
49 49 resources :groups do
50 50 member do
51 51 post 'add_user', to: 'groups#add_user', as: 'add_user'
52 52 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
53 53 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
54 54 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
55 55 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
56 56 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
57 57 end
58 58 collection do
59 59
60 60 end
61 61 end
62 62
63 63 resources :testcases, only: [] do
64 64 member do
65 65 get 'download_input'
66 66 get 'download_sol'
67 67 end
68 68 collection do
69 69 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
70 70 end
71 71 end
72 72
73 73 resources :grader_configuration, controller: 'configurations' do
74 74 collection do
75 75 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
76 76 end
77 77 end
78 78
79 79 resources :users do
80 80 member do
81 81 get 'toggle_activate', 'toggle_enable'
82 82 get 'stat'
83 83 end
84 84 collection do
85 85 get 'profile'
86 86 post 'chg_passwd'
87 87 end
88 88 end
89 89
90 90 resources :submissions do
91 91 member do
92 92 get 'download'
93 93 get 'compiler_msg'
94 94 get 'rejudge'
95 95 get 'source'
96 96 end
97 97 collection do
98 98 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
99 99 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
100 100 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
101 101 end
102 102 end
103 103
104 104
105 105 #user admin
106 106 resources :user_admin do
107 107 collection do
108 108 match 'bulk_manage', via: [:get, :post]
109 109 get 'bulk_mail'
110 110 get 'user_stat'
111 111 get 'import'
112 112 get 'new_list'
113 113 get 'admin'
114 114 get 'active'
115 115 get 'mass_mailing'
116 116 get 'revoke_admin'
117 117 post 'grant_admin'
118 118 match 'create_from_list', via: [:get, :post]
119 119 match 'random_all_passwords', via: [:get, :post]
120 120 end
121 121 member do
122 122 get 'clear_last_ip'
123 123 end
124 124 end
125 125
126 126 resources :contest_management, only: [:index] do
127 127 collection do
128 128 get 'user_stat'
129 129 get 'clear_stat'
130 130 get 'clear_all_stat'
131 131 get 'change_contest_mode'
132 132 end
133 133 end
134 134
135 135 #get 'user_admin', to: 'user_admin#index'
136 136 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
137 137 #post 'user_admin', to: 'user_admin#create'
138 138 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
139 139
140 140 #singular resource
141 141 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
142 142 #report
143 143 resource :report, only: [], controller: 'report' do
144 144 get 'login'
145 145 get 'multiple_login'
146 146 get 'problem_hof(/:id)', action: 'problem_hof', as: 'problem_hof'
147 147 get 'current_score(/:group_id)', action: 'current_score', as: 'current_score'
148 148 get 'max_score'
149 149 post 'show_max_score'
150 150 get 'stuck'
151 151 get 'cheat_report'
152 152 post 'cheat_report'
153 153 get 'cheat_scruntinize'
154 154 post 'cheat_scruntinize'
155 + get 'submission'
156 + post 'submission_query'
155 157 end
156 158 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
157 159 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
158 160 #get "report/login"
159 161 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
160 162 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
161 163
162 164 resource :main, only: [], controller: 'main' do
163 165 get 'login'
164 166 get 'logout'
165 167 get 'list'
166 168 get 'submission(/:id)', action: 'submission', as: 'main_submission'
167 169 get 'announcements'
168 170 get 'help'
169 171 post 'submit'
170 172 end
171 173 #main
172 174 #get "main/list"
173 175 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
174 176 #post 'main/submit', to: 'main#submit'
175 177 #get 'main/announcements', to: 'main#announcements'
176 178
177 179
178 180 #
179 181 get 'tasks/view/:file.:ext' => 'tasks#view'
180 182 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
181 183 get 'heartbeat/:id/edit' => 'heartbeat#edit'
182 184
183 185 #grader
184 186 get 'graders/list', to: 'graders#list', as: 'grader_list'
185 187 namespace :graders do
186 188 get 'task/:id/:type', action: 'task', as: 'task'
187 189 get 'view/:id/:type', action: 'view', as: 'view'
188 190 get 'clear/:id', action: 'clear', as: 'clear'
189 191 get 'stop'
190 192 get 'stop_all'
191 193 get 'clear_all'
192 194 get 'clear_terminated'
193 195 get 'start_grading'
194 196 get 'start_exam'
195 197
196 198 end
197 199
198 200
199 201 # See how all your routes lay out with "rake routes"
200 202
201 203 # This is a legacy wild controller route that's not recommended for RESTful applications.
202 204 # Note: This route will make all actions in every controller accessible via GET requests.
203 205 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
204 206 end
@@ -1,307 +1,321
1 + # encoding: UTF-8
1 2 # This file is auto-generated from the current state of the database. Instead
2 3 # of editing this file, please use the migrations feature of Active Record to
3 4 # incrementally modify your database, and then regenerate this schema definition.
4 5 #
5 6 # Note that this schema.rb definition is the authoritative source for your
6 7 # database schema. If you need to create the application database on another
7 8 # system, you should be using db:schema:load, not running all the migrations
8 9 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 10 # you'll amass, the slower it'll run and the greater likelihood for issues).
10 11 #
11 12 # It's strongly recommended that you check this file into your version control system.
12 13
13 - ActiveRecord::Schema.define(version: 2018_06_12_102327) do
14 + ActiveRecord::Schema.define(version: 20180612102327) do
14 15
15 - create_table "announcements", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
16 - t.string "author"
17 - t.text "body"
18 - t.boolean "published"
16 + create_table "announcements", force: :cascade do |t|
17 + t.string "author", limit: 255
18 + t.text "body", limit: 65535
19 + t.boolean "published"
19 20 t.datetime "created_at"
20 21 t.datetime "updated_at"
21 - t.boolean "frontpage", default: false
22 - t.boolean "contest_only", default: false
23 - t.string "title"
24 - t.string "notes"
22 + t.boolean "frontpage", default: false
23 + t.boolean "contest_only", default: false
24 + t.string "title", limit: 255
25 + t.string "notes", limit: 255
25 26 end
26 27
27 - create_table "contests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
28 - t.string "title"
29 - t.boolean "enabled"
28 + create_table "contests", force: :cascade do |t|
29 + t.string "title", limit: 255
30 + t.boolean "enabled"
30 31 t.datetime "created_at"
31 32 t.datetime "updated_at"
32 - t.string "name"
33 + t.string "name", limit: 255
33 34 end
34 35
35 - create_table "contests_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
36 - t.integer "contest_id"
37 - t.integer "problem_id"
36 + create_table "contests_problems", id: false, force: :cascade do |t|
37 + t.integer "contest_id", limit: 4
38 + t.integer "problem_id", limit: 4
38 39 end
39 40
40 - create_table "contests_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
41 - t.integer "contest_id"
42 - t.integer "user_id"
41 + create_table "contests_users", id: false, force: :cascade do |t|
42 + t.integer "contest_id", limit: 4
43 + t.integer "user_id", limit: 4
43 44 end
44 45
45 - create_table "countries", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
46 - t.string "name"
46 + create_table "countries", force: :cascade do |t|
47 + t.string "name", limit: 255
47 48 t.datetime "created_at"
48 49 t.datetime "updated_at"
49 50 end
50 51
51 - create_table "descriptions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
52 - t.text "body"
53 - t.boolean "markdowned"
52 + create_table "descriptions", force: :cascade do |t|
53 + t.text "body", limit: 65535
54 + t.boolean "markdowned"
54 55 t.datetime "created_at"
55 56 t.datetime "updated_at"
56 57 end
57 58
58 - create_table "grader_configurations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
59 - t.string "key"
60 - t.string "value_type"
61 - t.string "value"
59 + create_table "grader_configurations", force: :cascade do |t|
60 + t.string "key", limit: 255
61 + t.string "value_type", limit: 255
62 + t.string "value", limit: 255
62 63 t.datetime "created_at"
63 64 t.datetime "updated_at"
64 - t.text "description"
65 + t.text "description", limit: 65535
65 66 end
66 67
67 - create_table "grader_processes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
68 - t.string "host"
69 - t.integer "pid"
70 - t.string "mode"
71 - t.boolean "active"
68 + create_table "grader_processes", force: :cascade do |t|
69 + t.string "host", limit: 255
70 + t.integer "pid", limit: 4
71 + t.string "mode", limit: 255
72 + t.boolean "active"
72 73 t.datetime "created_at"
73 74 t.datetime "updated_at"
74 - t.integer "task_id"
75 - t.string "task_type"
76 - t.boolean "terminated"
77 - t.index ["host", "pid"], name: "index_grader_processes_on_host_and_pid"
75 + t.integer "task_id", limit: 4
76 + t.string "task_type", limit: 255
77 + t.boolean "terminated"
78 78 end
79 79
80 - create_table "groups", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
81 - t.string "name"
82 - t.string "description"
80 + add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_host_and_pid", using: :btree
81 +
82 + create_table "groups", force: :cascade do |t|
83 + t.string "name", limit: 255
84 + t.string "description", limit: 255
83 85 end
84 86
85 - create_table "groups_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
86 - t.integer "problem_id", null: false
87 - t.integer "group_id", null: false
88 - t.index ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id"
87 + create_table "groups_problems", id: false, force: :cascade do |t|
88 + t.integer "problem_id", limit: 4, null: false
89 + t.integer "group_id", limit: 4, null: false
89 90 end
90 91
91 - create_table "groups_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
92 - t.integer "group_id", null: false
93 - t.integer "user_id", null: false
94 - t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
92 + add_index "groups_problems", ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id", using: :btree
93 +
94 + create_table "groups_users", id: false, force: :cascade do |t|
95 + t.integer "group_id", limit: 4, null: false
96 + t.integer "user_id", limit: 4, null: false
95 97 end
96 98
97 - create_table "heart_beats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
98 - t.integer "user_id"
99 - t.string "ip_address"
99 + add_index "groups_users", ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id", using: :btree
100 +
101 + create_table "heart_beats", force: :cascade do |t|
102 + t.integer "user_id", limit: 4
103 + t.string "ip_address", limit: 255
100 104 t.datetime "created_at"
101 105 t.datetime "updated_at"
102 - t.string "status"
103 - t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
106 + t.string "status", limit: 255
104 107 end
105 108
106 - create_table "languages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
107 - t.string "name", limit: 10
108 - t.string "pretty_name"
109 - t.string "ext", limit: 10
110 - t.string "common_ext"
109 + add_index "heart_beats", ["updated_at"], name: "index_heart_beats_on_updated_at", using: :btree
110 +
111 + create_table "languages", force: :cascade do |t|
112 + t.string "name", limit: 10
113 + t.string "pretty_name", limit: 255
114 + t.string "ext", limit: 10
115 + t.string "common_ext", limit: 255
111 116 end
112 117
113 - create_table "logins", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
114 - t.integer "user_id"
115 - t.string "ip_address"
118 + create_table "logins", force: :cascade do |t|
119 + t.integer "user_id", limit: 4
120 + t.string "ip_address", limit: 255
116 121 t.datetime "created_at"
117 122 t.datetime "updated_at"
118 123 end
119 124
120 - create_table "messages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
121 - t.integer "sender_id"
122 - t.integer "receiver_id"
123 - t.integer "replying_message_id"
124 - t.text "body"
125 - t.boolean "replied"
125 + create_table "messages", force: :cascade do |t|
126 + t.integer "sender_id", limit: 4
127 + t.integer "receiver_id", limit: 4
128 + t.integer "replying_message_id", limit: 4
129 + t.text "body", limit: 65535
130 + t.boolean "replied"
126 131 t.datetime "created_at"
127 132 t.datetime "updated_at"
128 133 end
129 134
130 - create_table "problems", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
131 - t.string "name", limit: 30
132 - t.string "full_name"
133 - t.integer "full_score"
134 - t.date "date_added"
135 + create_table "problems", force: :cascade do |t|
136 + t.string "name", limit: 30
137 + t.string "full_name", limit: 255
138 + t.integer "full_score", limit: 4
139 + t.date "date_added"
135 140 t.boolean "available"
136 - t.string "url"
137 - t.integer "description_id"
141 + t.string "url", limit: 255
142 + t.integer "description_id", limit: 4
138 143 t.boolean "test_allowed"
139 144 t.boolean "output_only"
140 - t.string "description_filename"
145 + t.string "description_filename", limit: 255
141 146 t.boolean "view_testcase"
142 147 end
143 148
144 - create_table "problems_tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
145 - t.integer "problem_id"
146 - t.integer "tag_id"
147 - t.index ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true
148 - t.index ["problem_id"], name: "index_problems_tags_on_problem_id"
149 - t.index ["tag_id"], name: "index_problems_tags_on_tag_id"
149 + create_table "problems_tags", force: :cascade do |t|
150 + t.integer "problem_id", limit: 4
151 + t.integer "tag_id", limit: 4
150 152 end
151 153
152 - create_table "rights", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
153 - t.string "name"
154 - t.string "controller"
155 - t.string "action"
154 + add_index "problems_tags", ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true, using: :btree
155 + add_index "problems_tags", ["problem_id"], name: "index_problems_tags_on_problem_id", using: :btree
156 + add_index "problems_tags", ["tag_id"], name: "index_problems_tags_on_tag_id", using: :btree
157 +
158 + create_table "rights", force: :cascade do |t|
159 + t.string "name", limit: 255
160 + t.string "controller", limit: 255
161 + t.string "action", limit: 255
156 162 end
157 163
158 - create_table "rights_roles", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
159 - t.integer "right_id"
160 - t.integer "role_id"
161 - t.index ["role_id"], name: "index_rights_roles_on_role_id"
164 + create_table "rights_roles", id: false, force: :cascade do |t|
165 + t.integer "right_id", limit: 4
166 + t.integer "role_id", limit: 4
162 167 end
163 168
164 - create_table "roles", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
165 - t.string "name"
169 + add_index "rights_roles", ["role_id"], name: "index_rights_roles_on_role_id", using: :btree
170 +
171 + create_table "roles", force: :cascade do |t|
172 + t.string "name", limit: 255
166 173 end
167 174
168 - create_table "roles_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
169 - t.integer "role_id"
170 - t.integer "user_id"
171 - t.index ["user_id"], name: "index_roles_users_on_user_id"
175 + create_table "roles_users", id: false, force: :cascade do |t|
176 + t.integer "role_id", limit: 4
177 + t.integer "user_id", limit: 4
172 178 end
173 179
174 - create_table "sessions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
175 - t.string "session_id"
176 - t.text "data"
180 + add_index "roles_users", ["user_id"], name: "index_roles_users_on_user_id", using: :btree
181 +
182 + create_table "sessions", force: :cascade do |t|
183 + t.string "session_id", limit: 255
184 + t.text "data", limit: 65535
177 185 t.datetime "updated_at"
178 - t.index ["session_id"], name: "index_sessions_on_session_id"
179 - t.index ["updated_at"], name: "index_sessions_on_updated_at"
180 186 end
181 187
182 - create_table "sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
183 - t.string "name"
184 - t.boolean "started"
188 + add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", using: :btree
189 + add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at", using: :btree
190 +
191 + create_table "sites", force: :cascade do |t|
192 + t.string "name", limit: 255
193 + t.boolean "started"
185 194 t.datetime "start_time"
186 195 t.datetime "created_at"
187 196 t.datetime "updated_at"
188 - t.integer "country_id"
189 - t.string "password"
197 + t.integer "country_id", limit: 4
198 + t.string "password", limit: 255
190 199 end
191 200
192 - create_table "submission_view_logs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
193 - t.integer "user_id"
194 - t.integer "submission_id"
201 + create_table "submission_view_logs", force: :cascade do |t|
202 + t.integer "user_id", limit: 4
203 + t.integer "submission_id", limit: 4
195 204 t.datetime "created_at"
196 205 t.datetime "updated_at"
197 206 end
198 207
199 - create_table "submissions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
200 - t.integer "user_id"
201 - t.integer "problem_id"
202 - t.integer "language_id"
203 - t.text "source", limit: 16777215
204 - t.binary "binary"
208 + create_table "submissions", force: :cascade do |t|
209 + t.integer "user_id", limit: 4
210 + t.integer "problem_id", limit: 4
211 + t.integer "language_id", limit: 4
212 + t.text "source", limit: 16777215
213 + t.binary "binary", limit: 65535
205 214 t.datetime "submitted_at"
206 215 t.datetime "compiled_at"
207 - t.text "compiler_message"
216 + t.text "compiler_message", limit: 65535
208 217 t.datetime "graded_at"
209 - t.integer "points"
210 - t.text "grader_comment"
211 - t.integer "number"
212 - t.string "source_filename"
213 - t.float "max_runtime"
214 - t.integer "peak_memory"
215 - t.integer "effective_code_length"
216 - t.string "ip_address"
217 - t.index ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true
218 - t.index ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id"
218 + t.integer "points", limit: 4
219 + t.text "grader_comment", limit: 65535
220 + t.integer "number", limit: 4
221 + t.string "source_filename", limit: 255
222 + t.float "max_runtime", limit: 24
223 + t.integer "peak_memory", limit: 4
224 + t.integer "effective_code_length", limit: 4
225 + t.string "ip_address", limit: 255
219 226 end
220 227
221 - create_table "tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
222 - t.string "name", null: false
223 - t.text "description"
224 - t.boolean "public"
225 - t.datetime "created_at", null: false
226 - t.datetime "updated_at", null: false
228 + add_index "submissions", ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true, using: :btree
229 + add_index "submissions", ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id", using: :btree
230 +
231 + create_table "tags", force: :cascade do |t|
232 + t.string "name", limit: 255, null: false
233 + t.text "description", limit: 65535
234 + t.boolean "public"
235 + t.datetime "created_at", null: false
236 + t.datetime "updated_at", null: false
227 237 end
228 238
229 - create_table "tasks", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
230 - t.integer "submission_id"
239 + create_table "tasks", force: :cascade do |t|
240 + t.integer "submission_id", limit: 4
231 241 t.datetime "created_at"
232 - t.integer "status"
242 + t.integer "status", limit: 4
233 243 t.datetime "updated_at"
234 - t.index ["submission_id"], name: "index_tasks_on_submission_id"
235 244 end
236 245
237 - create_table "test_pairs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
238 - t.integer "problem_id"
239 - t.text "input", limit: 16777215
240 - t.text "solution", limit: 16777215
246 + add_index "tasks", ["submission_id"], name: "index_tasks_on_submission_id", using: :btree
247 +
248 + create_table "test_pairs", force: :cascade do |t|
249 + t.integer "problem_id", limit: 4
250 + t.text "input", limit: 16777215
251 + t.text "solution", limit: 16777215
241 252 t.datetime "created_at"
242 253 t.datetime "updated_at"
243 254 end
244 255
245 - create_table "test_requests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
246 - t.integer "user_id"
247 - t.integer "problem_id"
248 - t.integer "submission_id"
249 - t.string "input_file_name"
250 - t.string "output_file_name"
251 - t.string "running_stat"
252 - t.integer "status"
256 + create_table "test_requests", force: :cascade do |t|
257 + t.integer "user_id", limit: 4
258 + t.integer "problem_id", limit: 4
259 + t.integer "submission_id", limit: 4
260 + t.string "input_file_name", limit: 255
261 + t.string "output_file_name", limit: 255
262 + t.string "running_stat", limit: 255
263 + t.integer "status", limit: 4
253 264 t.datetime "updated_at"
254 265 t.datetime "submitted_at"
255 266 t.datetime "compiled_at"
256 - t.text "compiler_message"
267 + t.text "compiler_message", limit: 65535
257 268 t.datetime "graded_at"
258 - t.string "grader_comment"
269 + t.string "grader_comment", limit: 255
259 270 t.datetime "created_at"
260 - t.float "running_time"
261 - t.string "exit_status"
262 - t.integer "memory_usage"
263 - t.index ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id"
271 + t.float "running_time", limit: 24
272 + t.string "exit_status", limit: 255
273 + t.integer "memory_usage", limit: 4
264 274 end
265 275
266 - create_table "testcases", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
267 - t.integer "problem_id"
268 - t.integer "num"
269 - t.integer "group"
270 - t.integer "score"
271 - t.text "input", limit: 4294967295
272 - t.text "sol", limit: 4294967295
276 + add_index "test_requests", ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id", using: :btree
277 +
278 + create_table "testcases", force: :cascade do |t|
279 + t.integer "problem_id", limit: 4
280 + t.integer "num", limit: 4
281 + t.integer "group", limit: 4
282 + t.integer "score", limit: 4
283 + t.text "input", limit: 4294967295
284 + t.text "sol", limit: 4294967295
273 285 t.datetime "created_at"
274 286 t.datetime "updated_at"
275 - t.index ["problem_id"], name: "index_testcases_on_problem_id"
276 287 end
277 288
278 - create_table "user_contest_stats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
279 - t.integer "user_id"
289 + add_index "testcases", ["problem_id"], name: "index_testcases_on_problem_id", using: :btree
290 +
291 + create_table "user_contest_stats", force: :cascade do |t|
292 + t.integer "user_id", limit: 4
280 293 t.datetime "started_at"
281 294 t.datetime "created_at"
282 295 t.datetime "updated_at"
283 - t.boolean "forced_logout"
296 + t.boolean "forced_logout"
284 297 end
285 298
286 - create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
287 - t.string "login", limit: 50
288 - t.string "full_name"
289 - t.string "hashed_password"
290 - t.string "salt", limit: 5
291 - t.string "alias"
292 - t.string "email"
293 - t.integer "site_id"
294 - t.integer "country_id"
295 - t.boolean "activated", default: false
299 + create_table "users", force: :cascade do |t|
300 + t.string "login", limit: 50
301 + t.string "full_name", limit: 255
302 + t.string "hashed_password", limit: 255
303 + t.string "salt", limit: 5
304 + t.string "alias", limit: 255
305 + t.string "email", limit: 255
306 + t.integer "site_id", limit: 4
307 + t.integer "country_id", limit: 4
308 + t.boolean "activated", default: false
296 309 t.datetime "created_at"
297 310 t.datetime "updated_at"
298 - t.string "section"
299 - t.boolean "enabled", default: true
300 - t.string "remark"
301 - t.string "last_ip"
302 - t.index ["login"], name: "index_users_on_login", unique: true
311 + t.string "section", limit: 255
312 + t.boolean "enabled", default: true
313 + t.string "remark", limit: 255
314 + t.string "last_ip", limit: 255
303 315 end
304 316
317 + add_index "users", ["login"], name: "index_users_on_login", unique: true, using: :btree
318 +
305 319 add_foreign_key "problems_tags", "problems"
306 320 add_foreign_key "problems_tags", "tags"
307 321 end
deleted file
You need to be logged in to leave comments. Login now