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

r623:0e5f52b4f443 - - 101 files changed: 461 inserted, 2007 deleted

@@ -0,0 +1,3
1 + :javascript
2 + $("#compiler_msg").html("#{j @submissionhcompiler_msg}")
3 +
@@ -0,0 +1,8
1 + development:
2 + secret_key_base: '444f426d08add8e2d7cbd76e2057e521e06091231eb4d5472af6ba5654ea1124ce6a636f549be6827ce09561c314181226ad840d44e4677e1077942ee0dc82bd'
3 +
4 + test:
5 + secret_key_base: 'd52f411b06a79cc9f56d92e10d27e670cf0f0c3357e7caf9018ec23091b5c452ea9266c03a5c9e37b72c358702d4d460e957f90dcc553c9fc73a98adb520e781'
6 +
7 + production:
8 + secret_key_base: '7f85485d3d652fc6336dfe3cd98917d9bd7a323b32096bf7635d26b98ccd0480816cc2d12b5c10805cecf7d8fb322104e2bda71eb60dd871c5c537e56a063038'
@@ -0,0 +1,13
1 + require 'test_helper'
2 +
3 + class LoginTest < ActionDispatch::IntegrationTest
4 + # test "the truth" do
5 + # assert true
6 + # end
7 +
8 + test "login with valid information" do
9 + get root_path
10 + assert_response :success
11 +
12 + end
13 + end
@@ -1,80 +1,78
1 1 source 'https://rubygems.org'
2 2
3 - gem 'rails', '~>3.2'
3 + gem 'rails', '~>4.2.0'
4 + gem 'activerecord-session_store'
4 5
5 6 gem 'select2-rails'
6 7
7 8 # Bundle edge Rails instead:
8 9 # gem 'rails', :git => 'git://github.com/rails/rails.git'
9 10
10 11 gem 'mysql2'
12 + gem 'sqlite3'
11 13
12 14 # Gems used only for assets and not required
13 15 # in production environments by default.
14 - group :assets do
15 - gem 'sass-rails', '~> 3.2.6'
16 - gem 'coffee-rails', '~> 3.2.2'
16 + gem 'sass-rails'
17 + gem 'coffee-rails'
17 18
18 19 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
19 20 # gem 'therubyracer', :platforms => :ruby
20 21
21 22 gem 'uglifier'
22 - end
23 +
23 24
24 - gem 'prototype-rails'
25 + # gem 'prototype-rails'
25 26
26 27 # To use ActiveModel has_secure_password
27 28 # gem 'bcrypt-ruby', '~> 3.0.0'
28 29
29 30 # To use Jbuilder templates for JSON
30 31 # gem 'jbuilder'
31 32
32 33 # Use unicorn as the app server
33 34 # gem 'unicorn'
34 35
35 36 # Deploy with Capistrano
36 37 # gem 'capistrano'
37 38
38 39 # To use debugger
39 40 # gem 'debugger'
40 41 #
41 42
42 43 #in-place editor
43 44 gem 'best_in_place', '~> 3.0.1'
44 45
45 46 # jquery addition
46 47 gem 'jquery-rails'
47 - gem 'jquery-ui-sass-rails'
48 + gem 'jquery-ui-rails'
48 49 gem 'jquery-timepicker-addon-rails'
49 50 gem 'jquery-tablesorter'
50 51 gem 'jquery-countdown-rails'
51 52
52 53 #syntax highlighter
53 54 gem 'rouge'
54 55
55 56 #add bootstrap
56 57 gem 'bootstrap-sass', '~> 3.2.0'
57 58 gem 'bootstrap-switch-rails'
58 59 gem 'bootstrap-toggle-rails'
59 60 gem 'autoprefixer-rails'
60 61
61 62 #bootstrap sortable
62 63 gem 'momentjs-rails'
63 64 gem 'rails_bootstrap_sortable'
64 65
65 66 #ace editor
66 67 gem 'ace-rails-ap'
67 68
68 69 gem 'haml'
69 70 gem 'haml-rails'
70 71 gem 'mail'
71 72 gem 'rdiscount'
72 73 gem 'test-unit'
73 74 gem 'will_paginate', '~> 3.0.7'
74 75 gem 'dynamic_form'
75 76 gem 'in_place_editing'
76 77 gem 'verification', :git => 'https://github.com/sikachu/verification.git'
77 78
78 - group :test, :development do
79 - gem 'rspec-rails', '~> 2.99.0'
80 - end
@@ -1,208 +1,221
1 1 GIT
2 2 remote: https://github.com/sikachu/verification.git
3 - revision: 76eaf51b13276ecae54bd9cd115832595d2ff56d
3 + revision: ff31697b940d7b0e2ec65f08764215c96104e76d
4 4 specs:
5 5 verification (1.0.3)
6 - actionpack (>= 3.0.0, < 5.0)
7 - activesupport (>= 3.0.0, < 5.0)
6 + actionpack (>= 3.0.0, < 5.1)
7 + activesupport (>= 3.0.0, < 5.1)
8 8
9 9 GEM
10 10 remote: https://rubygems.org/
11 11 specs:
12 - ace-rails-ap (4.0.2)
13 - actionmailer (3.2.22.5)
14 - actionpack (= 3.2.22.5)
15 - mail (~> 2.5.4)
16 - actionpack (3.2.22.5)
17 - activemodel (= 3.2.22.5)
18 - activesupport (= 3.2.22.5)
19 - builder (~> 3.0.0)
12 + ace-rails-ap (4.1.1)
13 + actionmailer (4.2.7.1)
14 + actionpack (= 4.2.7.1)
15 + actionview (= 4.2.7.1)
16 + activejob (= 4.2.7.1)
17 + mail (~> 2.5, >= 2.5.4)
18 + rails-dom-testing (~> 1.0, >= 1.0.5)
19 + actionpack (4.2.7.1)
20 + actionview (= 4.2.7.1)
21 + activesupport (= 4.2.7.1)
22 + rack (~> 1.6)
23 + rack-test (~> 0.6.2)
24 + rails-dom-testing (~> 1.0, >= 1.0.5)
25 + rails-html-sanitizer (~> 1.0, >= 1.0.2)
26 + actionview (4.2.7.1)
27 + activesupport (= 4.2.7.1)
28 + builder (~> 3.1)
20 29 erubis (~> 2.7.0)
21 - journey (~> 1.0.4)
22 - rack (~> 1.4.5)
23 - rack-cache (~> 1.2)
24 - rack-test (~> 0.6.1)
25 - sprockets (~> 2.2.1)
26 - activemodel (3.2.22.5)
27 - activesupport (= 3.2.22.5)
28 - builder (~> 3.0.0)
29 - activerecord (3.2.22.5)
30 - activemodel (= 3.2.22.5)
31 - activesupport (= 3.2.22.5)
32 - arel (~> 3.0.2)
33 - tzinfo (~> 0.3.29)
34 - activeresource (3.2.22.5)
35 - activemodel (= 3.2.22.5)
36 - activesupport (= 3.2.22.5)
37 - activesupport (3.2.22.5)
38 - i18n (~> 0.6, >= 0.6.4)
39 - multi_json (~> 1.0)
40 - arel (3.0.3)
41 - autoprefixer-rails (6.0.3)
30 + rails-dom-testing (~> 1.0, >= 1.0.5)
31 + rails-html-sanitizer (~> 1.0, >= 1.0.2)
32 + activejob (4.2.7.1)
33 + activesupport (= 4.2.7.1)
34 + globalid (>= 0.3.0)
35 + activemodel (4.2.7.1)
36 + activesupport (= 4.2.7.1)
37 + builder (~> 3.1)
38 + activerecord (4.2.7.1)
39 + activemodel (= 4.2.7.1)
40 + activesupport (= 4.2.7.1)
41 + arel (~> 6.0)
42 + activerecord-session_store (1.0.0)
43 + actionpack (>= 4.0, < 5.1)
44 + activerecord (>= 4.0, < 5.1)
45 + multi_json (~> 1.11, >= 1.11.2)
46 + rack (>= 1.5.2, < 3)
47 + railties (>= 4.0, < 5.1)
48 + activesupport (4.2.7.1)
49 + i18n (~> 0.7)
50 + json (~> 1.7, >= 1.7.7)
51 + minitest (~> 5.1)
52 + thread_safe (~> 0.3, >= 0.3.4)
53 + tzinfo (~> 1.1)
54 + arel (6.0.4)
55 + autoprefixer-rails (6.6.0)
42 56 execjs
43 - json
44 57 best_in_place (3.0.3)
45 58 actionpack (>= 3.2)
46 59 railties (>= 3.2)
47 60 bootstrap-sass (3.2.0.2)
48 61 sass (~> 3.2)
49 62 bootstrap-switch-rails (3.3.3)
50 63 bootstrap-toggle-rails (2.2.1.0)
51 - builder (3.0.4)
52 - coffee-rails (3.2.2)
64 + builder (3.2.2)
65 + coffee-rails (4.2.1)
53 66 coffee-script (>= 2.2.0)
54 - railties (~> 3.2.0)
55 - coffee-script (2.3.0)
67 + railties (>= 4.0.0, < 5.2.x)
68 + coffee-script (2.4.1)
56 69 coffee-script-source
57 70 execjs
58 - coffee-script-source (1.9.0)
59 - diff-lcs (1.2.5)
71 + coffee-script-source (1.12.2)
72 + concurrent-ruby (1.0.4)
60 73 dynamic_form (1.1.4)
61 74 erubis (2.7.0)
62 - execjs (2.3.0)
63 - haml (4.0.6)
75 + execjs (2.7.0)
76 + globalid (0.3.7)
77 + activesupport (>= 4.1.0)
78 + haml (4.0.7)
64 79 tilt
65 - haml-rails (0.4)
66 - actionpack (>= 3.1, < 4.1)
67 - activesupport (>= 3.1, < 4.1)
68 - haml (>= 3.1, < 4.1)
69 - railties (>= 3.1, < 4.1)
70 - hike (1.2.3)
80 + haml-rails (0.9.0)
81 + actionpack (>= 4.0.1)
82 + activesupport (>= 4.0.1)
83 + haml (>= 4.0.6, < 5.0)
84 + html2haml (>= 1.0.1)
85 + railties (>= 4.0.1)
86 + html2haml (2.0.0)
87 + erubis (~> 2.7.0)
88 + haml (~> 4.0.0)
89 + nokogiri (~> 1.6.0)
90 + ruby_parser (~> 3.5)
71 91 i18n (0.7.0)
72 92 in_place_editing (1.2.0)
73 - journey (1.0.4)
74 93 jquery-countdown-rails (2.0.2)
75 - jquery-rails (3.1.2)
76 - railties (>= 3.0, < 5.0)
94 + jquery-rails (4.2.1)
95 + rails-dom-testing (>= 1, < 3)
96 + railties (>= 4.2.0)
77 97 thor (>= 0.14, < 2.0)
78 - jquery-tablesorter (1.13.4)
79 - railties (>= 3.1, < 5)
98 + jquery-tablesorter (1.23.3)
99 + railties (>= 3.2, < 6)
80 100 jquery-timepicker-addon-rails (1.4.1)
81 101 railties (>= 3.1)
82 - jquery-ui-rails (4.0.3)
83 - jquery-rails
84 - railties (>= 3.1.0)
85 - jquery-ui-sass-rails (4.0.3.0)
86 - jquery-rails
87 - jquery-ui-rails (= 4.0.3)
88 - railties (>= 3.1.0)
102 + jquery-ui-rails (6.0.1)
103 + railties (>= 3.2.16)
89 104 json (1.8.3)
90 - mail (2.5.4)
91 - mime-types (~> 1.16)
92 - treetop (~> 1.4.8)
93 - mime-types (1.25.1)
94 - momentjs-rails (2.11.1)
105 + loofah (2.0.3)
106 + nokogiri (>= 1.5.9)
107 + mail (2.6.4)
108 + mime-types (>= 1.16, < 4)
109 + mime-types (3.1)
110 + mime-types-data (~> 3.2015)
111 + mime-types-data (3.2016.0521)
112 + mini_portile2 (2.1.0)
113 + minitest (5.10.1)
114 + momentjs-rails (2.15.1)
95 115 railties (>= 3.1)
96 116 multi_json (1.12.1)
97 - mysql2 (0.3.20)
98 - polyglot (0.3.5)
99 - power_assert (0.2.2)
100 - prototype-rails (3.2.1)
101 - rails (~> 3.2)
102 - rack (1.4.7)
103 - rack-cache (1.6.1)
104 - rack (>= 0.4)
105 - rack-ssl (1.3.4)
106 - rack
117 + mysql2 (0.4.5)
118 + nokogiri (1.6.8.1)
119 + mini_portile2 (~> 2.1.0)
120 + power_assert (0.4.1)
121 + rack (1.6.5)
107 122 rack-test (0.6.3)
108 123 rack (>= 1.0)
109 - rails (3.2.22.5)
110 - actionmailer (= 3.2.22.5)
111 - actionpack (= 3.2.22.5)
112 - activerecord (= 3.2.22.5)
113 - activeresource (= 3.2.22.5)
114 - activesupport (= 3.2.22.5)
115 - bundler (~> 1.0)
116 - railties (= 3.2.22.5)
117 - rails_bootstrap_sortable (2.0.0)
118 - momentjs-rails (~> 2, >= 2.8.3)
119 - railties (3.2.22.5)
120 - actionpack (= 3.2.22.5)
121 - activesupport (= 3.2.22.5)
122 - rack-ssl (~> 1.3.2)
124 + rails (4.2.7.1)
125 + actionmailer (= 4.2.7.1)
126 + actionpack (= 4.2.7.1)
127 + actionview (= 4.2.7.1)
128 + activejob (= 4.2.7.1)
129 + activemodel (= 4.2.7.1)
130 + activerecord (= 4.2.7.1)
131 + activesupport (= 4.2.7.1)
132 + bundler (>= 1.3.0, < 2.0)
133 + railties (= 4.2.7.1)
134 + sprockets-rails
135 + rails-deprecated_sanitizer (1.0.3)
136 + activesupport (>= 4.2.0.alpha)
137 + rails-dom-testing (1.0.8)
138 + activesupport (>= 4.2.0.beta, < 5.0)
139 + nokogiri (~> 1.6)
140 + rails-deprecated_sanitizer (>= 1.0.1)
141 + rails-html-sanitizer (1.0.3)
142 + loofah (~> 2.0)
143 + rails_bootstrap_sortable (2.0.1)
144 + momentjs-rails (>= 2.8.3)
145 + railties (4.2.7.1)
146 + actionpack (= 4.2.7.1)
147 + activesupport (= 4.2.7.1)
123 148 rake (>= 0.8.7)
124 - rdoc (~> 3.4)
125 - thor (>= 0.14.6, < 2.0)
126 - rake (11.2.2)
127 - rdiscount (2.1.8)
128 - rdoc (3.12.2)
129 - json (~> 1.4)
130 - rouge (1.8.0)
131 - rspec-collection_matchers (1.1.2)
132 - rspec-expectations (>= 2.99.0.beta1)
133 - rspec-core (2.99.2)
134 - rspec-expectations (2.99.2)
135 - diff-lcs (>= 1.1.3, < 2.0)
136 - rspec-mocks (2.99.3)
137 - rspec-rails (2.99.0)
138 - actionpack (>= 3.0)
139 - activemodel (>= 3.0)
140 - activesupport (>= 3.0)
141 - railties (>= 3.0)
142 - rspec-collection_matchers
143 - rspec-core (~> 2.99.0)
144 - rspec-expectations (~> 2.99.0)
145 - rspec-mocks (~> 2.99.0)
146 - sass (3.4.11)
147 - sass-rails (3.2.6)
148 - railties (~> 3.2.0)
149 - sass (>= 3.1.10)
150 - tilt (~> 1.3)
151 - select2-rails (4.0.1)
149 + thor (>= 0.18.1, < 2.0)
150 + rake (12.0.0)
151 + rdiscount (2.2.0.1)
152 + rouge (2.0.7)
153 + ruby_parser (3.8.3)
154 + sexp_processor (~> 4.1)
155 + sass (3.4.23)
156 + sass-rails (5.0.6)
157 + railties (>= 4.0.0, < 6)
158 + sass (~> 3.1)
159 + sprockets (>= 2.8, < 4.0)
160 + sprockets-rails (>= 2.0, < 4.0)
161 + tilt (>= 1.1, < 3)
162 + select2-rails (4.0.3)
152 163 thor (~> 0.14)
153 - sprockets (2.2.3)
154 - hike (~> 1.2)
155 - multi_json (~> 1.0)
156 - rack (~> 1.0)
157 - tilt (~> 1.1, != 1.3.0)
158 - test-unit (3.0.9)
164 + sexp_processor (4.7.0)
165 + sprockets (3.7.1)
166 + concurrent-ruby (~> 1.0)
167 + rack (> 1, < 3)
168 + sprockets-rails (3.2.0)
169 + actionpack (>= 4.0)
170 + activesupport (>= 4.0)
171 + sprockets (>= 3.0.0)
172 + sqlite3 (1.3.12)
173 + test-unit (3.2.3)
159 174 power_assert
160 - thor (0.19.1)
161 - tilt (1.4.1)
162 - treetop (1.4.15)
163 - polyglot
164 - polyglot (>= 0.3.1)
165 - tzinfo (0.3.51)
166 - uglifier (2.7.0)
167 - execjs (>= 0.3.0)
168 - json (>= 1.8.0)
169 - will_paginate (3.0.7)
175 + thor (0.19.4)
176 + thread_safe (0.3.5)
177 + tilt (2.0.5)
178 + tzinfo (1.2.2)
179 + thread_safe (~> 0.1)
180 + uglifier (3.0.4)
181 + execjs (>= 0.3.0, < 3)
182 + will_paginate (3.0.12)
170 183
171 184 PLATFORMS
172 185 ruby
173 186
174 187 DEPENDENCIES
175 188 ace-rails-ap
189 + activerecord-session_store
176 190 autoprefixer-rails
177 191 best_in_place (~> 3.0.1)
178 192 bootstrap-sass (~> 3.2.0)
179 193 bootstrap-switch-rails
180 194 bootstrap-toggle-rails
181 - coffee-rails (~> 3.2.2)
195 + coffee-rails
182 196 dynamic_form
183 197 haml
184 198 haml-rails
185 199 in_place_editing
186 200 jquery-countdown-rails
187 201 jquery-rails
188 202 jquery-tablesorter
189 203 jquery-timepicker-addon-rails
190 - jquery-ui-sass-rails
204 + jquery-ui-rails
191 205 mail
192 206 momentjs-rails
193 207 mysql2
194 - prototype-rails
195 - rails (~> 3.2)
208 + rails (~> 4.2.0)
196 209 rails_bootstrap_sortable
197 210 rdiscount
198 211 rouge
199 - rspec-rails (~> 2.99.0)
200 - sass-rails (~> 3.2.6)
212 + sass-rails
201 213 select2-rails
214 + sqlite3
202 215 test-unit
203 216 uglifier
204 217 verification!
205 218 will_paginate (~> 3.0.7)
206 219
207 220 BUNDLED WITH
208 - 1.12.5
221 + 1.13.6
@@ -1,44 +1,41
1 1 // This is a manifest file that'll be compiled into application.js, which will include all the files
2 2 // listed below.
3 3 //
4 4 // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 5 // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 6 //
7 7 // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 8 // the compiled file.
9 9 //
10 10 // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 11 // GO AFTER THE REQUIRES BELOW.
12 12 //
13 13 //= require jquery
14 14 //= require jquery_ujs
15 - //= require jquery.ui.all
15 + //= require jquery-ui
16 16 //= require bootstrap-sprockets
17 17 //= require moment
18 18 //= require bootstrap-sortable
19 19 //= require select2
20 20 //= require ace-rails-ap
21 21 //= require ace/mode-c_cpp
22 22 //= require ace/mode-python
23 23 //= require ace/mode-ruby
24 24 //= require ace/mode-pascal
25 25 //= require ace/mode-javascript
26 26 //= require ace/mode-java
27 27 //= require ace/theme-merbivore
28 28 //= require custom
29 29 //= require jquery.countdown
30 30 //-------------- addition from local_jquery -----------
31 - //= require jquery.ui.datepicker
32 - //= require jquery.ui.slider
33 - //= require jquery-ui-timepicker-addon
34 31 //= require jquery-tablesorter
35 32 //= require best_in_place
36 33 //= require best_in_place.jquery-ui
37 34 //= require brython
38 35
39 36 // since this is after blank line, it is not downloaded
40 37 //x= require prototype
41 38 //x= require prototype_ujs
42 39 //x= require effects
43 40 //x= require dragdrop
44 41 //x= require controls
@@ -1,14 +1,4
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
7 - //= require jquery-tablesorter
8 - //= require best_in_place
9 - //= require best_in_place.jquery-ui
10 -
11 1 $(document).ready(function() {
12 2 /* Activating Best In Place */
13 3 jQuery(".best_in_place").best_in_place();
14 4 });
@@ -1,405 +1,405
1 1 /* This is a manifest file that'll be compiled into application.css, which will include all the files
2 2 * listed below.
3 3 *
4 4 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
5 5 * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
6 6 *
7 7 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
8 8 * compiled file so the styles you add here take precedence over styles defined in any styles
9 9 * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
10 10 * file per style scope.
11 11 *
12 12 * // bootstrap says that we should not do this, but @import each file instead
13 13 * # *= require_tree .
14 14 * # *= require_self
15 15 */
16 16
17 - @import "jquery.ui.all";
18 - @import "jquery.ui.core";
19 - @import "jquery.ui.theme";
20 - @import "jquery.ui.datepicker";
21 - @import "jquery.ui.slider";
17 + @import "jquery-ui";
18 + //@import "jquery.ui.core";
19 + //@import "jquery.ui.theme";
20 + //@import "jquery.ui.datepicker";
21 + //@import "jquery.ui.slider";
22 22 @import "jquery-ui-timepicker-addon";
23 23 @import "jquery-tablesorter/theme.metro-dark";
24 24 @import "jquery.countdown";
25 25 @import "tablesorter-theme.cafe";
26 26
27 27 //bootstrap
28 28 @import "bootstrap-sprockets";
29 29 @import "bootstrap";
30 30 @import "select2";
31 31 @import "select2-bootstrap";
32 32
33 33 //@import bootstrap3-switch
34 34 @import "bootstrap-toggle";
35 35 @import "bootstrap-sortable";
36 36
37 37 //bootstrap navbar color (from)
38 38 $bgDefault: #19197b;
39 39 $bgHighlight: #06064b;
40 40 $colDefault: #8e8eb4;
41 41 $colHighlight: #ffffff;
42 42 $dropDown: false;
43 43
44 44 @font-face {
45 45 font-family: 'Glyphicons Halflings';
46 46 src: font-path('bootstrap/glyphicons-halflings-regular.eot');
47 47 src: font-path('bootstrap/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
48 48 font-path('bootstrap/glyphicons-halflings-regular.woff') format('woff'),
49 49 font-path('bootstrap/glyphicons-halflings-regular.ttf') format('truetype'),
50 50 font-path('bootstrap/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
51 51 }
52 52
53 53
54 54 .navbar-default {
55 55 background-color: $bgDefault;
56 56 border-color: $bgHighlight;
57 57
58 58 .navbar-brand {
59 59 color: $colDefault;
60 60
61 61 &:hover, &:focus {
62 62 color: $colHighlight;
63 63 }
64 64 }
65 65
66 66 .navbar-text {
67 67 color: $colDefault;
68 68 }
69 69
70 70 .navbar-nav {
71 71 > li {
72 72 > a {
73 73 color: $colDefault;
74 74
75 75 &:hover, &:focus {
76 76 color: $colHighlight;
77 77 }
78 78 }
79 79
80 80 @if $dropDown {
81 81 > .dropdown-menu {
82 82 background-color: $bgDefault;
83 83
84 84 > li {
85 85 > a {
86 86 color: $colDefault;
87 87
88 88 &:hover, &:focus {
89 89 color: $colHighlight;
90 90 background-color: $bgHighlight;
91 91 }
92 92 }
93 93
94 94 > .divider {
95 95 background-color: $bgHighlight;
96 96 }
97 97 }
98 98 }
99 99 }
100 100 }
101 101
102 102 @if $dropDown {
103 103 .open .dropdown-menu > .active {
104 104 > a, > a:hover, > a:focus {
105 105 color: $colHighlight;
106 106 background-color: $bgHighlight;
107 107 }
108 108 }
109 109 }
110 110
111 111 > .active {
112 112 > a, > a:hover, > a:focus {
113 113 color: $colHighlight;
114 114 background-color: $bgHighlight;
115 115 }
116 116 }
117 117
118 118 > .open {
119 119 > a, > a:hover, > a:focus {
120 120 color: $colHighlight;
121 121 background-color: $bgHighlight;
122 122 }
123 123 }
124 124 }
125 125
126 126 .navbar-toggle {
127 127 border-color: $bgHighlight;
128 128
129 129 &:hover, &:focus {
130 130 background-color: $bgHighlight;
131 131 }
132 132
133 133 .icon-bar {
134 134 background-color: $colDefault;
135 135 }
136 136 }
137 137
138 138 .navbar-collapse,
139 139 .navbar-form {
140 140 border-color: $colDefault;
141 141 }
142 142
143 143 .navbar-link {
144 144 color: $colDefault;
145 145
146 146 &:hover {
147 147 color: $colHighlight;
148 148 }
149 149 }
150 150 }
151 151
152 152 @media (max-width: 767px) {
153 153 .navbar-default .navbar-nav .open .dropdown-menu {
154 154 > li > a {
155 155 color: $colDefault;
156 156
157 157 &:hover, &:focus {
158 158 color: $colHighlight;
159 159 }
160 160 }
161 161
162 162 > .active {
163 163 > a, > a:hover, > a:focus {
164 164 color: $colHighlight;
165 165 background-color: $bgHighlight;
166 166 }
167 167 }
168 168 }
169 169 }
170 170
171 171 .secondnavbar {
172 172 top: 50px;
173 173 }
174 174
175 175 // --------------- bootstrap file upload ----------------------
176 176 .btn-file {
177 177 position: relative;
178 178 overflow: hidden;
179 179 }
180 180
181 181 .btn-file input[type=file] {
182 182 position: absolute;
183 183 top: 0;
184 184 right: 0;
185 185 min-width: 100%;
186 186 min-height: 100%;
187 187 font-size: 100px;
188 188 text-align: right;
189 189 filter: alpha(opacity = 0);
190 190 opacity: 0;
191 191 outline: none;
192 192 background: white;
193 193 cursor: inherit;
194 194 display: block;
195 195 }
196 196
197 197 body {
198 198 background: white image-url("topbg.jpg") repeat-x top center;
199 199
200 200 //font-size: 13px
201 201 //font-family: Tahoma, "sans-serif"
202 202 margin: 10px;
203 203 padding: 10px;
204 204 padding-top: 60px;
205 205 }
206 206
207 207 // ------------------ bootstrap sortable --------------------
208 208 table.sortable th {
209 209 padding-right: 20px !important;
210 210
211 211 span.sign {
212 212 right: (-15px) !important;
213 213 }
214 214
215 215 &.text-right {
216 216 padding-left: 20px !important;
217 217 padding-right: 8px !important;
218 218
219 219 &:after, span.sign {
220 220 left: (-15px) !important;
221 221 }
222 222 }
223 223 }
224 224
225 225 input {
226 226 font-family: Tahoma, "sans-serif";
227 227 }
228 228
229 229 h1 {
230 230 font-size: 24px;
231 231 color: #334488;
232 232 line-height: 2em;
233 233 }
234 234
235 235 h2 {
236 236 font-size: 18px;
237 237 color: #5566bb;
238 238 line-height: 1.5em;
239 239 }
240 240
241 241 hr {
242 242 border-top: 1px solid #dddddd;
243 243 border-bottom: 1px solid #eeeeee;
244 244 }
245 245
246 246 //#a
247 247 // color: #6666cc
248 248 // text-decoration: none
249 249 //
250 250 // &:link, &:visited
251 251 // color: #6666cc
252 252 // text-decoration: none
253 253 //
254 254 // &:hover, &:focus
255 255 // color: #111166
256 256 // text-decoration: none
257 257
258 258 div {
259 259 &.userbar {
260 260 line-height: 1.5em;
261 261 text-align: right;
262 262 font-size: 12px;
263 263 }
264 264
265 265 &.title {
266 266 padding: 10px 0px;
267 267 line-height: 1.5em;
268 268 font-size: 13px;
269 269
270 270 span.contest-over-msg {
271 271 font-size: 15px;
272 272 color: red;
273 273 }
274 274
275 275 table {
276 276 width: 100%;
277 277 font-weight: bold;
278 278 }
279 279
280 280 td {
281 281 &.left-col {
282 282 text-align: left;
283 283 vertical-align: top;
284 284 color: #444444;
285 285 }
286 286
287 287 &.right-col {
288 288 text-align: right;
289 289 vertical-align: top;
290 290 font-size: 18px;
291 291 color: #116699;
292 292 }
293 293 }
294 294 }
295 295 }
296 296
297 297 table.info {
298 298 margin: 10px 0;
299 299 border: 1px solid #666666;
300 300 border-collapse: collapse;
301 301 font-size: 12px;
302 302
303 303 th {
304 304 border: 1px solid #666666;
305 305 line-height: 1.5em;
306 306 padding: 0 0.5em;
307 307 }
308 308
309 309 td {
310 310 border-left: 1px solid #666666;
311 311 border-right: 1px solid #666666;
312 312 line-height: 1.5em;
313 313 padding: 0 0.5em;
314 314 }
315 315 }
316 316
317 317 tr {
318 318 &.info-head {
319 319 background: #777777;
320 320 color: white;
321 321 }
322 322
323 323 &.info-odd {
324 324 background: #eeeeee;
325 325 }
326 326
327 327 &.info-even {
328 328 background: #fcfcfc;
329 329 }
330 330 }
331 331
332 332 @mixin basicbox {
333 333 background: #eeeeff;
334 334 border: 1px dotted #99aaee;
335 335 padding: 5px;
336 336 margin: 10px 0px;
337 337 color: black;
338 338 font-size: 13px;
339 339 }
340 340
341 341 .infobox {
342 342 @include basicbox;
343 343 }
344 344
345 345 .submitbox {
346 346 @include basicbox;
347 347 }
348 348
349 349 .errorExplanation {
350 350 border: 1px dotted gray;
351 351 color: #bb2222;
352 352 padding: 5px 15px 5px 15px;
353 353 margin-bottom: 5px;
354 354 background-color: white;
355 355 font-weight: normal;
356 356
357 357 h2 {
358 358 color: #cc1111;
359 359 font-weight: bold;
360 360 }
361 361 }
362 362
363 363 table.uinfo {
364 364 border-collapse: collapse;
365 365 border: 1px solid black;
366 366 font-size: 13px;
367 367 }
368 368
369 369 td.uinfo {
370 370 vertical-align: top;
371 371 border: 1px solid black;
372 372 padding: 5px;
373 373 }
374 374
375 375 th.uinfo {
376 376 background: lightgreen;
377 377 vertical-align: top;
378 378 text-align: right;
379 379 border: 1px solid black;
380 380 padding: 5px;
381 381 }
382 382
383 383 div {
384 384 &.compilermsgbody {
385 385 font-family: monospace;
386 386 }
387 387
388 388 &.task-menu {
389 389 text-align: center;
390 390 font-size: 13px;
391 391 line-height: 1.75em;
392 392 font-weight: bold;
393 393 border-top: 1px dashed gray;
394 394 border-bottom: 1px dashed gray;
395 395 margin-top: 2px;
396 396 margin-bottom: 4px;
397 397 }
398 398 }
399 399
400 400 table.taskdesc {
401 401 border: 2px solid #dddddd;
402 402 border-collapse: collapse;
403 403 margin: 10px auto;
404 404 width: 90%;
405 405 font-size: 13px;
@@ -1,111 +1,116
1 1 class AnnouncementsController < ApplicationController
2 2
3 3 before_filter :admin_authorization
4 4
5 5 in_place_edit_for :announcement, :published
6 6
7 7 # GET /announcements
8 8 # GET /announcements.xml
9 9 def index
10 - @announcements = Announcement.find(:all,
11 - :order => "created_at DESC")
10 + @announcements = Announcement.order(created_at: :desc)
12 11
13 12 respond_to do |format|
14 13 format.html # index.html.erb
15 14 format.xml { render :xml => @announcements }
16 15 end
17 16 end
18 17
19 18 # GET /announcements/1
20 19 # GET /announcements/1.xml
21 20 def show
22 21 @announcement = Announcement.find(params[:id])
23 22
24 23 respond_to do |format|
25 24 format.html # show.html.erb
26 25 format.xml { render :xml => @announcement }
27 26 end
28 27 end
29 28
30 29 # GET /announcements/new
31 30 # GET /announcements/new.xml
32 31 def new
33 32 @announcement = Announcement.new
34 33
35 34 respond_to do |format|
36 35 format.html # new.html.erb
37 36 format.xml { render :xml => @announcement }
38 37 end
39 38 end
40 39
41 40 # GET /announcements/1/edit
42 41 def edit
43 42 @announcement = Announcement.find(params[:id])
44 43 end
45 44
46 45 # POST /announcements
47 46 # POST /announcements.xml
48 47 def create
49 48 @announcement = Announcement.new(params[:announcement])
50 49
51 50 respond_to do |format|
52 51 if @announcement.save
53 52 flash[:notice] = 'Announcement was successfully created.'
54 53 format.html { redirect_to(@announcement) }
55 54 format.xml { render :xml => @announcement, :status => :created, :location => @announcement }
56 55 else
57 56 format.html { render :action => "new" }
58 57 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
59 58 end
60 59 end
61 60 end
62 61
63 62 # PUT /announcements/1
64 63 # PUT /announcements/1.xml
65 64 def update
66 65 @announcement = Announcement.find(params[:id])
67 66
68 67 respond_to do |format|
69 - if @announcement.update_attributes(params[:announcement])
68 + if @announcement.update_attributes(announcement_params)
70 69 flash[:notice] = 'Announcement was successfully updated.'
71 70 format.html { redirect_to(@announcement) }
72 71 format.js {}
73 72 format.xml { head :ok }
74 73 else
75 74 format.html { render :action => "edit" }
76 75 format.js {}
77 76 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
78 77 end
79 78 end
80 79 end
81 80
82 81 def toggle
83 82 @announcement = Announcement.find(params[:id])
84 83 @announcement.update_attributes( published: !@announcement.published? )
85 84 respond_to do |format|
86 85 format.js { render partial: 'toggle_button',
87 86 locals: {button_id: "#announcement_toggle_#{@announcement.id}",button_on: @announcement.published? } }
88 87 end
89 88 end
90 89
91 90 def toggle_front
92 91 @announcement = Announcement.find(params[:id])
93 92 @announcement.update_attributes( frontpage: !@announcement.frontpage? )
94 93 respond_to do |format|
95 94 format.js { render partial: 'toggle_button',
96 95 locals: {button_id: "#announcement_toggle_front_#{@announcement.id}",button_on: @announcement.frontpage? } }
97 96 end
98 97 end
99 98
100 99 # DELETE /announcements/1
101 100 # DELETE /announcements/1.xml
102 101 def destroy
103 102 @announcement = Announcement.find(params[:id])
104 103 @announcement.destroy
105 104
106 105 respond_to do |format|
107 106 format.html { redirect_to(announcements_url) }
108 107 format.xml { head :ok }
109 108 end
110 109 end
110 +
111 + private
112 +
113 + def announcement_params
114 + params.require(:announcement).permit(:author, :body, :published, :frontpage, :contest_only,:title, :note)
111 115 end
116 + end
@@ -1,128 +1,128
1 1 class ApplicationController < ActionController::Base
2 2 protect_from_forgery
3 3
4 4 before_filter :current_user
5 5
6 6 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
7 7 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
8 8
9 9 #report and redirect for unauthorized activities
10 10 def unauthorized_redirect
11 11 flash[:notice] = 'You are not authorized to view the page you requested'
12 12 redirect_to :controller => 'main', :action => 'login'
13 13 end
14 14
15 15 # Returns the current logged-in user (if any).
16 16 def current_user
17 17 return nil unless session[:user_id]
18 18 @current_user ||= User.find(session[:user_id])
19 19 end
20 20
21 21 def admin_authorization
22 22 return false unless authenticate
23 - user = User.find(session[:user_id], :include => ['roles'])
23 + user = User.includes(:roles).find(session[:user_id])
24 24 unless user.admin?
25 25 unauthorized_redirect
26 26 return false
27 27 end
28 28 return true
29 29 end
30 30
31 31 def authorization_by_roles(allowed_roles)
32 32 return false unless authenticate
33 33 user = User.find(session[:user_id])
34 34 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
35 35 unauthorized_redirect
36 36 return false
37 37 end
38 38 end
39 39
40 40 protected
41 41
42 42 def authenticate
43 43 unless session[:user_id]
44 44 flash[:notice] = 'You need to login'
45 45 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
46 46 flash[:notice] = 'You need to login but you cannot log in at this time'
47 47 end
48 48 redirect_to :controller => 'main', :action => 'login'
49 49 return false
50 50 end
51 51
52 52 # check if run in single user mode
53 53 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
54 54 user = User.find_by_id(session[:user_id])
55 55 if user==nil or (not user.admin?)
56 56 flash[:notice] = 'You cannot log in at this time'
57 57 redirect_to :controller => 'main', :action => 'login'
58 58 return false
59 59 end
60 60 unless user.enabled?
61 61 flash[:notice] = 'Your account is disabled'
62 62 redirect_to :controller => 'main', :action => 'login'
63 63 return false
64 64 end
65 65 return true
66 66 end
67 67
68 68 if GraderConfiguration.multicontests?
69 69 user = User.find(session[:user_id])
70 70 return true if user.admin?
71 71 begin
72 72 if user.contest_stat(true).forced_logout
73 73 flash[:notice] = 'You have been automatically logged out.'
74 74 redirect_to :controller => 'main', :action => 'index'
75 75 end
76 76 rescue
77 77 end
78 78 end
79 79 return true
80 80 end
81 81
82 82 def authenticate_by_ip_address
83 83 #this assume that we have already authenticate normally
84 84 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
85 85 user = User.find(session[:user_id])
86 86 if (not user.admin? and user.last_ip and user.last_ip != request.remote_ip)
87 87 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
88 88 redirect_to :controller => 'main', :action => 'login'
89 89 puts "CHEAT: user #{user.login} tried to login from '#{request.remote_ip}' while last ip is '#{user.last_ip}' at #{Time.zone.now}"
90 90 return false
91 91 end
92 92 unless user.last_ip
93 93 user.last_ip = request.remote_ip
94 94 user.save
95 95 end
96 96 end
97 97 return true
98 98 end
99 99
100 100 def authorization
101 101 return false unless authenticate
102 102 user = User.find(session[:user_id])
103 103 unless user.roles.detect { |role|
104 104 role.rights.detect{ |right|
105 105 right.controller == self.class.controller_name and
106 106 (right.action == 'all' or right.action == action_name)
107 107 }
108 108 }
109 109 flash[:notice] = 'You are not authorized to view the page you requested'
110 110 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
111 111 redirect_to :controller => 'main', :action => 'login'
112 112 return false
113 113 end
114 114 end
115 115
116 116 def verify_time_limit
117 117 return true if session[:user_id]==nil
118 118 user = User.find(session[:user_id], :include => :site)
119 119 return true if user==nil or user.site == nil
120 120 if user.contest_finished?
121 121 flash[:notice] = 'Error: the contest you are participating is over.'
122 122 redirect_to :back
123 123 return false
124 124 end
125 125 return true
126 126 end
127 127
128 128 end
@@ -1,30 +1,34
1 1 class ConfigurationsController < ApplicationController
2 2
3 3 before_filter :authenticate
4 4 before_filter { |controller| controller.authorization_by_roles(['admin'])}
5 5
6 6
7 7 def index
8 - @configurations = GraderConfiguration.find(:all,
9 - :order => '`key`')
8 + @configurations = GraderConfiguration.order(:key)
10 9 @group = GraderConfiguration.pluck("grader_configurations.key").map{ |x| x[0...(x.index('.'))] }.uniq.sort
11 10 end
12 11
13 12 def reload
14 13 GraderConfiguration.reload
15 14 redirect_to :action => 'index'
16 15 end
17 16
18 17 def update
19 18 @config = GraderConfiguration.find(params[:id])
20 19 User.clear_last_login if @config.key == GraderConfiguration::MULTIPLE_IP_LOGIN_KEY and @config.value == 'true' and params[:grader_configuration][:value] == 'false'
21 20 respond_to do |format|
22 - if @config.update_attributes(params[:grader_configuration])
21 + if @config.update_attributes(configuration_params)
23 22 format.json { head :ok }
24 23 else
25 24 format.json { respond_with_bip(@config) }
26 25 end
27 26 end
28 27 end
29 28
29 + private
30 + def configuration_params
31 + params.require(:grader_configuration).permit(:key,:value_type,:value,:description)
30 32 end
33 +
34 + end
@@ -1,50 +1,50
1 1 class ContestManagementController < ApplicationController
2 2
3 3 before_filter :admin_authorization
4 4
5 5 def index
6 6 @num_contests = Contest.count()
7 7 end
8 8
9 9 def user_stat
10 10 if not GraderConfiguration.indv_contest_mode?
11 11 redirect_to :action => 'index' and return
12 12 end
13 13
14 - @users = User.find(:all)
14 + @users = User.all
15 15 @start_times = {}
16 - UserContestStat.find(:all).each do |stat|
16 + UserContestStat.all.each do |stat|
17 17 @start_times[stat.user_id] = stat.started_at
18 18 end
19 19 end
20 20
21 21 def clear_stat
22 22 user = User.find(params[:id])
23 23 if user.contest_stat!=nil
24 24 user.contest_stat.destroy
25 25 end
26 26 redirect_to :action => 'user_stat'
27 27 end
28 28
29 29 def clear_all_stat
30 30 if not GraderConfiguration.indv_contest_mode?
31 31 redirect_to :action => 'index' and return
32 32 end
33 33
34 34 UserContestStat.delete_all()
35 35 flash[:notice] = 'All start time statistic cleared.'
36 36 redirect_to :action => 'index'
37 37 end
38 38
39 39 def change_contest_mode
40 40 if ['standard', 'contest', 'indv-contest', 'analysis'].include? params[:id]
41 41 config = GraderConfiguration.find_by_key('system.mode')
42 42 config.value = params[:id]
43 43 config.save
44 44 else
45 45 flash[:notice] = 'Wrong contest mode value'
46 46 end
47 47 redirect_to :action => 'index'
48 48 end
49 49
50 50 end
@@ -1,92 +1,98
1 1 class ContestsController < ApplicationController
2 2
3 3 before_filter :admin_authorization
4 4
5 5 in_place_edit_for :contest, :title
6 6 in_place_edit_for :contest, :enabled
7 7
8 8 # GET /contests
9 9 # GET /contests.xml
10 10 def index
11 11 @contests = Contest.all
12 12
13 13 respond_to do |format|
14 14 format.html # index.html.erb
15 15 format.xml { render :xml => @contests }
16 16 end
17 17 end
18 18
19 19 # GET /contests/1
20 20 # GET /contests/1.xml
21 21 def show
22 22 @contest = Contest.find(params[:id])
23 23
24 24 respond_to do |format|
25 25 format.html # show.html.erb
26 26 format.xml { render :xml => @contest }
27 27 end
28 28 end
29 29
30 30 # GET /contests/new
31 31 # GET /contests/new.xml
32 32 def new
33 33 @contest = Contest.new
34 34
35 35 respond_to do |format|
36 36 format.html # new.html.erb
37 37 format.xml { render :xml => @contest }
38 38 end
39 39 end
40 40
41 41 # GET /contests/1/edit
42 42 def edit
43 43 @contest = Contest.find(params[:id])
44 44 end
45 45
46 46 # POST /contests
47 47 # POST /contests.xml
48 48 def create
49 49 @contest = Contest.new(params[:contest])
50 50
51 51 respond_to do |format|
52 52 if @contest.save
53 53 flash[:notice] = 'Contest was successfully created.'
54 54 format.html { redirect_to(@contest) }
55 55 format.xml { render :xml => @contest, :status => :created, :location => @contest }
56 56 else
57 57 format.html { render :action => "new" }
58 58 format.xml { render :xml => @contest.errors, :status => :unprocessable_entity }
59 59 end
60 60 end
61 61 end
62 62
63 63 # PUT /contests/1
64 64 # PUT /contests/1.xml
65 65 def update
66 66 @contest = Contest.find(params[:id])
67 67
68 68 respond_to do |format|
69 - if @contest.update_attributes(params[:contest])
69 + if @contest.update_attributes(contests_params)
70 70 flash[:notice] = 'Contest was successfully updated.'
71 71 format.html { redirect_to(@contest) }
72 72 format.xml { head :ok }
73 73 else
74 74 format.html { render :action => "edit" }
75 75 format.xml { render :xml => @contest.errors, :status => :unprocessable_entity }
76 76 end
77 77 end
78 78 end
79 79
80 80 # DELETE /contests/1
81 81 # DELETE /contests/1.xml
82 82 def destroy
83 83 @contest = Contest.find(params[:id])
84 84 @contest.destroy
85 85
86 86 respond_to do |format|
87 87 format.html { redirect_to(contests_url) }
88 88 format.xml { head :ok }
89 89 end
90 90 end
91 91
92 + private
93 +
94 + def contests_params
95 + params.require(:contest).permit(:title,:enabled,:name)
92 96 end
97 +
98 + end
@@ -1,130 +1,128
1 1 class GradersController < ApplicationController
2 2
3 3 before_filter :admin_authorization, except: [ :submission ]
4 4 before_filter(only: [:submission]) {
5 5 #check if authenticated
6 6 return false unless authenticate
7 7
8 8 #admin always has privileged
9 9 if @current_user.admin?
10 10 return true
11 11 end
12 12
13 13 if GraderConfiguration["right.user_view_submission"] and Submission.find(params[:id]).problem.available?
14 14 return true
15 15 else
16 16 unauthorized_redirect
17 17 return false
18 18 end
19 19 }
20 20
21 21 verify :method => :post, :only => ['clear_all',
22 22 'start_exam',
23 23 'start_grading',
24 24 'stop_all',
25 25 'clear_terminated'],
26 26 :redirect_to => {:action => 'index'}
27 27
28 28 def index
29 29 redirect_to :action => 'list'
30 30 end
31 31
32 32 def list
33 33 @grader_processes = GraderProcess.find_running_graders
34 34 @stalled_processes = GraderProcess.find_stalled_process
35 35
36 36 @terminated_processes = GraderProcess.find_terminated_graders
37 37
38 - @last_task = Task.find(:first,
39 - :order => 'created_at DESC')
40 - @last_test_request = TestRequest.find(:first,
41 - :order => 'created_at DESC')
38 + @last_task = Task.last
39 + @last_test_request = TestRequest.last
42 40 @submission = Submission.order("id desc").limit(20)
43 41 @backlog_submission = Submission.where('graded_at is null')
44 42 end
45 43
46 44 def clear
47 45 grader_proc = GraderProcess.find(params[:id])
48 46 grader_proc.destroy if grader_proc!=nil
49 47 redirect_to :action => 'list'
50 48 end
51 49
52 50 def clear_terminated
53 51 GraderProcess.find_terminated_graders.each do |p|
54 52 p.destroy
55 53 end
56 54 redirect_to :action => 'list'
57 55 end
58 56
59 57 def clear_all
60 - GraderProcess.find(:all).each do |p|
58 + GraderProcess.all.each do |p|
61 59 p.destroy
62 60 end
63 61 redirect_to :action => 'list'
64 62 end
65 63
66 64 def view
67 65 if params[:type]=='Task'
68 66 redirect_to :action => 'task', :id => params[:id]
69 67 else
70 68 redirect_to :action => 'test_request', :id => params[:id]
71 69 end
72 70 end
73 71
74 72 def test_request
75 73 @test_request = TestRequest.find(params[:id])
76 74 end
77 75
78 76 def task
79 77 @task = Task.find(params[:id])
80 78 end
81 79
82 80 def submission
83 81 @submission = Submission.find(params[:id])
84 82 formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', line_numbers: true )
85 83 lexer = case @submission.language.name
86 84 when "c" then Rouge::Lexers::C.new
87 85 when "cpp" then Rouge::Lexers::Cpp.new
88 86 when "pas" then Rouge::Lexers::Pas.new
89 87 when "ruby" then Rouge::Lexers::Ruby.new
90 88 when "python" then Rouge::Lexers::Python.new
91 89 when "java" then Rouge::Lexers::Java.new
92 90 when "php" then Rouge::Lexers::PHP.new
93 91 end
94 92 @formatted_code = formatter.format(lexer.lex(@submission.source))
95 93 @css_style = Rouge::Themes::ThankfulEyes.render(scope: '.highlight')
96 94
97 95 user = User.find(session[:user_id])
98 96 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
99 97
100 98 end
101 99
102 100 # various grader controls
103 101
104 102 def stop
105 103 grader_proc = GraderProcess.find(params[:id])
106 104 GraderScript.stop_grader(grader_proc.pid)
107 105 flash[:notice] = 'Grader stopped. It may not disappear now, but it should disappear shortly.'
108 106 redirect_to :action => 'list'
109 107 end
110 108
111 109 def stop_all
112 110 GraderScript.stop_graders(GraderProcess.find_running_graders +
113 111 GraderProcess.find_stalled_process)
114 112 flash[:notice] = 'Graders stopped. They may not disappear now, but they should disappear shortly.'
115 113 redirect_to :action => 'list'
116 114 end
117 115
118 116 def start_grading
119 117 GraderScript.start_grader('grading')
120 118 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
121 119 redirect_to :action => 'list'
122 120 end
123 121
124 122 def start_exam
125 123 GraderScript.start_grader('exam')
126 124 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
127 125 redirect_to :action => 'list'
128 126 end
129 127
130 128 end
@@ -1,393 +1,393
1 1 class MainController < ApplicationController
2 2
3 3 before_filter :authenticate, :except => [:index, :login]
4 4 before_filter :check_viewability, :except => [:index, :login]
5 5
6 6 append_before_filter :confirm_and_update_start_time,
7 7 :except => [:index,
8 8 :login,
9 9 :confirm_contest_start]
10 10
11 11 # to prevent log in box to be shown when user logged out of the
12 12 # system only in some tab
13 13 prepend_before_filter :reject_announcement_refresh_when_logged_out,
14 14 :only => [:announcements]
15 15
16 16 before_filter :authenticate_by_ip_address, :only => [:list]
17 17
18 18 # COMMENTED OUT: filter in each action instead
19 19 # before_filter :verify_time_limit, :only => [:submit]
20 20
21 21 verify :method => :post, :only => [:submit],
22 22 :redirect_to => { :action => :index }
23 23
24 24 # COMMENT OUT: only need when having high load
25 25 # caches_action :index, :login
26 26
27 27 # NOTE: This method is not actually needed, 'config/routes.rb' has
28 28 # assigned action login as a default action.
29 29 def index
30 30 redirect_to :action => 'login'
31 31 end
32 32
33 33 def login
34 34 saved_notice = flash[:notice]
35 35 reset_session
36 36 flash.now[:notice] = saved_notice
37 37
38 38 # EXPERIMENT:
39 39 # Hide login if in single user mode and the url does not
40 40 # explicitly specify /login
41 41 #
42 42 # logger.info "PATH: #{request.path}"
43 43 # if GraderConfiguration['system.single_user_mode'] and
44 44 # request.path!='/main/login'
45 45 # @hidelogin = true
46 46 # end
47 47
48 - @announcements = Announcement.find_for_frontpage
48 + @announcements = Announcement.frontpage
49 49 render :action => 'login', :layout => 'empty'
50 50 end
51 51
52 52 def list
53 53 prepare_list_information
54 54 end
55 55
56 56 def help
57 57 @user = User.find(session[:user_id])
58 58 end
59 59
60 60 def submit
61 61 user = User.find(session[:user_id])
62 62
63 63 @submission = Submission.new
64 64 @submission.problem_id = params[:submission][:problem_id]
65 65 @submission.user = user
66 66 @submission.language_id = 0
67 67 if (params['file']) and (params['file']!='')
68 68 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
69 69 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
70 70 @submission.source_filename = params['file'].original_filename
71 71 end
72 72
73 73 if (params[:editor_text])
74 74 language = Language.find_by_id(params[:language_id])
75 75 @submission.source = params[:editor_text]
76 76 @submission.source_filename = "live_edit.#{language.ext}"
77 77 @submission.language = language
78 78 end
79 79
80 80 @submission.submitted_at = Time.new.gmtime
81 81 @submission.ip_address = request.remote_ip
82 82
83 83 if GraderConfiguration.time_limit_mode? and user.contest_finished?
84 84 @submission.errors.add(:base,"The contest is over.")
85 85 prepare_list_information
86 86 render :action => 'list' and return
87 87 end
88 88
89 89 if @submission.valid?
90 90 if @submission.save == false
91 91 flash[:notice] = 'Error saving your submission'
92 92 elsif Task.create(:submission_id => @submission.id,
93 93 :status => Task::STATUS_INQUEUE) == false
94 94 flash[:notice] = 'Error adding your submission to task queue'
95 95 end
96 96 else
97 97 prepare_list_information
98 98 render :action => 'list' and return
99 99 end
100 100 redirect_to :action => 'list'
101 101 end
102 102
103 103 def source
104 104 submission = Submission.find(params[:id])
105 105 if ((submission.user_id == session[:user_id]) and
106 106 (submission.problem != nil) and
107 107 (submission.problem.available))
108 108 send_data(submission.source,
109 109 {:filename => submission.download_filename,
110 110 :type => 'text/plain'})
111 111 else
112 112 flash[:notice] = 'Error viewing source'
113 113 redirect_to :action => 'list'
114 114 end
115 115 end
116 116
117 117 def compiler_msg
118 118 @submission = Submission.find(params[:id])
119 119 if @submission.user_id == session[:user_id]
120 120 render :action => 'compiler_msg', :layout => 'empty'
121 121 else
122 122 flash[:notice] = 'Error viewing source'
123 123 redirect_to :action => 'list'
124 124 end
125 125 end
126 126
127 127 def submission
128 128 @user = User.find(session[:user_id])
129 129 @problems = @user.available_problems
130 130 if params[:id]==nil
131 131 @problem = nil
132 132 @submissions = nil
133 133 else
134 134 @problem = Problem.find_by_id(params[:id])
135 135 if (@problem == nil) or (not @problem.available)
136 136 redirect_to :action => 'list'
137 137 flash[:notice] = 'Error: submissions for that problem are not viewable.'
138 138 return
139 139 end
140 140 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
141 141 end
142 142 end
143 143
144 144 def result
145 145 if !GraderConfiguration.show_grading_result
146 146 redirect_to :action => 'list' and return
147 147 end
148 148 @user = User.find(session[:user_id])
149 149 @submission = Submission.find(params[:id])
150 150 if @submission.user!=@user
151 151 flash[:notice] = 'You are not allowed to view result of other users.'
152 152 redirect_to :action => 'list' and return
153 153 end
154 154 prepare_grading_result(@submission)
155 155 end
156 156
157 157 def load_output
158 158 if !GraderConfiguration.show_grading_result or params[:num]==nil
159 159 redirect_to :action => 'list' and return
160 160 end
161 161 @user = User.find(session[:user_id])
162 162 @submission = Submission.find(params[:id])
163 163 if @submission.user!=@user
164 164 flash[:notice] = 'You are not allowed to view result of other users.'
165 165 redirect_to :action => 'list' and return
166 166 end
167 167 case_num = params[:num].to_i
168 168 out_filename = output_filename(@user.login,
169 169 @submission.problem.name,
170 170 @submission.id,
171 171 case_num)
172 172 if !FileTest.exists?(out_filename)
173 173 flash[:notice] = 'Output not found.'
174 174 redirect_to :action => 'list' and return
175 175 end
176 176
177 177 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
178 178 response.headers['Content-Type'] = "application/force-download"
179 179 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
180 180 response.headers["X-Sendfile"] = out_filename
181 181 response.headers['Content-length'] = File.size(out_filename)
182 182 render :nothing => true
183 183 else
184 184 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
185 185 end
186 186 end
187 187
188 188 def error
189 189 @user = User.find(session[:user_id])
190 190 end
191 191
192 192 # announcement refreshing and hiding methods
193 193
194 194 def announcements
195 195 if params.has_key? 'recent'
196 196 prepare_announcements(params[:recent])
197 197 else
198 198 prepare_announcements
199 199 end
200 200 render(:partial => 'announcement',
201 201 :collection => @announcements,
202 202 :locals => {:announcement_effect => true})
203 203 end
204 204
205 205 def confirm_contest_start
206 206 user = User.find(session[:user_id])
207 207 if request.method == 'POST'
208 208 user.update_start_time
209 209 redirect_to :action => 'list'
210 210 else
211 211 @contests = user.contests
212 212 @user = user
213 213 end
214 214 end
215 215
216 216 protected
217 217
218 218 def prepare_announcements(recent=nil)
219 219 if GraderConfiguration.show_tasks_to?(@user)
220 - @announcements = Announcement.find_published(true)
220 + @announcements = Announcement.published(true)
221 221 else
222 - @announcements = Announcement.find_published
222 + @announcements = Announcement.published
223 223 end
224 224 if recent!=nil
225 225 recent_id = recent.to_i
226 226 @announcements = @announcements.find_all { |a| a.id > recent_id }
227 227 end
228 228 end
229 229
230 230 def prepare_list_information
231 231 @user = User.find(session[:user_id])
232 232 if not GraderConfiguration.multicontests?
233 233 @problems = @user.available_problems
234 234 else
235 235 @contest_problems = @user.available_problems_group_by_contests
236 236 @problems = @user.available_problems
237 237 end
238 238 @prob_submissions = {}
239 239 @problems.each do |p|
240 240 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
241 241 if sub!=nil
242 242 @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
243 243 else
244 244 @prob_submissions[p.id] = { :count => 0, :submission => nil }
245 245 end
246 246 end
247 247 prepare_announcements
248 248 end
249 249
250 250 def check_viewability
251 251 @user = User.find(session[:user_id])
252 252 if (!GraderConfiguration.show_tasks_to?(@user)) and
253 253 ((action_name=='submission') or (action_name=='submit'))
254 254 redirect_to :action => 'list' and return
255 255 end
256 256 end
257 257
258 258 def prepare_grading_result(submission)
259 259 if GraderConfiguration.task_grading_info.has_key? submission.problem.name
260 260 grading_info = GraderConfiguration.task_grading_info[submission.problem.name]
261 261 else
262 262 # guess task info from problem.full_score
263 263 cases = submission.problem.full_score / 10
264 264 grading_info = {
265 265 'testruns' => cases,
266 266 'testcases' => cases
267 267 }
268 268 end
269 269 @test_runs = []
270 270 if grading_info['testruns'].is_a? Integer
271 271 trun_count = grading_info['testruns']
272 272 trun_count.times do |i|
273 273 @test_runs << [ read_grading_result(@user.login,
274 274 submission.problem.name,
275 275 submission.id,
276 276 i+1) ]
277 277 end
278 278 else
279 279 grading_info['testruns'].keys.sort.each do |num|
280 280 run = []
281 281 testrun = grading_info['testruns'][num]
282 282 testrun.each do |c|
283 283 run << read_grading_result(@user.login,
284 284 submission.problem.name,
285 285 submission.id,
286 286 c)
287 287 end
288 288 @test_runs << run
289 289 end
290 290 end
291 291 end
292 292
293 293 def grading_result_dir(user_name, problem_name, submission_id, case_num)
294 294 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
295 295 end
296 296
297 297 def output_filename(user_name, problem_name, submission_id, case_num)
298 298 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
299 299 return "#{dir}/output.txt"
300 300 end
301 301
302 302 def read_grading_result(user_name, problem_name, submission_id, case_num)
303 303 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
304 304 result_file_name = "#{dir}/result"
305 305 if !FileTest.exists?(result_file_name)
306 306 return {:num => case_num, :msg => 'program did not run'}
307 307 else
308 308 results = File.open(result_file_name).readlines
309 309 run_stat = extract_running_stat(results)
310 310 output_filename = "#{dir}/output.txt"
311 311 if FileTest.exists?(output_filename)
312 312 output_file = true
313 313 output_size = File.size(output_filename)
314 314 else
315 315 output_file = false
316 316 output_size = 0
317 317 end
318 318
319 319 return {
320 320 :num => case_num,
321 321 :msg => results[0],
322 322 :run_stat => run_stat,
323 323 :output => output_file,
324 324 :output_size => output_size
325 325 }
326 326 end
327 327 end
328 328
329 329 # copied from grader/script/lib/test_request_helper.rb
330 330 def extract_running_stat(results)
331 331 running_stat_line = results[-1]
332 332
333 333 # extract exit status line
334 334 run_stat = ""
335 335 if !(/[Cc]orrect/.match(results[0]))
336 336 run_stat = results[0].chomp
337 337 else
338 338 run_stat = 'Program exited normally'
339 339 end
340 340
341 341 logger.info "Stat line: #{running_stat_line}"
342 342
343 343 # extract running time
344 344 if res = /r(.*)u(.*)s/.match(running_stat_line)
345 345 seconds = (res[1].to_f + res[2].to_f)
346 346 time_stat = "Time used: #{seconds} sec."
347 347 else
348 348 seconds = nil
349 349 time_stat = "Time used: n/a sec."
350 350 end
351 351
352 352 # extract memory usage
353 353 if res = /s(.*)m/.match(running_stat_line)
354 354 memory_used = res[1].to_i
355 355 else
356 356 memory_used = -1
357 357 end
358 358
359 359 return {
360 360 :msg => "#{run_stat}\n#{time_stat}",
361 361 :running_time => seconds,
362 362 :exit_status => run_stat,
363 363 :memory_usage => memory_used
364 364 }
365 365 end
366 366
367 367 def confirm_and_update_start_time
368 368 user = User.find(session[:user_id])
369 369 if (GraderConfiguration.indv_contest_mode? and
370 370 GraderConfiguration['contest.confirm_indv_contest_start'] and
371 371 !user.contest_started?)
372 372 redirect_to :action => 'confirm_contest_start' and return
373 373 end
374 374 if not GraderConfiguration.analysis_mode?
375 375 user.update_start_time
376 376 end
377 377 end
378 378
379 379 def reject_announcement_refresh_when_logged_out
380 380 if not session[:user_id]
381 381 render :text => 'Access forbidden', :status => 403
382 382 end
383 383
384 384 if GraderConfiguration.multicontests?
385 385 user = User.find(session[:user_id])
386 386 if user.contest_stat.forced_logout
387 387 render :text => 'Access forbidden', :status => 403
388 388 end
389 389 end
390 390 end
391 391
392 392 end
393 393
@@ -1,281 +1,279
1 1 class ProblemsController < ApplicationController
2 2
3 3 before_filter :authenticate, :authorization
4 4
5 5 in_place_edit_for :problem, :name
6 6 in_place_edit_for :problem, :full_name
7 7 in_place_edit_for :problem, :full_score
8 8
9 9 def index
10 - @problems = Problem.find(:all, :order => 'date_added DESC')
10 + @problems = Problem.order(date_added: :desc)
11 11 end
12 12
13 13 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
14 14 verify :method => :post, :only => [ :create, :quick_create,
15 15 :do_manage,
16 16 :do_import,
17 17 ],
18 18 :redirect_to => { :action => :index }
19 19
20 20 def show
21 21 @problem = Problem.find(params[:id])
22 22 end
23 23
24 24 def new
25 25 @problem = Problem.new
26 26 @description = nil
27 27 end
28 28
29 29 def create
30 30 @problem = Problem.new(params[:problem])
31 31 @description = Description.new(params[:description])
32 32 if @description.body!=''
33 33 if !@description.save
34 34 render :action => new and return
35 35 end
36 36 else
37 37 @description = nil
38 38 end
39 39 @problem.description = @description
40 40 if @problem.save
41 41 flash[:notice] = 'Problem was successfully created.'
42 42 redirect_to action: :index
43 43 else
44 44 render :action => 'new'
45 45 end
46 46 end
47 47
48 48 def quick_create
49 49 @problem = Problem.new(params[:problem])
50 50 @problem.full_name = @problem.name if @problem.full_name == ''
51 51 @problem.full_score = 100
52 52 @problem.available = false
53 53 @problem.test_allowed = true
54 54 @problem.output_only = false
55 55 @problem.date_added = Time.new
56 56 if @problem.save
57 57 flash[:notice] = 'Problem was successfully created.'
58 58 redirect_to action: :index
59 59 else
60 60 flash[:notice] = 'Error saving problem'
61 61 redirect_to action: :index
62 62 end
63 63 end
64 64
65 65 def edit
66 66 @problem = Problem.find(params[:id])
67 67 @description = @problem.description
68 68 end
69 69
70 70 def update
71 71 @problem = Problem.find(params[:id])
72 72 @description = @problem.description
73 73 if @description.nil? and params[:description][:body]!=''
74 74 @description = Description.new(params[:description])
75 75 if !@description.save
76 76 flash[:notice] = 'Error saving description'
77 77 render :action => 'edit' and return
78 78 end
79 79 @problem.description = @description
80 80 elsif @description
81 81 if !@description.update_attributes(params[:description])
82 82 flash[:notice] = 'Error saving description'
83 83 render :action => 'edit' and return
84 84 end
85 85 end
86 86 if params[:file] and params[:file].content_type != 'application/pdf'
87 87 flash[:notice] = 'Error: Uploaded file is not PDF'
88 88 render :action => 'edit' and return
89 89 end
90 90 if @problem.update_attributes(params[:problem])
91 91 flash[:notice] = 'Problem was successfully updated.'
92 92 unless params[:file] == nil or params[:file] == ''
93 93 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
94 94 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
95 95 if not FileTest.exists? out_dirname
96 96 Dir.mkdir out_dirname
97 97 end
98 98
99 99 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
100 100 if FileTest.exists? out_filename
101 101 File.delete out_filename
102 102 end
103 103
104 104 File.open(out_filename,"wb") do |file|
105 105 file.write(params[:file].read)
106 106 end
107 107 @problem.description_filename = "#{@problem.name}.pdf"
108 108 @problem.save
109 109 end
110 110 redirect_to :action => 'show', :id => @problem
111 111 else
112 112 render :action => 'edit'
113 113 end
114 114 end
115 115
116 116 def destroy
117 117 p = Problem.find(params[:id]).destroy
118 118 redirect_to action: :index
119 119 end
120 120
121 121 def toggle
122 122 @problem = Problem.find(params[:id])
123 123 @problem.update_attributes(available: !(@problem.available) )
124 124 respond_to do |format|
125 125 format.js { }
126 126 end
127 127 end
128 128
129 129 def toggle_test
130 130 @problem = Problem.find(params[:id])
131 131 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
132 132 respond_to do |format|
133 133 format.js { }
134 134 end
135 135 end
136 136
137 137 def turn_all_off
138 - Problem.find(:all,
139 - :conditions => "available = 1").each do |problem|
138 + Problem.available.all.each do |problem|
140 139 problem.available = false
141 140 problem.save
142 141 end
143 142 redirect_to action: :index
144 143 end
145 144
146 145 def turn_all_on
147 - Problem.find(:all,
148 - :conditions => "available = 0").each do |problem|
146 + Problem.where.not(available: true).each do |problem|
149 147 problem.available = true
150 148 problem.save
151 149 end
152 150 redirect_to action: :index
153 151 end
154 152
155 153 def stat
156 154 @problem = Problem.find(params[:id])
157 155 unless @problem.available or session[:admin]
158 156 redirect_to :controller => 'main', :action => 'list'
159 157 return
160 158 end
161 159 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
162 160
163 161 #stat summary
164 162 range =65
165 163 @histogram = { data: Array.new(range,0), summary: {} }
166 164 user = Hash.new(0)
167 165 @submissions.find_each do |sub|
168 166 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
169 167 @histogram[:data][d.to_i] += 1 if d < range
170 168 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
171 169 end
172 170 @histogram[:summary][:max] = [@histogram[:data].max,1].max
173 171
174 172 @summary = { attempt: user.count, solve: 0 }
175 173 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
176 174 end
177 175
178 176 def manage
179 - @problems = Problem.find(:all, :order => 'date_added DESC')
177 + @problems = Problem.order(date_added: :desc)
180 178 end
181 179
182 180 def do_manage
183 181 if params.has_key? 'change_date_added'
184 182 change_date_added
185 183 elsif params.has_key? 'add_to_contest'
186 184 add_to_contest
187 185 elsif params.has_key? 'enable_problem'
188 186 set_available(true)
189 187 elsif params.has_key? 'disable_problem'
190 188 set_available(false)
191 189 end
192 190 redirect_to :action => 'manage'
193 191 end
194 192
195 193 def import
196 194 @allow_test_pair_import = allow_test_pair_import?
197 195 end
198 196
199 197 def do_import
200 198 old_problem = Problem.find_by_name(params[:name])
201 199 if !allow_test_pair_import? and params.has_key? :import_to_db
202 200 params.delete :import_to_db
203 201 end
204 202 @problem, import_log = Problem.create_from_import_form_params(params,
205 203 old_problem)
206 204
207 205 if !@problem.errors.empty?
208 206 render :action => 'import' and return
209 207 end
210 208
211 209 if old_problem!=nil
212 210 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
213 211 end
214 212 @log = import_log
215 213 end
216 214
217 215 def remove_contest
218 216 problem = Problem.find(params[:id])
219 217 contest = Contest.find(params[:contest_id])
220 218 if problem!=nil and contest!=nil
221 219 problem.contests.delete(contest)
222 220 end
223 221 redirect_to :action => 'manage'
224 222 end
225 223
226 224 ##################################
227 225 protected
228 226
229 227 def allow_test_pair_import?
230 228 if defined? ALLOW_TEST_PAIR_IMPORT
231 229 return ALLOW_TEST_PAIR_IMPORT
232 230 else
233 231 return false
234 232 end
235 233 end
236 234
237 235 def change_date_added
238 236 problems = get_problems_from_params
239 237 year = params[:date_added][:year].to_i
240 238 month = params[:date_added][:month].to_i
241 239 day = params[:date_added][:day].to_i
242 240 date = Date.new(year,month,day)
243 241 problems.each do |p|
244 242 p.date_added = date
245 243 p.save
246 244 end
247 245 end
248 246
249 247 def add_to_contest
250 248 problems = get_problems_from_params
251 249 contest = Contest.find(params[:contest][:id])
252 250 if contest!=nil and contest.enabled
253 251 problems.each do |p|
254 252 p.contests << contest
255 253 end
256 254 end
257 255 end
258 256
259 257 def set_available(avail)
260 258 problems = get_problems_from_params
261 259 problems.each do |p|
262 260 p.available = avail
263 261 p.save
264 262 end
265 263 end
266 264
267 265 def get_problems_from_params
268 266 problems = []
269 267 params.keys.each do |k|
270 268 if k.index('prob-')==0
271 269 name, id, order = k.split('-')
272 270 problems << Problem.find(id)
273 271 end
274 272 end
275 273 problems
276 274 end
277 275
278 276 def get_problems_stat
279 277 end
280 278
281 279 end
@@ -1,462 +1,458
1 1 require 'csv'
2 2
3 3 class ReportController < ApplicationController
4 4
5 5 before_filter :authenticate
6 6
7 7 before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score]
8 8
9 9 before_filter(only: [:problem_hof]) { |c|
10 10 return false unless authenticate
11 11
12 - if GraderConfiguration["right.user_view_submission"]
13 - return true;
14 - end
15 -
16 - admin_authorization
12 + admin_authorization unless GraderConfiguration["right.user_view_submission"]
17 13 }
18 14
19 15 def max_score
20 16 end
21 17
22 18 def current_score
23 - @problems = Problem.find_available_problems
19 + @problems = Problem.available_problems
24 20 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
25 21 @scorearray = calculate_max_score(@problems, @users,0,0,true)
26 22
27 23 #rencer accordingly
28 24 if params[:button] == 'download' then
29 25 csv = gen_csv_from_scorearray(@scorearray,@problems)
30 26 send_data csv, filename: 'max_score.csv'
31 27 else
32 28 #render template: 'user_admin/user_stat'
33 29 render 'current_score'
34 30 end
35 31 end
36 32
37 33 def show_max_score
38 34 #process parameters
39 35 #problems
40 36 @problems = []
41 37 params[:problem_id].each do |id|
42 38 next unless id.strip != ""
43 39 pid = Problem.find_by_id(id.to_i)
44 40 @problems << pid if pid
45 41 end
46 42
47 43 #users
48 44 @users = if params[:user] == "all" then
49 - User.find(:all, :include => [:contests, :contest_stat])
45 + User.includes(:contests).includes(:contest_stat)
50 46 else
51 47 User.includes(:contests).includes(:contest_stat).where(enabled: true)
52 48 end
53 49
54 50 #set up range from param
55 51 since_id = params.fetch(:from_id, 0).to_i
56 52 until_id = params.fetch(:to_id, 0).to_i
57 53
58 54 #calculate the routine
59 55 @scorearray = calculate_max_score(@problems, @users,since_id,until_id)
60 56
61 57 #rencer accordingly
62 58 if params[:button] == 'download' then
63 59 csv = gen_csv_from_scorearray(@scorearray,@problems)
64 60 send_data csv, filename: 'max_score.csv'
65 61 else
66 62 #render template: 'user_admin/user_stat'
67 63 render 'max_score'
68 64 end
69 65
70 66 end
71 67
72 68 def score
73 69 if params[:commit] == 'download csv'
74 70 @problems = Problem.all
75 71 else
76 - @problems = Problem.find_available_problems
72 + @problems = Problem.available_problems
77 73 end
78 - @users = User.includes(:contests, :contest_stat).where(enabled: true) #find(:all, :include => [:contests, :contest_stat]).where(enabled: true)
74 + @users = User.includes(:contests, :contest_stat).where(enabled: true)
79 75 @scorearray = Array.new
80 76 @users.each do |u|
81 77 ustat = Array.new
82 78 ustat[0] = u
83 79 @problems.each do |p|
84 80 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
85 81 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
86 82 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
87 83 else
88 84 ustat << [0,false]
89 85 end
90 86 end
91 87 @scorearray << ustat
92 88 end
93 89 if params[:commit] == 'download csv' then
94 90 csv = gen_csv_from_scorearray(@scorearray,@problems)
95 91 send_data csv, filename: 'last_score.csv'
96 92 else
97 93 render template: 'user_admin/user_stat'
98 94 end
99 95
100 96 end
101 97
102 98 def login_stat
103 99 @logins = Array.new
104 100
105 101 date_and_time = '%Y-%m-%d %H:%M'
106 102 begin
107 103 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
108 104 @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)
109 105 rescue
110 106 @since_time = DateTime.new(1000,1,1)
111 107 end
112 108 begin
113 109 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
114 110 @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)
115 111 rescue
116 112 @until_time = DateTime.new(3000,1,1)
117 113 end
118 114
119 115 User.all.each do |user|
120 116 @logins << { id: user.id,
121 117 login: user.login,
122 118 full_name: user.full_name,
123 119 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
124 120 user.id,@since_time,@until_time)
125 121 .count(:id),
126 122 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
127 123 user.id,@since_time,@until_time)
128 124 .minimum(:created_at),
129 125 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
130 126 user.id,@since_time,@until_time)
131 127 .maximum(:created_at),
132 128 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
133 129 user.id,@since_time,@until_time)
134 130 .select(:ip_address).uniq
135 131
136 132 }
137 133 end
138 134 end
139 135
140 136 def submission_stat
141 137
142 138 date_and_time = '%Y-%m-%d %H:%M'
143 139 begin
144 140 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
145 141 rescue
146 142 @since_time = DateTime.new(1000,1,1)
147 143 end
148 144 begin
149 145 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
150 146 rescue
151 147 @until_time = DateTime.new(3000,1,1)
152 148 end
153 149
154 150 @submissions = {}
155 151
156 152 User.find_each do |user|
157 153 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
158 154 end
159 155
160 156 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
161 157 if @submissions[s.user_id]
162 158 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
163 159 a = Problem.find_by_id(s.problem_id)
164 160 @submissions[s.user_id][:sub][s.problem_id] =
165 161 { prob_name: (a ? a.full_name : '(NULL)'),
166 162 sub_ids: [s.id] }
167 163 else
168 164 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
169 165 end
170 166 @submissions[s.user_id][:count] += 1
171 167 end
172 168 end
173 169 end
174 170
175 171 def problem_hof
176 172 # gen problem list
177 173 @user = User.find(session[:user_id])
178 174 @problems = @user.available_problems
179 175
180 176 # get selected problems or the default
181 177 if params[:id]
182 178 begin
183 179 @problem = Problem.available.find(params[:id])
184 180 rescue
185 181 redirect_to action: :problem_hof
186 182 flash[:notice] = 'Error: submissions for that problem are not viewable.'
187 183 return
188 184 end
189 185 end
190 186
191 187 return unless @problem
192 188
193 189 @by_lang = {} #aggregrate by language
194 190
195 191 range =65
196 192 @histogram = { data: Array.new(range,0), summary: {} }
197 193 @summary = {count: 0, solve: 0, attempt: 0}
198 194 user = Hash.new(0)
199 195 Submission.where(problem_id: @problem.id).find_each do |sub|
200 196 #histogram
201 197 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
202 198 @histogram[:data][d.to_i] += 1 if d < range
203 199
204 200 next unless sub.points
205 201 @summary[:count] += 1
206 202 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
207 203
208 204 lang = Language.find_by_id(sub.language_id)
209 205 next unless lang
210 206 next unless sub.points >= @problem.full_score
211 207
212 208 #initialize
213 209 unless @by_lang.has_key?(lang.pretty_name)
214 210 @by_lang[lang.pretty_name] = {
215 211 runtime: { avail: false, value: 2**30-1 },
216 212 memory: { avail: false, value: 2**30-1 },
217 213 length: { avail: false, value: 2**30-1 },
218 214 first: { avail: false, value: DateTime.new(3000,1,1) }
219 215 }
220 216 end
221 217
222 218 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
223 219 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
224 220 end
225 221
226 222 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
227 223 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
228 224 end
229 225
230 226 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and
231 227 !sub.user.admin?
232 228 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
233 229 end
234 230
235 231 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
236 232 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
237 233 end
238 234 end
239 235
240 236 #process user_id
241 237 @by_lang.each do |lang,prop|
242 238 prop.each do |k,v|
243 239 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
244 240 end
245 241 end
246 242
247 243 #sum into best
248 244 if @by_lang and @by_lang.first
249 245 @best = @by_lang.first[1].clone
250 246 @by_lang.each do |lang,prop|
251 247 if @best[:runtime][:value] >= prop[:runtime][:value]
252 248 @best[:runtime] = prop[:runtime]
253 249 @best[:runtime][:lang] = lang
254 250 end
255 251 if @best[:memory][:value] >= prop[:memory][:value]
256 252 @best[:memory] = prop[:memory]
257 253 @best[:memory][:lang] = lang
258 254 end
259 255 if @best[:length][:value] >= prop[:length][:value]
260 256 @best[:length] = prop[:length]
261 257 @best[:length][:lang] = lang
262 258 end
263 259 if @best[:first][:value] >= prop[:first][:value]
264 260 @best[:first] = prop[:first]
265 261 @best[:first][:lang] = lang
266 262 end
267 263 end
268 264 end
269 265
270 266 @histogram[:summary][:max] = [@histogram[:data].max,1].max
271 267 @summary[:attempt] = user.count
272 268 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
273 269 end
274 270
275 271 def stuck #report struggling user,problem
276 272 # init
277 273 user,problem = nil
278 274 solve = true
279 275 tries = 0
280 276 @struggle = Array.new
281 277 record = {}
282 278 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
283 279 next unless sub.problem and sub.user
284 280 if user != sub.user_id or problem != sub.problem_id
285 281 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
286 282 record = {user: sub.user, problem: sub.problem}
287 283 user,problem = sub.user_id, sub.problem_id
288 284 solve = false
289 285 tries = 0
290 286 end
291 287 if sub.points >= sub.problem.full_score
292 288 solve = true
293 289 else
294 290 tries += 1
295 291 end
296 292 end
297 293 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
298 294 @struggle = @struggle[0..50]
299 295 end
300 296
301 297
302 298 def multiple_login
303 299 #user with multiple IP
304 300 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
305 301 last,count = 0,0
306 302 first = 0
307 303 @users = []
308 304 raw.each do |r|
309 305 if last != r.user.login
310 306 count = 1
311 307 last = r.user.login
312 308 first = r
313 309 else
314 310 @users << first if count == 1
315 311 @users << r
316 312 count += 1
317 313 end
318 314 end
319 315
320 316 #IP with multiple user
321 317 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
322 318 last,count = 0,0
323 319 first = 0
324 320 @ip = []
325 321 raw.each do |r|
326 322 if last != r.ip_address
327 323 count = 1
328 324 last = r.ip_address
329 325 first = r
330 326 else
331 327 @ip << first if count == 1
332 328 @ip << r
333 329 count += 1
334 330 end
335 331 end
336 332 end
337 333
338 334 def cheat_report
339 335 date_and_time = '%Y-%m-%d %H:%M'
340 336 begin
341 337 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
342 338 @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)
343 339 rescue
344 340 @since_time = Time.zone.now.ago( 90.minutes)
345 341 end
346 342 begin
347 343 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
348 344 @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)
349 345 rescue
350 346 @until_time = Time.zone.now
351 347 end
352 348
353 349 #multi login
354 350 @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")
355 351
356 352 st = <<-SQL
357 353 SELECT l2.*
358 354 FROM logins l2 INNER JOIN
359 355 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
360 356 FROM logins l
361 357 INNER JOIN users u ON l.user_id = u.id
362 358 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
363 359 GROUP BY u.id
364 360 HAVING count > 1
365 361 ) ml ON l2.user_id = ml.id
366 362 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
367 363 UNION
368 364 SELECT l2.*
369 365 FROM logins l2 INNER JOIN
370 366 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
371 367 FROM logins l
372 368 INNER JOIN users u ON l.user_id = u.id
373 369 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
374 370 GROUP BY l.ip_address
375 371 HAVING count > 1
376 372 ) ml on ml.ip_address = l2.ip_address
377 373 INNER JOIN users u ON l2.user_id = u.id
378 374 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
379 375 ORDER BY ip_address,created_at
380 376 SQL
381 377 @mld = Login.find_by_sql(st)
382 378
383 379 st = <<-SQL
384 380 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
385 381 FROM submissions s INNER JOIN
386 382 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
387 383 FROM logins l
388 384 INNER JOIN users u ON l.user_id = u.id
389 385 WHERE l.created_at >= ? and l.created_at <= ?
390 386 GROUP BY u.id
391 387 HAVING count > 1
392 388 ) ml ON s.user_id = ml.id
393 389 WHERE s.submitted_at >= ? and s.submitted_at <= ?
394 390 UNION
395 391 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
396 392 FROM submissions s INNER JOIN
397 393 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
398 394 FROM logins l
399 395 INNER JOIN users u ON l.user_id = u.id
400 396 WHERE l.created_at >= ? and l.created_at <= ?
401 397 GROUP BY l.ip_address
402 398 HAVING count > 1
403 399 ) ml on ml.ip_address = s.ip_address
404 400 WHERE s.submitted_at >= ? and s.submitted_at <= ?
405 401 ORDER BY ip_address,submitted_at
406 402 SQL
407 403 @subs = Submission.joins(:problem).find_by_sql([st,@since_time,@until_time,
408 404 @since_time,@until_time,
409 405 @since_time,@until_time,
410 406 @since_time,@until_time])
411 407
412 408 end
413 409
414 410 def cheat_scruntinize
415 411 #convert date & time
416 412 date_and_time = '%Y-%m-%d %H:%M'
417 413 begin
418 414 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
419 415 @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)
420 416 rescue
421 417 @since_time = Time.zone.now.ago( 90.minutes)
422 418 end
423 419 begin
424 420 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
425 421 @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)
426 422 rescue
427 423 @until_time = Time.zone.now
428 424 end
429 425
430 426 #convert sid
431 427 @sid = params[:SID].split(/[,\s]/) if params[:SID]
432 428 unless @sid and @sid.size > 0
433 429 return
434 430 redirect_to actoin: :cheat_scruntinize
435 431 flash[:notice] = 'Please enter at least 1 student id'
436 432 end
437 433 mark = Array.new(@sid.size,'?')
438 434 condition = "(u.login = " + mark.join(' OR u.login = ') + ')'
439 435
440 436 @st = <<-SQL
441 437 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
442 438 FROM logins l INNER JOIN users u on l.user_id = u.id
443 439 WHERE l.created_at >= ? AND l.created_at <= ? AND #{condition}
444 440 UNION
445 441 SELECT s.submitted_at,s.id,u.login,u.full_name,s.ip_address,s.problem_id,s.points,s.user_id
446 442 FROM submissions s INNER JOIN users u ON s.user_id = u.id
447 443 WHERE s.submitted_at >= ? AND s.submitted_at <= ? AND #{condition}
448 444 ORDER BY submitted_at
449 445 SQL
450 446
451 447 p = [@st,@since_time,@until_time] + @sid + [@since_time,@until_time] + @sid
452 448 @logs = Submission.joins(:problem).find_by_sql(p)
453 449
454 450
455 451
456 452
457 453
458 454 end
459 455
460 456 protected
461 457
462 458 def calculate_max_score(problems, users,since_id,until_id, get_last_score = false)
@@ -1,62 +1,67
1 1 class SiteController < ApplicationController
2 2
3 3 before_filter :site_admin_authorization, :except => 'login'
4 4
5 5 def login
6 6 # Site administrator login
7 - @countries = Country.find(:all, :include => :sites)
7 + @countries = Country.includes(:sites).all
8 8 @country_select = @countries.collect { |c| [c.name, c.id] }
9 9
10 10 @country_select_with_all = [['Any',0]]
11 11 @countries.each do |country|
12 12 @country_select_with_all << [country.name, country.id]
13 13 end
14 14
15 15 @site_select = []
16 16 @countries.each do |country|
17 17 country.sites.each do |site|
18 18 @site_select << ["#{site.name}, #{country.name}", site.id]
19 19 end
20 20 end
21 21
22 22 @default_site = Site.first if !GraderConfiguration['contest.multisites']
23 23
24 24 render :action => 'login', :layout => 'empty'
25 25 end
26 26
27 27 def index
28 28 if @site.started
29 29 render :action => 'started', :layout => 'empty'
30 30 else
31 31 render :action => 'prompt', :layout => 'empty'
32 32 end
33 33 end
34 34
35 35 def start
36 36 @site.started = true
37 37 @site.start_time = Time.new.gmtime
38 38 @site.save
39 39 redirect_to :action => 'index'
40 40 end
41 41
42 42 def logout
43 43 reset_session
44 44 redirect_to :controller => 'main', :action => 'login'
45 45 end
46 46
47 47 protected
48 48 def site_admin_authorization
49 49 if session[:site_id]==nil
50 50 redirect_to :controller => 'site', :action => 'login' and return
51 51 end
52 52 begin
53 53 @site = Site.find(session[:site_id], :include => :country)
54 54 rescue ActiveRecord::RecordNotFound
55 55 @site = nil
56 56 end
57 57 if @site==nil
58 58 redirect_to :controller => 'site', :action => 'login' and return
59 59 end
60 60 end
61 61
62 + private
63 + def site_params
64 + params.require(:site).permit()
62 65 end
66 +
67 + end
@@ -1,91 +1,97
1 1 class SitesController < ApplicationController
2 2
3 3 before_filter :admin_authorization
4 4
5 5 # GET /sites
6 6 # GET /sites.xml
7 7 def index
8 - @sites = Site.find(:all, :order => 'country_id')
8 + @sites = Site.order(:country_id)
9 9
10 10 respond_to do |format|
11 11 format.html # index.html.erb
12 12 format.xml { render :xml => @sites }
13 13 end
14 14 end
15 15
16 16 # GET /sites/1
17 17 # GET /sites/1.xml
18 18 def show
19 19 @site = Site.find(params[:id])
20 20
21 21 respond_to do |format|
22 22 format.html # show.html.erb
23 23 format.xml { render :xml => @site }
24 24 end
25 25 end
26 26
27 27 # GET /sites/new
28 28 # GET /sites/new.xml
29 29 def new
30 30 @site = Site.new
31 31
32 32 respond_to do |format|
33 33 format.html # new.html.erb
34 34 format.xml { render :xml => @site }
35 35 end
36 36 end
37 37
38 38 # GET /sites/1/edit
39 39 def edit
40 40 @site = Site.find(params[:id])
41 41 end
42 42
43 43 # POST /sites
44 44 # POST /sites.xml
45 45 def create
46 46 @site = Site.new(params[:site])
47 47 @site.clear_start_time_if_not_started
48 48
49 49 respond_to do |format|
50 50 if @site.save
51 51 flash[:notice] = 'Site was successfully created.'
52 52 format.html { redirect_to(@site) }
53 53 format.xml { render :xml => @site, :status => :created, :location => @site }
54 54 else
55 55 format.html { render :action => "new" }
56 56 format.xml { render :xml => @site.errors, :status => :unprocessable_entity }
57 57 end
58 58 end
59 59 end
60 60
61 61 # PUT /sites/1
62 62 # PUT /sites/1.xml
63 63 def update
64 64 @site = Site.find(params[:id])
65 65 @site.clear_start_time_if_not_started
66 66
67 67 respond_to do |format|
68 - if @site.update_attributes(params[:site])
68 + if @site.update_attributes(site_params)
69 69 flash[:notice] = 'Site was successfully updated.'
70 70 format.html { redirect_to(@site) }
71 71 format.xml { head :ok }
72 72 else
73 73 format.html { render :action => "edit" }
74 74 format.xml { render :xml => @site.errors, :status => :unprocessable_entity }
75 75 end
76 76 end
77 77 end
78 78
79 79 # DELETE /sites/1
80 80 # DELETE /sites/1.xml
81 81 def destroy
82 82 @site = Site.find(params[:id])
83 83 @site.destroy
84 84
85 85 respond_to do |format|
86 86 format.html { redirect_to(sites_url) }
87 87 format.xml { head :ok }
88 88 end
89 89 end
90 90
91 + private
92 +
93 + def site_params
94 + params.require(:site).permit(:name,:started,:start_time,:country_id,:password)
91 95 end
96 +
97 + end
@@ -1,137 +1,96
1 1 class SubmissionsController < ApplicationController
2 2 before_filter :authenticate
3 - before_filter :submission_authorization, only: [:show, :direct_edit_submission]
3 + before_filter :submission_authorization, only: [:show, :direct_edit_submission, :download]
4 4
5 5 # GET /submissions
6 6 # GET /submissions.json
7 7 # Show problem selection and user's submission of that problem
8 8 def index
9 9 @user = @current_user
10 10 @problems = @user.available_problems
11 11
12 12 if params[:problem_id]==nil
13 13 @problem = nil
14 14 @submissions = nil
15 15 else
16 16 @problem = Problem.find_by_id(params[:problem_id])
17 17 if (@problem == nil) or (not @problem.available)
18 18 redirect_to main_list_path
19 19 flash[:notice] = 'Error: submissions for that problem are not viewable.'
20 20 return
21 21 end
22 22 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
23 23 end
24 24 end
25 25
26 26 # GET /submissions/1
27 27 # GET /submissions/1.json
28 28 def show
29 29 @submission = Submission.find(params[:id])
30 30
31 31 #log the viewing
32 32 user = User.find(session[:user_id])
33 33 SubmissionViewLog.create(user_id: session[:user_id],submission_id: @submission.id) unless user.admin?
34 34 end
35 35
36 + def download
37 + @submission = Submission.find(params[:id])
38 + send_data(@submission.source, {:filename => @submission.download_filename, :type => 'text/plain'})
39 + end
40 +
41 + def compiler_msg
42 + @submission = Submission.find(params[:id])
43 + respond_to do |format|
44 + format.js
45 + end
46 + end
47 +
36 48 #on-site new submission on specific problem
37 49 def direct_edit_problem
38 50 @problem = Problem.find(params[:problem_id])
39 51 @source = ''
40 52 render 'edit'
41 53 end
42 54
43 55 # GET /submissions/1/edit
44 56 def edit
45 57 @submission = Submission.find(params[:id])
46 58 @source = @submission.source.to_s
47 59 @problem = @submission.problem
48 60 @lang_id = @submission.language.id
49 61 end
50 62
51 63
52 64 def get_latest_submission_status
53 65 @problem = Problem.find(params[:pid])
54 66 @submission = Submission.find_last_by_user_and_problem(params[:uid],params[:pid])
55 67 puts User.find(params[:uid]).login
56 68 puts Problem.find(params[:pid]).name
57 69 puts 'nil' unless @submission
58 70 respond_to do |format|
59 71 format.js
60 72 end
61 73 end
62 74
63 - # # GET /submissions/new
64 - # # GET /submissions/new.json
65 - # def new
66 - # @submission = Submission.new
67 - #
68 - # respond_to do |format|
69 - # format.html # new.html.erb
70 - # format.json { render json: @submission }
71 - # end
72 - # end
73 - #
74 - #
75 - # # POST /submissions
76 - # # POST /submissions.json
77 - # def create
78 - # @submission = Submission.new(params[:submission])
79 - #
80 - # respond_to do |format|
81 - # if @submission.save
82 - # format.html { redirect_to @submission, notice: 'Submission was successfully created.' }
83 - # format.json { render json: @submission, status: :created, location: @submission }
84 - # else
85 - # format.html { render action: "new" }
86 - # format.json { render json: @submission.errors, status: :unprocessable_entity }
87 - # end
88 - # end
89 - # end
90 - #
91 - # # PUT /submissions/1
92 - # # PUT /submissions/1.json
93 - # def update
94 - # @submission = Submission.find(params[:id])
95 - #
96 - # respond_to do |format|
97 - # if @submission.update_attributes(params[:submission])
98 - # format.html { redirect_to @submission, notice: 'Submission was successfully updated.' }
99 - # format.json { head :no_content }
100 - # else
101 - # format.html { render action: "edit" }
102 - # format.json { render json: @submission.errors, status: :unprocessable_entity }
103 - # end
104 - # end
105 - # end
106 - #
107 - # # DELETE /submissions/1
108 - # # DELETE /submissions/1.json
109 - # def destroy
110 - # @submission = Submission.find(params[:id])
111 - # @submission.destroy
112 - #
113 - # respond_to do |format|
114 - # format.html { redirect_to submissions_url }
115 - # format.json { head :no_content }
116 - # end
117 - # end
118 75
119 76 protected
77 +
120 78 def submission_authorization
121 79 #admin always has privileged
122 80 if @current_user.admin?
123 81 return true
124 82 end
125 83
126 84 sub = Submission.find(params[:id])
127 85 if sub.problem.available?
128 86 puts "sub = #{sub.user.id}, current = #{@current_user.id}"
129 87 return true if GraderConfiguration["right.user_view_submission"] or sub.user == @current_user
130 88 end
131 89
132 90 #default to NO
133 91 unauthorized_redirect
134 92 return false
135 93 end
136 94
95 +
137 96 end
@@ -1,538 +1,543
1 1 require 'csv'
2 2
3 3 class UserAdminController < ApplicationController
4 4
5 5 include MailHelperMethods
6 6
7 7 before_filter :admin_authorization
8 8
9 9 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
10 10 verify :method => :post, :only => [
11 11 :create, :create_from_list,
12 12 :update,
13 13 :manage_contest,
14 14 :bulk_mail
15 15 ],
16 16 :redirect_to => { :action => :list }
17 17
18 18 def index
19 19 @user_count = User.count
20 20 if params[:page] == 'all'
21 21 @users = User.all
22 22 @paginated = false
23 23 else
24 24 @users = User.paginate :page => params[:page]
25 25 @paginated = true
26 26 end
27 27 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
28 28 @contests = Contest.enabled
29 29 end
30 30
31 31 def active
32 - sessions = ActiveRecord::SessionStore::Session.find(:all, :conditions => ["updated_at >= ?", 60.minutes.ago])
32 + sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
33 33 @users = []
34 34 sessions.each do |session|
35 35 if session.data[:user_id]
36 36 @users << User.find(session.data[:user_id])
37 37 end
38 38 end
39 39 end
40 40
41 41 def show
42 42 @user = User.find(params[:id])
43 43 end
44 44
45 45 def new
46 46 @user = User.new
47 47 end
48 48
49 49 def create
50 50 @user = User.new(params[:user])
51 51 @user.activated = true
52 52 if @user.save
53 53 flash[:notice] = 'User was successfully created.'
54 54 redirect_to :action => 'index'
55 55 else
56 56 render :action => 'new'
57 57 end
58 58 end
59 59
60 60 def clear_last_ip
61 61 @user = User.find(params[:id])
62 62 @user.last_ip = nil
63 63 @user.save
64 64 redirect_to action: 'index', page: params[:page]
65 65 end
66 66
67 67 def create_from_list
68 68 lines = params[:user_list]
69 69
70 70 note = []
71 71
72 72 lines.split("\n").each do |line|
73 73 items = line.chomp.split(',')
74 74 if items.length>=2
75 75 login = items[0]
76 76 full_name = items[1]
77 77
78 78 added_random_password = false
79 79 if items.length>=3
80 80 password = items[2].chomp(" ")
81 81 user_alias = (items.length>=4) ? items[3] : login
82 82 else
83 83 password = random_password
84 84 user_alias = (items.length>=4) ? items[3] : login
85 85 added_random_password = true
86 86 end
87 87
88 88 user = User.find_by_login(login)
89 89 if (user)
90 90 user.full_name = full_name
91 91 user.password = password
92 92 else
93 93 user = User.new({:login => login,
94 94 :full_name => full_name,
95 95 :password => password,
96 96 :password_confirmation => password,
97 97 :alias => user_alias})
98 98 end
99 99 user.activated = true
100 100 user.save
101 101
102 102 if added_random_password
103 103 note << "'#{login}' (+)"
104 104 else
105 105 note << login
106 106 end
107 107 end
108 108 end
109 109 flash[:notice] = 'User(s) ' + note.join(', ') +
110 110 ' were successfully created. ' +
111 111 '( (+) - created with random passwords.)'
112 112 redirect_to :action => 'index'
113 113 end
114 114
115 115 def edit
116 116 @user = User.find(params[:id])
117 117 end
118 118
119 119 def update
120 120 @user = User.find(params[:id])
121 - if @user.update_attributes(params[:user])
121 + if @user.update_attributes(user_params)
122 122 flash[:notice] = 'User was successfully updated.'
123 123 redirect_to :action => 'show', :id => @user
124 124 else
125 125 render :action => 'edit'
126 126 end
127 127 end
128 128
129 129 def destroy
130 130 User.find(params[:id]).destroy
131 131 redirect_to :action => 'index'
132 132 end
133 133
134 134 def user_stat
135 135 if params[:commit] == 'download csv'
136 136 @problems = Problem.all
137 137 else
138 - @problems = Problem.find_available_problems
138 + @problems = Problem.available_problems
139 139 end
140 - @users = User.includes(:contests, :contest_stat).where(enabled: true) #find(:all, :include => [:contests, :contest_stat]).where(enabled: true)
140 + @users = User.includes(:contests, :contest_stat).where(enabled: true)
141 141 @scorearray = Array.new
142 142 @users.each do |u|
143 143 ustat = Array.new
144 144 ustat[0] = u
145 145 @problems.each do |p|
146 146 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
147 147 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
148 148 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
149 149 else
150 150 ustat << [0,false]
151 151 end
152 152 end
153 153 @scorearray << ustat
154 154 end
155 155 if params[:commit] == 'download csv' then
156 156 csv = gen_csv_from_scorearray(@scorearray,@problems)
157 157 send_data csv, filename: 'last_score.csv'
158 158 else
159 159 render template: 'user_admin/user_stat'
160 160 end
161 161 end
162 162
163 163 def user_stat_max
164 164 if params[:commit] == 'download csv'
165 165 @problems = Problem.all
166 166 else
167 - @problems = Problem.find_available_problems
167 + @problems = Problem.available_problems
168 168 end
169 - @users = User.find(:all, :include => [:contests, :contest_stat])
169 + @users = User.includes(:contests).includes(:contest_stat).all
170 170 @scorearray = Array.new
171 171 #set up range from param
172 172 since_id = params.fetch(:since_id, 0).to_i
173 173 until_id = params.fetch(:until_id, 0).to_i
174 174 @users.each do |u|
175 175 ustat = Array.new
176 176 ustat[0] = u
177 177 @problems.each do |p|
178 178 max_points = 0
179 179 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
180 180 max_points = sub.points if sub and sub.points and (sub.points > max_points)
181 181 end
182 182 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
183 183 end
184 184 @scorearray << ustat
185 185 end
186 186
187 187 if params[:commit] == 'download csv' then
188 188 csv = gen_csv_from_scorearray(@scorearray,@problems)
189 189 send_data csv, filename: 'max_score.csv'
190 190 else
191 191 render template: 'user_admin/user_stat'
192 192 end
193 193 end
194 194
195 195 def import
196 196 if params[:file]==''
197 197 flash[:notice] = 'Error importing no file'
198 198 redirect_to :action => 'index' and return
199 199 end
200 200 import_from_file(params[:file])
201 201 end
202 202
203 203 def random_all_passwords
204 - users = User.find(:all)
204 + users = User.all
205 205 @prefix = params[:prefix] || ''
206 206 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
207 207 @changed = false
208 208 if request.request_method == 'POST'
209 209 @non_admin_users.each do |user|
210 210 password = random_password
211 211 user.password = password
212 212 user.password_confirmation = password
213 213 user.save
214 214 end
215 215 @changed = true
216 216 end
217 217 end
218 218
219 219 # contest management
220 220
221 221 def contests
222 222 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
223 223 @contests = Contest.enabled
224 224 end
225 225
226 226 def assign_from_list
227 227 contest_id = params[:users_contest_id]
228 228 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
229 229 contest = Contest.find(params[:new_contest][:id])
230 230 if !contest
231 231 flash[:notice] = 'Error: no contest'
232 232 redirect_to :action => 'contests', :id =>contest_id
233 233 end
234 234
235 235 note = []
236 236 users.each do |u|
237 237 u.contests = [contest]
238 238 note << u.login
239 239 end
240 240 flash[:notice] = 'User(s) ' + note.join(', ') +
241 241 " were successfully reassigned to #{contest.title}."
242 242 redirect_to :action => 'contests', :id =>contest.id
243 243 end
244 244
245 245 def add_to_contest
246 246 user = User.find(params[:id])
247 247 contest = Contest.find(params[:contest_id])
248 248 if user and contest
249 249 user.contests << contest
250 250 end
251 251 redirect_to :action => 'index'
252 252 end
253 253
254 254 def remove_from_contest
255 255 user = User.find(params[:id])
256 256 contest = Contest.find(params[:contest_id])
257 257 if user and contest
258 258 user.contests.delete(contest)
259 259 end
260 260 redirect_to :action => 'index'
261 261 end
262 262
263 263 def contest_management
264 264 end
265 265
266 266 def manage_contest
267 267 contest = Contest.find(params[:contest][:id])
268 268 if !contest
269 269 flash[:notice] = 'You did not choose the contest.'
270 270 redirect_to :action => 'contest_management' and return
271 271 end
272 272
273 273 operation = params[:operation]
274 274
275 275 if not ['add','remove','assign'].include? operation
276 276 flash[:notice] = 'You did not choose the operation to perform.'
277 277 redirect_to :action => 'contest_management' and return
278 278 end
279 279
280 280 lines = params[:login_list]
281 281 if !lines or lines.blank?
282 282 flash[:notice] = 'You entered an empty list.'
283 283 redirect_to :action => 'contest_management' and return
284 284 end
285 285
286 286 note = []
287 287 users = []
288 288 lines.split("\n").each do |line|
289 289 user = User.find_by_login(line.chomp)
290 290 if user
291 291 if operation=='add'
292 292 if ! user.contests.include? contest
293 293 user.contests << contest
294 294 end
295 295 elsif operation=='remove'
296 296 user.contests.delete(contest)
297 297 else
298 298 user.contests = [contest]
299 299 end
300 300
301 301 if params[:reset_timer]
302 302 user.contest_stat.forced_logout = true
303 303 user.contest_stat.reset_timer_and_save
304 304 end
305 305
306 306 if params[:notification_emails]
307 307 send_contest_update_notification_email(user, contest)
308 308 end
309 309
310 310 note << user.login
311 311 users << user
312 312 end
313 313 end
314 314
315 315 if params[:reset_timer]
316 316 logout_users(users)
317 317 end
318 318
319 319 flash[:notice] = 'User(s) ' + note.join(', ') +
320 320 ' were successfully modified. '
321 321 redirect_to :action => 'contest_management'
322 322 end
323 323
324 324 # admin management
325 325
326 326 def admin
327 - @admins = User.find(:all).find_all {|user| user.admin? }
327 + @admins = User.all.find_all {|user| user.admin? }
328 328 end
329 329
330 330 def grant_admin
331 331 login = params[:login]
332 332 user = User.find_by_login(login)
333 333 if user!=nil
334 334 admin_role = Role.find_by_name('admin')
335 335 user.roles << admin_role
336 336 else
337 337 flash[:notice] = 'Unknown user'
338 338 end
339 339 flash[:notice] = 'User added as admins'
340 340 redirect_to :action => 'admin'
341 341 end
342 342
343 343 def revoke_admin
344 344 user = User.find(params[:id])
345 345 if user==nil
346 346 flash[:notice] = 'Unknown user'
347 347 redirect_to :action => 'admin' and return
348 348 elsif user.login == 'root'
349 349 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
350 350 redirect_to :action => 'admin' and return
351 351 end
352 352
353 353 admin_role = Role.find_by_name('admin')
354 354 user.roles.delete(admin_role)
355 355 flash[:notice] = 'User permission revoked'
356 356 redirect_to :action => 'admin'
357 357 end
358 358
359 359 # mass mailing
360 360
361 361 def mass_mailing
362 362 end
363 363
364 364 def bulk_mail
365 365 lines = params[:login_list]
366 366 if !lines or lines.blank?
367 367 flash[:notice] = 'You entered an empty list.'
368 368 redirect_to :action => 'mass_mailing' and return
369 369 end
370 370
371 371 mail_subject = params[:subject]
372 372 if !mail_subject or mail_subject.blank?
373 373 flash[:notice] = 'You entered an empty mail subject.'
374 374 redirect_to :action => 'mass_mailing' and return
375 375 end
376 376
377 377 mail_body = params[:email_body]
378 378 if !mail_body or mail_body.blank?
379 379 flash[:notice] = 'You entered an empty mail body.'
380 380 redirect_to :action => 'mass_mailing' and return
381 381 end
382 382
383 383 note = []
384 384 users = []
385 385 lines.split("\n").each do |line|
386 386 user = User.find_by_login(line.chomp)
387 387 if user
388 388 send_mail(user.email, mail_subject, mail_body)
389 389 note << user.login
390 390 end
391 391 end
392 392
393 393 flash[:notice] = 'User(s) ' + note.join(', ') +
394 394 ' were successfully modified. '
395 395 redirect_to :action => 'mass_mailing'
396 396 end
397 397
398 398 protected
399 399
400 400 def random_password(length=5)
401 401 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
402 402 newpass = ""
403 403 length.times { newpass << chars[rand(chars.size-1)] }
404 404 return newpass
405 405 end
406 406
407 407 def import_from_file(f)
408 408 data_hash = YAML.load(f)
409 409 @import_log = ""
410 410
411 411 country_data = data_hash[:countries]
412 412 site_data = data_hash[:sites]
413 413 user_data = data_hash[:users]
414 414
415 415 # import country
416 416 countries = {}
417 417 country_data.each_pair do |id,country|
418 418 c = Country.find_by_name(country[:name])
419 419 if c!=nil
420 420 countries[id] = c
421 421 @import_log << "Found #{country[:name]}\n"
422 422 else
423 423 countries[id] = Country.new(:name => country[:name])
424 424 countries[id].save
425 425 @import_log << "Created #{country[:name]}\n"
426 426 end
427 427 end
428 428
429 429 # import sites
430 430 sites = {}
431 431 site_data.each_pair do |id,site|
432 432 s = Site.find_by_name(site[:name])
433 433 if s!=nil
434 434 @import_log << "Found #{site[:name]}\n"
435 435 else
436 436 s = Site.new(:name => site[:name])
437 437 @import_log << "Created #{site[:name]}\n"
438 438 end
439 439 s.password = site[:password]
440 440 s.country = countries[site[:country_id]]
441 441 s.save
442 442 sites[id] = s
443 443 end
444 444
445 445 # import users
446 446 user_data.each_pair do |id,user|
447 447 u = User.find_by_login(user[:login])
448 448 if u!=nil
449 449 @import_log << "Found #{user[:login]}\n"
450 450 else
451 451 u = User.new(:login => user[:login])
452 452 @import_log << "Created #{user[:login]}\n"
453 453 end
454 454 u.full_name = user[:name]
455 455 u.password = user[:password]
456 456 u.country = countries[user[:country_id]]
457 457 u.site = sites[user[:site_id]]
458 458 u.activated = true
459 459 u.email = "empty-#{u.login}@none.com"
460 460 if not u.save
461 461 @import_log << "Errors\n"
462 462 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
463 463 end
464 464 end
465 465
466 466 end
467 467
468 468 def logout_users(users)
469 469 users.each do |user|
470 470 contest_stat = user.contest_stat(true)
471 471 if contest_stat and !contest_stat.forced_logout
472 472 contest_stat.forced_logout = true
473 473 contest_stat.save
474 474 end
475 475 end
476 476 end
477 477
478 478 def send_contest_update_notification_email(user, contest)
479 479 contest_title_name = GraderConfiguration['contest.name']
480 480 contest_name = contest.name
481 481 mail_subject = t('contest.notification.email_subject', {
482 482 :contest_title_name => contest_title_name,
483 483 :contest_name => contest_name })
484 484 mail_body = t('contest.notification.email_body', {
485 485 :full_name => user.full_name,
486 486 :contest_title_name => contest_title_name,
487 487 :contest_name => contest.name,
488 488 })
489 489
490 490 logger.info mail_body
491 491 send_mail(user.email, mail_subject, mail_body)
492 492 end
493 493
494 494 def find_contest_and_user_from_contest_id(id)
495 495 if id!='none'
496 496 @contest = Contest.find(id)
497 497 else
498 498 @contest = nil
499 499 end
500 500 if @contest
501 501 @users = @contest.users
502 502 else
503 503 @users = User.find_users_with_no_contest
504 504 end
505 505 return [@contest, @users]
506 506 end
507 507
508 508 def gen_csv_from_scorearray(scorearray,problem)
509 509 CSV.generate do |csv|
510 510 #add header
511 511 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
512 512 problem.each { |p| header << p.name }
513 513 header += ['Total','Passed']
514 514 csv << header
515 515 #add data
516 516 scorearray.each do |sc|
517 517 total = num_passed = 0
518 518 row = Array.new
519 519 sc.each_index do |i|
520 520 if i == 0
521 521 row << sc[i].login
522 522 row << sc[i].full_name
523 523 row << sc[i].activated
524 524 row << (sc[i].try(:contest_stat).try(:started_at).nil? ? 'no' : 'yes')
525 525 row << sc[i].contests.collect {|c| c.name}.join(', ')
526 526 else
527 527 row << sc[i][0]
528 528 total += sc[i][0]
529 529 num_passed += 1 if sc[i][1]
530 530 end
531 531 end
532 532 row << total
533 533 row << num_passed
534 534 csv << row
535 535 end
536 536 end
537 537 end
538 +
539 + private
540 + def user_params
541 + params.require(:user).permit(:login,:full_name,:hashed_password,:salt,:alias,:email,:site_id,:country_id,:activated,:enabled,:remark,:last_ip,:section)
538 542 end
543 + end
@@ -1,21 +1,15
1 1 class Announcement < ActiveRecord::Base
2 2
3 - def self.find_published(contest_started=false)
3 + def self.published(contest_started=false)
4 4 if contest_started
5 - Announcement.find(:all,
6 - :conditions => "(published = 1) AND (frontpage = 0)",
7 - :order => "created_at DESC")
5 + where(published: true).where(frontpage: false).order(created_at: :desc)
8 6 else
9 - Announcement.find(:all,
10 - :conditions => "(published = 1) AND (frontpage = 0) AND (contest_only = 0)",
11 - :order => "created_at DESC")
7 + where(published: true).where(frontpage: false).where(contest_only: false).order(created_at: :desc)
12 8 end
13 9 end
14 10
15 - def self.find_for_frontpage
16 - Announcement.find(:all,
17 - :conditions => "(published = 1) AND (frontpage = 1)",
18 - :order => "created_at DESC")
11 + def self.frontpage
12 + where(published: 1).where(frontpage: 1).order(created_at: :desc)
19 13 end
20 14
21 15 end
@@ -1,8 +1,8
1 1 class Contest < ActiveRecord::Base
2 2
3 3 has_and_belongs_to_many :users
4 4 has_and_belongs_to_many :problems
5 5
6 - scope :enabled, :conditions => {:enabled => true}
6 + scope :enabled, -> { where(enabled: true) }
7 7
8 8 end
@@ -1,177 +1,177
1 1 require 'yaml'
2 2
3 3 #
4 4 # This class also contains various login of the system.
5 5 #
6 6 class GraderConfiguration < ActiveRecord::Base
7 7
8 8 SYSTEM_MODE_CONF_KEY = 'system.mode'
9 9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
10 10 MULTICONTESTS_KEY = 'system.multicontests'
11 11 CONTEST_TIME_LIMIT_KEY = 'contest.time_limit'
12 12 MULTIPLE_IP_LOGIN_KEY = 'right.multiple_ip_login'
13 13
14 14 cattr_accessor :config_cache
15 15 cattr_accessor :task_grading_info_cache
16 16 cattr_accessor :contest_time_str
17 17 cattr_accessor :contest_time
18 18
19 19 GraderConfiguration.config_cache = nil
20 20 GraderConfiguration.task_grading_info_cache = nil
21 21
22 22 def self.config_cached?
23 23 (defined? CONFIGURATION_CACHE_ENABLED) and (CONFIGURATION_CACHE_ENABLED)
24 24 end
25 25
26 26 def self.get(key)
27 27 if GraderConfiguration.config_cached?
28 28 if GraderConfiguration.config_cache == nil
29 29 self.read_config
30 30 end
31 31 return GraderConfiguration.config_cache[key]
32 32 else
33 33 return GraderConfiguration.read_one_key(key)
34 34 end
35 35 end
36 36
37 37 def self.[](key)
38 38 self.get(key)
39 39 end
40 40
41 41 def self.reload
42 42 self.read_config
43 43 end
44 44
45 45 def self.clear
46 46 GraderConfiguration.config_cache = nil
47 47 end
48 48
49 49 #
50 50 # View decision
51 51 #
52 52 def self.show_submitbox_to?(user)
53 53 mode = get(SYSTEM_MODE_CONF_KEY)
54 54 return false if mode=='analysis'
55 55 if (mode=='contest')
56 56 return false if (user.site!=nil) and
57 57 ((user.site.started!=true) or (user.site.finished?))
58 58 end
59 59 return true
60 60 end
61 61
62 62 def self.show_tasks_to?(user)
63 63 if time_limit_mode?
64 64 return false if not user.contest_started?
65 65 end
66 66 return true
67 67 end
68 68
69 69 def self.show_grading_result
70 70 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
71 71 end
72 72
73 73 def self.allow_test_request(user)
74 74 mode = get(SYSTEM_MODE_CONF_KEY)
75 75 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
76 76 if (mode=='contest')
77 77 return false if ((user.site!=nil) and
78 78 ((user.site.started!=true) or
79 79 (early_timeout and (user.site.time_left < 30.minutes))))
80 80 end
81 81 return false if mode=='analysis'
82 82 return true
83 83 end
84 84
85 85 def self.task_grading_info
86 86 if GraderConfiguration.task_grading_info_cache==nil
87 87 read_grading_info
88 88 end
89 89 return GraderConfiguration.task_grading_info_cache
90 90 end
91 91
92 92 def self.standard_mode?
93 93 return get(SYSTEM_MODE_CONF_KEY) == 'standard'
94 94 end
95 95
96 96 def self.contest_mode?
97 97 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
98 98 end
99 99
100 100 def self.indv_contest_mode?
101 101 return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
102 102 end
103 103
104 104 def self.multicontests?
105 105 return get(MULTICONTESTS_KEY) == true
106 106 end
107 107
108 108 def self.time_limit_mode?
109 109 mode = get(SYSTEM_MODE_CONF_KEY)
110 110 return ((mode == 'contest') or (mode == 'indv-contest'))
111 111 end
112 112
113 113 def self.analysis_mode?
114 114 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
115 115 end
116 116
117 117 def self.contest_time_limit
118 118 contest_time_str = GraderConfiguration[CONTEST_TIME_LIMIT_KEY]
119 119
120 120 if not defined? GraderConfiguration.contest_time_str
121 121 GraderConfiguration.contest_time_str = nil
122 122 end
123 123
124 124 if GraderConfiguration.contest_time_str != contest_time_str
125 125 GraderConfiguration.contest_time_str = contest_time_str
126 126 if tmatch = /(\d+):(\d+)/.match(contest_time_str)
127 127 h = tmatch[1].to_i
128 128 m = tmatch[2].to_i
129 129
130 130 GraderConfiguration.contest_time = h.hour + m.minute
131 131 else
132 132 GraderConfiguration.contest_time = nil
133 133 end
134 134 end
135 135 return GraderConfiguration.contest_time
136 136 end
137 137
138 138 protected
139 139
140 140 def self.convert_type(val,type)
141 141 case type
142 142 when 'string'
143 143 return val
144 144
145 145 when 'integer'
146 146 return val.to_i
147 147
148 148 when 'boolean'
149 149 return (val=='true')
150 150 end
151 151 end
152 152
153 153 def self.read_config
154 154 GraderConfiguration.config_cache = {}
155 - GraderConfiguration.find(:all).each do |conf|
155 + GraderConfiguration.all.each do |conf|
156 156 key = conf.key
157 157 val = conf.value
158 158 GraderConfiguration.config_cache[key] = GraderConfiguration.convert_type(val,conf.value_type)
159 159 end
160 160 end
161 161
162 162 def self.read_one_key(key)
163 163 conf = GraderConfiguration.find_by_key(key)
164 164 if conf
165 165 return GraderConfiguration.convert_type(conf.value,conf.value_type)
166 166 else
167 167 return nil
168 168 end
169 169 end
170 170
171 171 def self.read_grading_info
172 172 f = File.open(TASK_GRADING_INFO_FILENAME)
173 173 GraderConfiguration.task_grading_info_cache = YAML.load(f)
174 174 f.close
175 175 end
176 176
177 177 end
@@ -1,81 +1,72
1 1 class GraderProcess < ActiveRecord::Base
2 2
3 3 def self.find_by_host_and_pid(host,pid)
4 - return GraderProcess.find(:first,
5 - :conditions => {
6 - :host => host,
7 - :pid => pid
8 - })
4 + return GraderProcess.where(host:host).where(pid: pid).first
9 5 end
10 6
11 7 def self.register(host,pid,mode)
12 8 grader = GraderProcess.find_by_host_and_pid(host,pid)
13 9 if grader
14 10 grader.mode = mode
15 11 grader.active = nil
16 12 grader.task_id = nil
17 13 grader.task_type = nil
18 14 grader.terminated = false
19 15 grader.save
20 16 else
21 17 grader = GraderProcess.create(:host => host,
22 18 :pid => pid,
23 19 :mode => mode,
24 20 :terminated => false)
25 21 end
26 22 grader
27 23 end
28 24
29 25 def self.find_running_graders
30 - GraderProcess.find(:all,
31 - :conditions => {:terminated => 0})
26 + where(terminated: false)
32 27 end
33 28
34 29 def self.find_terminated_graders
35 - GraderProcess.find(:all,
36 - :conditions => "`terminated`")
30 + where(terminated: true)
37 31 end
38 32
39 33 def self.find_stalled_process
40 - GraderProcess.find(:all,
41 - :conditions => ["(`terminated` = 0) AND active AND " +
42 - "(updated_at < ?)",
43 - Time.now.gmtime - GraderProcess.stalled_time])
34 + where(terminated: false).where(active: true).where("updated_at < ?",Time.now.gmtime - GraderProcess.stalled_time)
44 35 end
45 36
46 37 def report_active(task=nil)
47 38 self.active = true
48 39 if task!=nil
49 40 self.task_id = task.id
50 41 self.task_type = task.class.to_s
51 42 else
52 43 self.task_id = nil
53 44 self.task_type = nil
54 45 end
55 46 self.save
56 47 end
57 48
58 49 def report_inactive(task=nil)
59 50 self.active = false
60 51 if task!=nil
61 52 self.task_id = task.id
62 53 self.task_type = task.class.to_s
63 54 else
64 55 self.task_id = nil
65 56 self.task_type = nil
66 57 end
67 58 self.save
68 59 end
69 60
70 61 def terminate
71 62 self.terminated = true
72 63 self.save
73 64 end
74 65
75 66 protected
76 67
77 68 def self.stalled_time()
78 69 return 1.minute
79 70 end
80 71
81 72 end
@@ -1,24 +1,24
1 1 class Language < ActiveRecord::Base
2 2
3 3 @@languages_by_ext = {}
4 4
5 5 def self.cache_ext_hash
6 6 @@languages_by_ext = {}
7 - Language.find(:all).each do |language|
7 + Language.all.each do |language|
8 8 language.common_ext.split(',').each do |ext|
9 9 @@languages_by_ext[ext] = language
10 10 end
11 11 end
12 12 end
13 13
14 14 def self.find_by_extension(ext)
15 15 if @@languages_by_ext.length == 0
16 16 Language.cache_ext_hash
17 17 end
18 18 if @@languages_by_ext.has_key? ext
19 19 return @@languages_by_ext[ext]
20 20 else
21 21 return nil
22 22 end
23 23 end
24 24 end
@@ -1,5 +1,4
1 1 class Login < ActiveRecord::Base
2 2 belongs_to :user
3 3
4 - attr_accessible :ip_address, :logged_in_at, :user_id
5 4 end
@@ -1,60 +1,58
1 1 class Message < ActiveRecord::Base
2 2
3 3 belongs_to :sender, :class_name => "User"
4 4 belongs_to :receiver, :class_name => "User"
5 5
6 6 belongs_to :replying_message, :class_name => "Message"
7 7
8 8 # commented manually do it
9 9 #
10 10 #has_many :replied_messages, {
11 11 # :class_name => "Message",
12 12 # :foreign_key => "replying_message_id"
13 13 #}
14 14 #
15 15
16 16 attr_accessor :replied_messages
17 17
18 18 def self.find_all_sent_by_user(user)
19 19 messages = user.messages
20 20 replied_messages = user.replied_messages
21 21 Message.build_replying_message_hierarchy messages, replied_messages
22 22 return messages
23 23 end
24 24
25 25 def self.find_all_system_unreplied_messages
26 - self.find(:all,
27 - :conditions => 'ISNULL(receiver_id) ' +
28 - 'AND (ISNULL(replied) OR replied=0)',
29 - :order => 'created_at')
26 + where('ISNULL(receiver_id) ' +
27 + 'AND (ISNULL(replied) OR replied=0)')
30 28 end
31 29
32 30 def self.build_replying_message_hierarchy(*args)
33 31 # manually build replies hierarchy (to improve efficiency)
34 32 all_messages = {}
35 33
36 34 args.each do |collection|
37 35 collection.each do |m|
38 36 all_messages[m.id] = m
39 37 m.replied_messages = []
40 38 end
41 39 end
42 40
43 41 all_messages.each_value do |m|
44 42 rep_id = m.replying_message_id
45 43 if all_messages[rep_id]!=nil
46 44 all_messages[rep_id].add_replied_message(m)
47 45 end
48 46 end
49 47 end
50 48
51 49 def add_replied_message(m)
52 50 if @replied_messages==nil
53 51 @replied_messages = [m]
54 52 else
55 53 @replied_messages << m
56 54 end
57 55 @replied_messages
58 56 end
59 57
60 58 end
@@ -1,133 +1,134
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 has_many :testcases, :dependent => :destroy
7 7
8 8 validates_presence_of :name
9 - validates_format_of :name, :with => /^\w+$/
9 + validates_format_of :name, :with => /\A\w+\z/
10 10 validates_presence_of :full_name
11 11
12 - scope :available, :conditions => {:available => true}
12 + scope :available, -> { where(available: true) }
13 13
14 14 DEFAULT_TIME_LIMIT = 1
15 15 DEFAULT_MEMORY_LIMIT = 32
16 16
17 - def self.find_available_problems
18 - Problem.available.all(:order => "date_added DESC, name ASC")
17 + def self.available_problems
18 + available.order(date_added: :desc).order(:name)
19 + #Problem.available.all(:order => "date_added DESC, name ASC")
19 20 end
20 21
21 22 def self.create_from_import_form_params(params, old_problem=nil)
22 23 org_problem = old_problem || Problem.new
23 24 import_params, problem = Problem.extract_params_and_check(params,
24 25 org_problem)
25 26
26 27 if !problem.errors.empty?
27 28 return problem, 'Error importing'
28 29 end
29 30
30 31 problem.full_score = 100
31 32 problem.date_added = Time.new
32 33 problem.test_allowed = true
33 34 problem.output_only = false
34 35 problem.available = false
35 36
36 37 if not problem.save
37 38 return problem, 'Error importing'
38 39 end
39 40
40 41 import_to_db = params.has_key? :import_to_db
41 42
42 43 importer = TestdataImporter.new(problem)
43 44
44 45 if not importer.import_from_file(import_params[:file],
45 46 import_params[:time_limit],
46 47 import_params[:memory_limit],
47 48 import_params[:checker_name],
48 49 import_to_db)
49 50 problem.errors.add(:base,'Import error.')
50 51 end
51 52
52 53 return problem, importer.log_msg
53 54 end
54 55
55 56 def self.download_file_basedir
56 57 return "#{Rails.root}/data/tasks"
57 58 end
58 59
59 60 def get_submission_stat
60 61 result = Hash.new
61 62 #total number of submission
62 63 result[:total_sub] = Submission.where(problem_id: self.id).count
63 64 result[:attempted_user] = Submission.where(problem_id: self.id).group_by(:user_id)
64 65 end
65 66
66 67 def long_name
67 68 "[#{name}] #{full_name}"
68 69 end
69 70
70 71 protected
71 72
72 73 def self.to_i_or_default(st, default)
73 74 if st!=''
74 75 result = st.to_i
75 76 end
76 77 result ||= default
77 78 end
78 79
79 80 def self.to_f_or_default(st, default)
80 81 if st!=''
81 82 result = st.to_f
82 83 end
83 84 result ||= default
84 85 end
85 86
86 87 def self.extract_params_and_check(params, problem)
87 88 time_limit = Problem.to_f_or_default(params[:time_limit],
88 89 DEFAULT_TIME_LIMIT)
89 90 memory_limit = Problem.to_i_or_default(params[:memory_limit],
90 91 DEFAULT_MEMORY_LIMIT)
91 92
92 93 if time_limit<=0 or time_limit >60
93 94 problem.errors.add(:base,'Time limit out of range.')
94 95 end
95 96
96 97 if memory_limit==0 and params[:memory_limit]!='0'
97 98 problem.errors.add(:base,'Memory limit format errors.')
98 99 elsif memory_limit<=0 or memory_limit >512
99 100 problem.errors.add(:base,'Memory limit out of range.')
100 101 end
101 102
102 103 if params[:file]==nil or params[:file]==''
103 104 problem.errors.add(:base,'No testdata file.')
104 105 end
105 106
106 107 checker_name = 'text'
107 108 if ['text','float'].include? params[:checker]
108 109 checker_name = params[:checker]
109 110 end
110 111
111 112 file = params[:file]
112 113
113 114 if !problem.errors.empty?
114 115 return nil, problem
115 116 end
116 117
117 118 problem.name = params[:name]
118 119 if params[:full_name]!=''
119 120 problem.full_name = params[:full_name]
120 121 else
121 122 problem.full_name = params[:name]
122 123 end
123 124
124 125 return [{
125 126 :time_limit => time_limit,
126 127 :memory_limit => memory_limit,
127 128 :file => file,
128 129 :checker_name => checker_name
129 130 },
130 131 problem]
131 132 end
132 133
133 134 end
@@ -1,173 +1,160
1 1 class Submission < ActiveRecord::Base
2 2
3 3 belongs_to :language
4 4 belongs_to :problem
5 5 belongs_to :user
6 6
7 7 before_validation :assign_problem
8 8 before_validation :assign_language
9 9
10 10 validates_presence_of :source
11 11 validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'too long'
12 12 validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short'
13 13 validate :must_have_valid_problem
14 14 validate :must_specify_language
15 15
16 16 before_save :assign_latest_number_if_new_recond
17 17
18 18 def self.find_last_by_user_and_problem(user_id, problem_id)
19 - last_sub = find(:first,
20 - :conditions => {:user_id => user_id,
21 - :problem_id => problem_id},
22 - :order => 'number DESC')
23 - return last_sub
19 + where("user_id = ? AND problem_id = ?",user_id,problem_id).last
24 20 end
25 21
26 22 def self.find_all_last_by_problem(problem_id)
27 23 # need to put in SQL command, maybe there's a better way
28 24 Submission.includes(:user).find_by_sql("SELECT * FROM submissions " +
29 25 "WHERE id = " +
30 26 "(SELECT MAX(id) FROM submissions AS subs " +
31 27 "WHERE subs.user_id = submissions.user_id AND " +
32 28 "problem_id = " + problem_id.to_s + " " +
33 29 "GROUP BY user_id) " +
34 30 "ORDER BY user_id")
35 31 end
36 32
37 33 def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id)
38 34 records = Submission.where(problem_id: problem_id,user_id: user_id)
39 35 records = records.where('id >= ?',since_id) if since_id > 0
40 36 records = records.where('id <= ?',until_id) if until_id > 0
41 37 records.all
42 38 end
43 39
44 40 def self.find_last_for_all_available_problems(user_id)
45 41 submissions = Array.new
46 - problems = Problem.find_available_problems
42 + problems = Problem.available_problems
47 43 problems.each do |problem|
48 44 sub = Submission.find_last_by_user_and_problem(user_id, problem.id)
49 45 submissions << sub if sub!=nil
50 46 end
51 47 submissions
52 48 end
53 49
54 50 def self.find_by_user_problem_number(user_id, problem_id, number)
55 - Submission.find(:first,
56 - :conditions => {
57 - :user_id => user_id,
58 - :problem_id => problem_id,
59 - :number => number
60 - })
51 + where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first
61 52 end
62 53
63 54 def self.find_all_by_user_problem(user_id, problem_id)
64 - Submission.find(:all,
65 - :conditions => {
66 - :user_id => user_id,
67 - :problem_id => problem_id,
68 - })
55 + where("user_id = ? AND problem_id = ?",user_id,problem_id)
69 56 end
70 57
71 58 def download_filename
72 59 if self.problem.output_only
73 60 return self.source_filename
74 61 else
75 62 timestamp = self.submitted_at.localtime.strftime("%H%M%S")
76 63 return "#{self.problem.name}-#{timestamp}.#{self.language.ext}"
77 64 end
78 65 end
79 66
80 67 protected
81 68
82 69 def self.find_option_in_source(option, source)
83 70 if source==nil
84 71 return nil
85 72 end
86 73 i = 0
87 74 source.each_line do |s|
88 75 if s =~ option
89 76 words = s.split
90 77 return words[1]
91 78 end
92 79 i = i + 1
93 80 if i==10
94 81 return nil
95 82 end
96 83 end
97 84 return nil
98 85 end
99 86
100 87 def self.find_language_in_source(source, source_filename="")
101 88 langopt = find_option_in_source(/^LANG:/,source)
102 89 if langopt
103 90 return (Language.find_by_name(langopt) ||
104 91 Language.find_by_pretty_name(langopt))
105 92 else
106 93 if source_filename
107 94 return Language.find_by_extension(source_filename.split('.').last)
108 95 else
109 96 return nil
110 97 end
111 98 end
112 99 end
113 100
114 101 def self.find_problem_in_source(source, source_filename="")
115 102 prob_opt = find_option_in_source(/^TASK:/,source)
116 103 if problem = Problem.find_by_name(prob_opt)
117 104 return problem
118 105 else
119 106 if source_filename
120 107 return Problem.find_by_name(source_filename.split('.').first)
121 108 else
122 109 return nil
123 110 end
124 111 end
125 112 end
126 113
127 114 def assign_problem
128 115 if self.problem_id!=-1
129 116 begin
130 117 self.problem = Problem.find(self.problem_id)
131 118 rescue ActiveRecord::RecordNotFound
132 119 self.problem = nil
133 120 end
134 121 else
135 122 self.problem = Submission.find_problem_in_source(self.source,
136 123 self.source_filename)
137 124 end
138 125 end
139 126
140 127 def assign_language
141 128 self.language = Submission.find_language_in_source(self.source,
142 129 self.source_filename)
143 130 end
144 131
145 132 # validation codes
146 133 def must_specify_language
147 134 return if self.source==nil
148 135
149 136 # for output_only tasks
150 137 return if self.problem!=nil and self.problem.output_only
151 138
152 139 if self.language==nil
153 140 errors.add('source',"Cannot detect language. Did you submit a correct source file?") unless self.language!=nil
154 141 end
155 142 end
156 143
157 144 def must_have_valid_problem
158 145 return if self.source==nil
159 146 if self.problem==nil
160 147 errors.add('problem',"must be specified.")
161 148 elsif (!self.problem.available) and (self.new_record?)
162 149 errors.add('problem',"must be valid.")
163 150 end
164 151 end
165 152
166 153 # callbacks
167 154 def assign_latest_number_if_new_recond
168 155 return if !self.new_record?
169 156 latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id)
170 157 self.number = (latest==nil) ? 1 : latest.number + 1;
171 158 end
172 159
173 160 end
@@ -1,3 +1,3
1 1 class SubmissionViewLog < ActiveRecord::Base
2 - attr_accessible :submission_id, :user_id
2 + #attr_accessible :submission_id, :user_id
3 3 end
@@ -1,68 +1,65
1 1 class Task < ActiveRecord::Base
2 2
3 3 belongs_to :submission
4 4
5 5 STATUS_GRADING = 0
6 6 STATUS_INQUEUE = 1
7 7 STATUS_COMPLETE = 2
8 8
9 9 def status_inqueue
10 10 self.status = Task::STATUS_INQUEUE
11 11 end
12 12
13 13 def status_inqueue!
14 14 status_inqueue
15 15 self.save
16 16 end
17 17
18 18 def status_grading
19 19 self.status = Task::STATUS_GRADING
20 20 end
21 21
22 22 def status_grading!
23 23 status_grading
24 24 self.save
25 25 end
26 26
27 27 def status_complete
28 28 self.status = Task::STATUS_COMPLETE
29 29 end
30 30
31 31 def status_complete!
32 32 status_complete
33 33 self.save
34 34 end
35 35
36 36 def status_str
37 37 case self.status
38 38 when Task::STATUS_INQUEUE
39 39 "inqueue"
40 40 when Task::STATUS_GRADING
41 41 "grading"
42 42 when Task::STATUS_COMPLETE
43 43 "complete"
44 44 end
45 45 end
46 46
47 47 def self.get_inqueue_and_change_status(status)
48 48 task = nil
49 49 begin
50 50 Task.transaction do
51 - task = Task.find(:first,
52 - :order => "created_at",
53 - :conditions => {:status=> Task::STATUS_INQUEUE},
54 - :lock => true)
51 + task = Task.where(status: Task::STATUS_INQUEUE).where(lock: true).first
55 52 if task!=nil
56 53 task.status = status
57 54 task.save!
58 55 end
59 56 end
60 57
61 58 rescue
62 59 task = nil
63 60
64 61 end
65 62 task
66 63 end
67 64
68 65 end
@@ -1,157 +1,154
1 1 #
2 2 # A TestRequest is a composition of submission with user's testdata.
3 3 #
4 4 # Note about TestRequest#problem: Usually, A TestRequest has to be
5 5 # associated with a problem, so that execution environment can be
6 6 # determined. However, to be more flexible, we have to ensure that
7 7 # it works as well with problem=nil. In this case, we shall provide
8 8 # a "default" execution environment for it. This can be done
9 9 # seamlessly by using TestRequest#problem_name or
10 10 # TestRequest#name_of(problem) when retrieving the name of the
11 11 # problem: #name_of would return problem.name when problem!=nil and
12 12 # it would return "default" when problem=nil, #problem_name just
13 13 # call #name_of.
14 14 #
15 15
16 16 require 'fileutils'
17 17
18 18 class TestRequest < Task
19 -
20 - set_table_name "test_requests"
19 + self.table_name = "test_requests"
21 20
22 21 belongs_to :user
23 22 belongs_to :problem
24 23 belongs_to :submission
25 24
26 25 validates_presence_of :submission
27 26 validate :must_have_valid_problem
28 27
29 28 def problem_name
30 29 TestRequest.name_of(self.problem)
31 30 end
32 31
33 32 def language
34 33 self.submission.language
35 34 end
36 35
37 36 def self.get_inqueue_and_change_status(status)
38 37 # since there will be only one grader grading TestRequest
39 38 # we do not need locking (hopefully)
40 39
41 - test_request = TestRequest.find(:first,
42 - :order => "created_at",
43 - :conditions => {:status=> Task::STATUS_INQUEUE})
40 + test_request = TestRequest.where(status: Task::STATUS_INQUEUE).first
44 41 if test_request!=nil
45 42 test_request.status = status
46 43 test_request.save!
47 44 end
48 45
49 46 test_request
50 47 end
51 48
52 49 # interfacing with form
53 50 def self.new_from_form_params(user,params)
54 51 test_request = TestRequest.new
55 52 test_request.user = user
56 53 begin
57 54 problem = Problem.find(params[:problem_id])
58 55 rescue ActiveRecord::RecordNotFound
59 56 problem = nil
60 57 end
61 58 test_request.problem = problem
62 59 if problem!=nil
63 60 test_request.submission =
64 61 Submission.find_by_user_problem_number(user.id,
65 62 problem.id,
66 63 params[:submission_number])
67 64 else
68 65 test_request.submission = nil
69 66 end
70 67
71 68 # checks if the user submits any input file
72 69 if params[:input_file]==nil or params[:input_file]==""
73 70 test_request.errors.add(:base,"No input submitted.")
74 71 test_request.input_file_name = nil
75 72 else
76 73 test_request.input_file_name = save_input_file(params[:input_file], user, problem)
77 74 if test_request.input_file_name == nil
78 75 test_request.errors.adds(:base,"No input submitted.")
79 76 end
80 77 if params[:additional_file]!=nil and params[:additional_file]!=""
81 78 save_additional_file(params[:additional_file],
82 79 "#{test_request.input_file_name}.files")
83 80 end
84 81 end
85 82 test_request.submitted_at = Time.new.gmtime
86 83 test_request.status_inqueue
87 84 test_request
88 85 end
89 86
90 87 protected
91 88
92 89 def self.name_of(problem)
93 90 if problem!=nil
94 91 problem.name
95 92 else
96 93 "default"
97 94 end
98 95 end
99 96
100 97 def self.random_input_file_name(user,problem)
101 98 problem_name = TestRequest.name_of(problem)
102 99 begin
103 100 tmpname = TEST_REQUEST_INPUT_FILE_DIR + "/#{user.login}/#{problem_name}/#{rand(10000)}"
104 101 end while File.exists?(tmpname)
105 102 tmpname
106 103 end
107 104
108 105 def self.save_input_file(tempfile, user, problem)
109 106 new_file_name = random_input_file_name(user,problem)
110 107 dirname = File.dirname(new_file_name)
111 108 FileUtils.mkdir_p(File.dirname(new_file_name)) if !File.exists?(dirname)
112 109
113 110 # when the user did not submit any file
114 111 return nil if tempfile==""
115 112
116 113 if tempfile.instance_of?(Tempfile)
117 114 tempfile.close
118 115 FileUtils.move(tempfile.path,new_file_name)
119 116 else
120 117 File.open(new_file_name, "wb") do |f|
121 118 f.write(tempfile.read)
122 119 end
123 120 end
124 121 new_file_name
125 122 end
126 123
127 124 def self.save_additional_file(tempfile,dir)
128 125 new_file_name = "#{dir}/#{tempfile.original_filename}"
129 126 dirname = File.dirname(new_file_name)
130 127 FileUtils.mkdir_p(File.dirname(new_file_name)) if !File.exists?(dirname)
131 128
132 129 # when the user did not submit any file
133 130 return nil if tempfile==""
134 131
135 132 if tempfile.instance_of?(Tempfile)
136 133 tempfile.close
137 134 FileUtils.move(tempfile.path,new_file_name)
138 135 else
139 136 File.open(new_file_name, "wb") do |f|
140 137 f.write(tempfile.read)
141 138 end
142 139 end
143 140 new_file_name
144 141 end
145 142
146 143 #
147 144 # validations
148 145 #
149 146 def must_have_valid_problem
150 147 if problem==nil
151 148 errors.add('problem',"must be specified.")
152 149 elsif (!problem.available) and (self.new_record?)
153 150 errors.add('problem',"must be valid.")
154 151 end
155 152 end
156 153
157 154 end
@@ -1,4 +1,4
1 1 class Testcase < ActiveRecord::Base
2 2 belongs_to :problem
3 - attr_accessible :group, :input, :num, :score, :sol
3 + #attr_accessible :group, :input, :num, :score, :sol
4 4 end
@@ -1,381 +1,379
1 1 require 'digest/sha1'
2 2 require 'net/pop'
3 3 require 'net/https'
4 4 require 'net/http'
5 5 require 'json'
6 6
7 7 class User < ActiveRecord::Base
8 8
9 9 has_and_belongs_to_many :roles
10 10
11 - has_many :test_requests, :order => "submitted_at DESC"
11 + has_many :test_requests, -> {order(submitted_at: DESC)}
12 12
13 - has_many :messages,
13 + has_many :messages, -> { order(created_at: DESC) },
14 14 :class_name => "Message",
15 - :foreign_key => "sender_id",
16 - :order => 'created_at DESC'
15 + :foreign_key => "sender_id"
17 16
18 - has_many :replied_messages,
17 + has_many :replied_messages, -> { order(created_at: DESC) },
19 18 :class_name => "Message",
20 - :foreign_key => "receiver_id",
21 - :order => 'created_at DESC'
19 + :foreign_key => "receiver_id"
22 20
23 21 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
24 22
25 23 belongs_to :site
26 24 belongs_to :country
27 25
28 - has_and_belongs_to_many :contests, :uniq => true, :order => 'name'
26 + has_and_belongs_to_many :contests, -> { order(:name); uniq}
29 27
30 - scope :activated_users, :conditions => {:activated => true}
28 + scope :activated_users, -> {where activated: true}
31 29
32 30 validates_presence_of :login
33 31 validates_uniqueness_of :login
34 - validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
32 + validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
35 33 validates_length_of :login, :within => 3..30
36 34
37 35 validates_presence_of :full_name
38 36 validates_length_of :full_name, :minimum => 1
39 37
40 38 validates_presence_of :password, :if => :password_required?
41 39 validates_length_of :password, :within => 4..20, :if => :password_required?
42 40 validates_confirmation_of :password, :if => :password_required?
43 41
44 42 validates_format_of :email,
45 43 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
46 44 :if => :email_validation?
47 45 validate :uniqueness_of_email_from_activated_users,
48 46 :if => :email_validation?
49 47 validate :enough_time_interval_between_same_email_registrations,
50 48 :if => :email_validation?
51 49
52 50 # these are for ytopc
53 51 # disable for now
54 52 #validates_presence_of :province
55 53
56 54 attr_accessor :password
57 55
58 56 before_save :encrypt_new_password
59 57 before_save :assign_default_site
60 58 before_save :assign_default_contest
61 59
62 60 # this is for will_paginate
63 61 cattr_reader :per_page
64 62 @@per_page = 50
65 63
66 64 def self.authenticate(login, password)
67 65 user = find_by_login(login)
68 66 if user
69 67 return user if user.authenticated?(password)
70 68 if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
71 69 user.password = password
72 70 user.save
73 71 return user
74 72 end
75 73 end
76 74 end
77 75
78 76 def authenticated?(password)
79 77 if self.activated
80 78 hashed_password == User.encrypt(password,self.salt)
81 79 else
82 80 false
83 81 end
84 82 end
85 83
86 84 def authenticated_by_pop3?(password)
87 85 Net::POP3.enable_ssl
88 86 pop = Net::POP3.new('pops.it.chula.ac.th')
89 87 authen = true
90 88 begin
91 89 pop.start(login, password)
92 90 pop.finish
93 91 return true
94 92 rescue
95 93 return false
96 94 end
97 95 end
98 96
99 97 def authenticated_by_cucas?(password)
100 98 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
101 99 appid = '41508763e340d5858c00f8c1a0f5a2bb'
102 100 appsecret ='d9cbb5863091dbe186fded85722a1e31'
103 101 post_args = {
104 102 'appid' => appid,
105 103 'appsecret' => appsecret,
106 104 'username' => login,
107 105 'password' => password
108 106 }
109 107
110 108 #simple call
111 109 begin
112 110 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
113 111 http.use_ssl = true
114 112 http.verify_mode = OpenSSL::SSL::VERIFY_NONE
115 113 result = [ ]
116 114 http.start do |http|
117 115 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
118 116 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
119 117 resp = http.request(req,param)
120 118 result = JSON.parse resp.body
121 119 end
122 120 return true if result["type"] == "beanStudent"
123 121 rescue => e
124 122 return false
125 123 end
126 124 return false
127 125 end
128 126
129 127 def admin?
130 128 self.roles.detect {|r| r.name == 'admin' }
131 129 end
132 130
133 131 def email_for_editing
134 132 if self.email==nil
135 133 "(unknown)"
136 134 elsif self.email==''
137 135 "(blank)"
138 136 else
139 137 self.email
140 138 end
141 139 end
142 140
143 141 def email_for_editing=(e)
144 142 self.email=e
145 143 end
146 144
147 145 def alias_for_editing
148 146 if self.alias==nil
149 147 "(unknown)"
150 148 elsif self.alias==''
151 149 "(blank)"
152 150 else
153 151 self.alias
154 152 end
155 153 end
156 154
157 155 def alias_for_editing=(e)
158 156 self.alias=e
159 157 end
160 158
161 159 def activation_key
162 160 if self.hashed_password==nil
163 161 encrypt_new_password
164 162 end
165 163 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
166 164 end
167 165
168 166 def verify_activation_key(key)
169 167 key == activation_key
170 168 end
171 169
172 170 def self.random_password(length=5)
173 171 chars = 'abcdefghjkmnopqrstuvwxyz'
174 172 password = ''
175 173 length.times { password << chars[rand(chars.length - 1)] }
176 174 password
177 175 end
178 176
179 177 def self.find_non_admin_with_prefix(prefix='')
180 - users = User.find(:all)
178 + users = User.all
181 179 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
182 180 end
183 181
184 182 # Contest information
185 183
186 184 def self.find_users_with_no_contest()
187 - users = User.find(:all)
185 + users = User.all
188 186 return users.find_all { |u| u.contests.length == 0 }
189 187 end
190 188
191 189
192 190 def contest_time_left
193 191 if GraderConfiguration.contest_mode?
194 192 return nil if site==nil
195 193 return site.time_left
196 194 elsif GraderConfiguration.indv_contest_mode?
197 195 time_limit = GraderConfiguration.contest_time_limit
198 196 if time_limit == nil
199 197 return nil
200 198 end
201 199 if contest_stat==nil or contest_stat.started_at==nil
202 200 return (Time.now.gmtime + time_limit) - Time.now.gmtime
203 201 else
204 202 finish_time = contest_stat.started_at + time_limit
205 203 current_time = Time.now.gmtime
206 204 if current_time > finish_time
207 205 return 0
208 206 else
209 207 return finish_time - current_time
210 208 end
211 209 end
212 210 else
213 211 return nil
214 212 end
215 213 end
216 214
217 215 def contest_finished?
218 216 if GraderConfiguration.contest_mode?
219 217 return false if site==nil
220 218 return site.finished?
221 219 elsif GraderConfiguration.indv_contest_mode?
222 220 return false if self.contest_stat(true)==nil
223 221 return contest_time_left == 0
224 222 else
225 223 return false
226 224 end
227 225 end
228 226
229 227 def contest_started?
230 228 if GraderConfiguration.indv_contest_mode?
231 229 stat = self.contest_stat
232 230 return ((stat != nil) and (stat.started_at != nil))
233 231 elsif GraderConfiguration.contest_mode?
234 232 return true if site==nil
235 233 return site.started
236 234 else
237 235 return true
238 236 end
239 237 end
240 238
241 239 def update_start_time
242 240 stat = self.contest_stat
243 241 if stat.nil? or stat.started_at.nil?
244 242 stat ||= UserContestStat.new(:user => self)
245 243 stat.started_at = Time.now.gmtime
246 244 stat.save
247 245 end
248 246 end
249 247
250 248 def problem_in_user_contests?(problem)
251 249 problem_contests = problem.contests.all
252 250
253 251 if problem_contests.length == 0 # this is public contest
254 252 return true
255 253 end
256 254
257 255 contests.each do |contest|
258 256 if problem_contests.find {|c| c.id == contest.id }
259 257 return true
260 258 end
261 259 end
262 260 return false
263 261 end
264 262
265 263 def available_problems_group_by_contests
266 264 contest_problems = []
267 265 pin = {}
268 266 contests.enabled.each do |contest|
269 267 available_problems = contest.problems.available
270 268 contest_problems << {
271 269 :contest => contest,
272 270 :problems => available_problems
273 271 }
274 272 available_problems.each {|p| pin[p.id] = true}
275 273 end
276 274 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
277 275 contest_problems << {
278 276 :contest => nil,
279 277 :problems => other_avaiable_problems
280 278 }
281 279 return contest_problems
282 280 end
283 281
284 282 def available_problems
285 283 if not GraderConfiguration.multicontests?
286 - return Problem.find_available_problems
284 + return Problem.available_problems
287 285 else
288 286 contest_problems = []
289 287 pin = {}
290 288 contests.enabled.each do |contest|
291 289 contest.problems.available.each do |problem|
292 290 if not pin.has_key? problem.id
293 291 contest_problems << problem
294 292 end
295 293 pin[problem.id] = true
296 294 end
297 295 end
298 296 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
299 297 return contest_problems + other_avaiable_problems
300 298 end
301 299 end
302 300
303 301 def can_view_problem?(problem)
304 302 if not GraderConfiguration.multicontests?
305 303 return problem.available
306 304 else
307 305 return problem_in_user_contests? problem
308 306 end
309 307 end
310 308
311 309 def self.clear_last_login
312 310 User.update_all(:last_ip => nil)
313 311 end
314 312
315 313 protected
316 314 def encrypt_new_password
317 315 return if password.blank?
318 316 self.salt = (10+rand(90)).to_s
319 317 self.hashed_password = User.encrypt(self.password,self.salt)
320 318 end
321 319
322 320 def assign_default_site
323 321 # have to catch error when migrating (because self.site is not available).
324 322 begin
325 323 if self.site==nil
326 324 self.site = Site.find_by_name('default')
327 325 if self.site==nil
328 326 self.site = Site.find(1) # when 'default has be renamed'
329 327 end
330 328 end
331 329 rescue
332 330 end
333 331 end
334 332
335 333 def assign_default_contest
336 334 # have to catch error when migrating (because self.site is not available).
337 335 begin
338 336 if self.contests.length == 0
339 337 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
340 338 if default_contest
341 339 self.contests = [default_contest]
342 340 end
343 341 end
344 342 rescue
345 343 end
346 344 end
347 345
348 346 def password_required?
349 347 self.hashed_password.blank? || !self.password.blank?
350 348 end
351 349
352 350 def self.encrypt(string,salt)
353 351 Digest::SHA1.hexdigest(salt + string)
354 352 end
355 353
356 354 def uniqueness_of_email_from_activated_users
357 355 user = User.activated_users.find_by_email(self.email)
358 356 if user and (user.login != self.login)
359 357 self.errors.add(:base,"Email has already been taken")
360 358 end
361 359 end
362 360
363 361 def enough_time_interval_between_same_email_registrations
364 362 return if !self.new_record?
365 363 return if self.activated
366 364 open_user = User.find_by_email(self.email,
367 365 :order => 'created_at DESC')
368 366 if open_user and open_user.created_at and
369 367 (open_user.created_at > Time.now.gmtime - 5.minutes)
370 368 self.errors.add(:base,"There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
371 369 end
372 370 end
373 371
374 372 def email_validation?
375 373 begin
376 374 return VALIDATE_USER_EMAILS
377 375 rescue
378 376 return false
379 377 end
380 378 end
381 379 end
@@ -1,26 +1,26
1 1
2 2 - if submission.nil?
3 3 = "-"
4 4 - else
5 5 - unless submission.graded_at
6 6 = t 'main.submitted_at'
7 7 = format_short_time(submission.submitted_at.localtime)
8 8 - else
9 - = t 'main.graded_at'
10 - = "#{format_short_time(submission.graded_at.localtime)}, "
9 + %strong= t 'main.graded_at'
10 + = "#{format_short_time(submission.graded_at.localtime)} "
11 + %br
11 12 - if GraderConfiguration['ui.show_score']
12 - = t 'main.score'
13 + %strong=t 'main.score'
13 14 = "#{(submission.points*100/submission.problem.full_score).to_i} "
14 15 = " ["
15 16 %tt
16 17 = submission.grader_comment
17 18 = "]"
19 + %br
20 + %strong View:
18 21 - if GraderConfiguration.show_grading_result
19 - = " | "
20 22 = link_to '[detailed result]', :action => 'result', :id => submission.id
21 - = " | "
22 - = link_to("[#{t 'main.cmp_msg'}]", {:action => 'compiler_msg', :id => submission.id}, {:popup => true})
23 - = " | "
24 - = link_to("[#{t 'main.src_link'}]",{:action => 'source', :id => submission.id})
25 - //= " | "
26 - //= link_to "[#{t 'main.submissions_link'}]", main_submission_path(submission.problem.id)
23 + = link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
24 + = link_to "#{t 'main.src_link'}", download_submission_path(submission.id), class: 'btn btn-xs btn-info'
25 + = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info'
26 +
@@ -1,26 +1,27
1 1
2 2 - if submission.nil?
3 3 = "-"
4 4 - else
5 5 - unless submission.graded_at
6 6 = t 'main.submitted_at'
7 7 = format_short_time(submission.submitted_at.localtime)
8 8 - else
9 9 %strong= t 'main.graded_at'
10 10 = "#{format_short_time(submission.graded_at.localtime)} "
11 11 %br
12 12 - if GraderConfiguration['ui.show_score']
13 13 %strong=t 'main.score'
14 14 = "#{(submission.points*100/submission.problem.full_score).to_i} "
15 15 = " ["
16 16 %tt
17 17 = submission.grader_comment
18 18 = "]"
19 19 %br
20 20 %strong View:
21 21 - if GraderConfiguration.show_grading_result
22 22 = link_to '[detailed result]', :action => 'result', :id => submission.id
23 - = link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
23 + /= link_to "#{t 'main.cmp_msg'}", {:action => 'compiler_msg', :id => submission.id}, {popup: true,class: 'btn btn-xs btn-info'}
24 + = link_to "#{t 'main.cmp_msg'}", compiler_msg_submission_path(submission.id), {popup: true,remote: true,class: 'btn btn-xs btn-info'}
24 25 = link_to "#{t 'main.src_link'}",{:action => 'source', :id => submission.id}, class: 'btn btn-xs btn-info'
25 26 = link_to "#{t 'main.submissions_link'}", problem_submissions_path(problem_id), class: 'btn btn-xs btn-info'
26 27
@@ -1,34 +1,34
1 1 %table.table.sortable.table-striped.table-bordered.table-condensed
2 2 %thead
3 3 %tr
4 4 %th Login
5 5 %th Name
6 6 / %th Activated?
7 7 / %th Logged_in
8 8 / %th Contest(s)
9 9 %th Remark
10 10 - @problems.each do |p|
11 11 %th.text-right= p.name.gsub('_',' ')
12 12 %th.text-right Total
13 13 %th.text-right Passed
14 14 %tbody
15 15 - @scorearray.each do |sc|
16 16 %tr
17 17 - total,num_passed = 0,0
18 18 - sc.each_index do |i|
19 19 - if i == 0
20 - %td= link_to sc[i].login, controller: 'users', action: 'profile', id: sc[i]
20 + %td= link_to sc[i].login, stat_user_path(sc[i])
21 21 %td= sc[i].full_name
22 22 / %td= sc[i].activated
23 23 / %td= sc[i].try(:contest_stat).try(:started_at) ? 'yes' : 'no'
24 24 / %td= sc[i].contests.collect {|c| c.name}.join(', ')
25 25 %td= sc[i].remark
26 26 - else
27 27 %td.text-right= sc[i][0]
28 28 - total += sc[i][0]
29 29 - num_passed += 1 if sc[i][1]
30 30 %td.text-right= total
31 31 %td.text-right= num_passed
32 32
33 33 :javascript
34 34 $.bootstrapSortable(true,'reversed')
@@ -1,268 +1,269
1 1 %h2 Live submit
2 2 %br
3 3
4 4 %textarea#text_haha{style: "display:none"}~ @source
5 5 .container
6 6 .row
7 7 .col-md-12
8 8 .alert.alert-info
9 9 Write your code in the following box, choose language, and click submit button when finished
10 10 .row
11 11 .col-md-8
12 12 %div#editor{style: 'height: 500px; border-radius: 7px; font-size: 14px;'}
13 13 .col-md-4
14 14 = form_tag({controller: :main, :action => 'submit'}, :multipart => true, class: 'form') do
15 15
16 16 = hidden_field_tag 'editor_text', @source
17 17 = hidden_field_tag 'submission[problem_id]', @problem.id
18 18 .form-group
19 19 = label_tag "Task:"
20 20 = text_field_tag 'asdf', "#{@problem.long_name}", class: 'form-control', disabled: true
21 21
22 22 .form-group
23 23 = label_tag 'Language'
24 24 = select_tag 'language_id', options_from_collection_for_select(Language.all, 'id', 'pretty_name', @lang_id || Language.find_by_pretty_name("Python").id || Language.first.id), class: 'form-control select', style: "width: 100px"
25 25 .form-group
26 26 = submit_tag 'Submit', class: 'btn btn-success', id: 'live_submit',
27 27 data: {confirm: "Submitting this source code for task #{@problem.long_name}?"}
28 28 .panel.panel-info
29 29 .panel-heading
30 30 Latest Submission Status
31 + = link_to "Refresh",get_latest_submission_status_submissions_path(@submission.user,@problem), class: "btn btn-default btn-sm", remote: true
31 32 .panel-body
32 33 - if @submission
33 34 = render :partial => 'submission_short',
34 - :locals => {:submission => @submission, :problem_name => @problem.name }
35 + :locals => {submission: @submission, problem_name: @problem.name, problem_id: @problem.id }
35 36 .row
36 37 .col-md-12
37 38 %h2 Console
38 39 %textarea#console{style: 'height: 100%; width: 100%;background-color:#000;color:#fff;font-family: consolas, monaco, "Droid Sans Mono";',rows: 20}
39 40
40 41 :javascript
41 42 $(document).ready(function() {
42 43 e = ace.edit("editor")
43 44 e.setValue($("#text_haha").val());
44 45 e.gotoLine(1);
45 46 $("#language_id").trigger('change');
46 47 brython();
47 48 });
48 49
49 50
50 51 %script#__main__{type:'text/python3'}
51 52 :plain
52 53 import sys
53 54 import traceback
54 55
55 56 from browser import document as doc
56 57 from browser import window, alert, console
57 58
58 59 _credits = """ Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
59 60 for supporting Python development. See www.python.org for more information."""
60 61
61 62 _copyright = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
62 63 All Rights Reserved.
63 64
64 65 Copyright (c) 2001-2013 Python Software Foundation.
65 66 All Rights Reserved.
66 67
67 68 Copyright (c) 2000 BeOpen.com.
68 69 All Rights Reserved.
69 70
70 71 Copyright (c) 1995-2001 Corporation for National Research Initiatives.
71 72 All Rights Reserved.
72 73
73 74 Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
74 75 All Rights Reserved."""
75 76
76 77 _license = """Copyright (c) 2012, Pierre Quentel pierre.quentel@gmail.com
77 78 All rights reserved.
78 79
79 80 Redistribution and use in source and binary forms, with or without
80 81 modification, are permitted provided that the following conditions are met:
81 82
82 83 Redistributions of source code must retain the above copyright notice, this
83 84 list of conditions and the following disclaimer. Redistributions in binary
84 85 form must reproduce the above copyright notice, this list of conditions and
85 86 the following disclaimer in the documentation and/or other materials provided
86 87 with the distribution.
87 88 Neither the name of the <ORGANIZATION> nor the names of its contributors may
88 89 be used to endorse or promote products derived from this software without
89 90 specific prior written permission.
90 91
91 92 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
92 93 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
93 94 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
94 95 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
95 96 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
96 97 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
97 98 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
98 99 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
99 100 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
100 101 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
101 102 POSSIBILITY OF SUCH DAMAGE.
102 103 """
103 104
104 105 def credits():
105 106 print(_credits)
106 107 credits.__repr__ = lambda:_credits
107 108
108 109 def copyright():
109 110 print(_copyright)
110 111 copyright.__repr__ = lambda:_copyright
111 112
112 113 def license():
113 114 print(_license)
114 115 license.__repr__ = lambda:_license
115 116
116 117 def write(data):
117 118 doc['console'].value += str(data)
118 119
119 120
120 121 sys.stdout.write = sys.stderr.write = write
121 122 history = []
122 123 current = 0
123 124 _status = "main" # or "block" if typing inside a block
124 125
125 126 # execution namespace
126 127 editor_ns = {'credits':credits,
127 128 'copyright':copyright,
128 129 'license':license,
129 130 '__name__':'__main__'}
130 131
131 132 def cursorToEnd(*args):
132 133 pos = len(doc['console'].value)
133 134 doc['console'].setSelectionRange(pos, pos)
134 135 doc['console'].scrollTop = doc['console'].scrollHeight
135 136
136 137 def get_col(area):
137 138 # returns the column num of cursor
138 139 sel = doc['console'].selectionStart
139 140 lines = doc['console'].value.split('\n')
140 141 for line in lines[:-1]:
141 142 sel -= len(line) + 1
142 143 return sel
143 144
144 145
145 146 def myKeyPress(event):
146 147 global _status, current
147 148 if event.keyCode == 9: # tab key
148 149 event.preventDefault()
149 150 doc['console'].value += " "
150 151 elif event.keyCode == 13: # return
151 152 src = doc['console'].value
152 153 if _status == "main":
153 154 currentLine = src[src.rfind('>>>') + 4:]
154 155 elif _status == "3string":
155 156 currentLine = src[src.rfind('>>>') + 4:]
156 157 currentLine = currentLine.replace('\n... ', '\n')
157 158 else:
158 159 currentLine = src[src.rfind('...') + 4:]
159 160 if _status == 'main' and not currentLine.strip():
160 161 doc['console'].value += '\n>>> '
161 162 event.preventDefault()
162 163 return
163 164 doc['console'].value += '\n'
164 165 history.append(currentLine)
165 166 current = len(history)
166 167 if _status == "main" or _status == "3string":
167 168 try:
168 169 _ = editor_ns['_'] = eval(currentLine, editor_ns)
169 170 if _ is not None:
170 171 write(repr(_)+'\n')
171 172 doc['console'].value += '>>> '
172 173 _status = "main"
173 174 except IndentationError:
174 175 doc['console'].value += '... '
175 176 _status = "block"
176 177 except SyntaxError as msg:
177 178 if str(msg) == 'invalid syntax : triple string end not found' or \
178 179 str(msg).startswith('Unbalanced bracket'):
179 180 doc['console'].value += '... '
180 181 _status = "3string"
181 182 elif str(msg) == 'eval() argument must be an expression':
182 183 try:
183 184 exec(currentLine, editor_ns)
184 185 except:
185 186 traceback.print_exc()
186 187 doc['console'].value += '>>> '
187 188 _status = "main"
188 189 elif str(msg) == 'decorator expects function':
189 190 doc['console'].value += '... '
190 191 _status = "block"
191 192 else:
192 193 traceback.print_exc()
193 194 doc['console'].value += '>>> '
194 195 _status = "main"
195 196 except:
196 197 traceback.print_exc()
197 198 doc['console'].value += '>>> '
198 199 _status = "main"
199 200 elif currentLine == "": # end of block
200 201 block = src[src.rfind('>>>') + 4:].splitlines()
201 202 block = [block[0]] + [b[4:] for b in block[1:]]
202 203 block_src = '\n'.join(block)
203 204 # status must be set before executing code in globals()
204 205 _status = "main"
205 206 try:
206 207 _ = exec(block_src, editor_ns)
207 208 if _ is not None:
208 209 print(repr(_))
209 210 except:
210 211 traceback.print_exc()
211 212 doc['console'].value += '>>> '
212 213 else:
213 214 doc['console'].value += '... '
214 215
215 216 cursorToEnd()
216 217 event.preventDefault()
217 218
218 219 def myKeyDown(event):
219 220 global _status, current
220 221 if event.keyCode == 37: # left arrow
221 222 sel = get_col(doc['console'])
222 223 if sel < 5:
223 224 event.preventDefault()
224 225 event.stopPropagation()
225 226 elif event.keyCode == 36: # line start
226 227 pos = doc['console'].selectionStart
227 228 col = get_col(doc['console'])
228 229 doc['console'].setSelectionRange(pos - col + 4, pos - col + 4)
229 230 event.preventDefault()
230 231 elif event.keyCode == 38: # up
231 232 if current > 0:
232 233 pos = doc['console'].selectionStart
233 234 col = get_col(doc['console'])
234 235 # remove current line
235 236 doc['console'].value = doc['console'].value[:pos - col + 4]
236 237 current -= 1
237 238 doc['console'].value += history[current]
238 239 event.preventDefault()
239 240 elif event.keyCode == 40: # down
240 241 if current < len(history) - 1:
241 242 pos = doc['console'].selectionStart
242 243 col = get_col(doc['console'])
243 244 # remove current line
244 245 doc['console'].value = doc['console'].value[:pos - col + 4]
245 246 current += 1
246 247 doc['console'].value += history[current]
247 248 event.preventDefault()
248 249 elif event.keyCode == 8: # backspace
249 250 src = doc['console'].value
250 251 lstart = src.rfind('\n')
251 252 if (lstart == -1 and len(src) < 5) or (len(src) - lstart < 6):
252 253 event.preventDefault()
253 254 event.stopPropagation()
254 255
255 256
256 257 doc['console'].bind('keypress', myKeyPress)
257 258 doc['console'].bind('keydown', myKeyDown)
258 259 doc['console'].bind('click', cursorToEnd)
259 260 v = sys.implementation.version
260 261 doc['console'].value = "Brython %s.%s.%s on %s %s\n>>> " % (
261 262 v[0], v[1], v[2], window.navigator.appName, window.navigator.appVersion)
262 263 #doc['console'].value += 'Type "copyright", "credits" or "license" for more information.'
263 264 doc['console'].focus()
264 265 cursorToEnd()
265 266
266 267
267 268
268 269
@@ -1,103 +1,104
1 1 %h1= "Submission: #{@submission.id}"
2 2
3 3 %textarea#data{style: "display:none;"}
4 4 :preserve
5 5 #{@submission.source}
6 6
7 7 //%div.highlight{:style => "border: 1px solid black;"}
8 8 //=@formatted_code.html_safe
9 9
10 10
11 11 .containter
12 12 .row
13 13 .col-md-7
14 14 %h2 Source Code
15 15 .col-md-5
16 16 %h2 Stat
17 17 .row
18 18 .col-md-7
19 19 %div#editor{ style: "font-size: 14px; height: 400px; border-radius:5px;" }
20 20 :javascript
21 21 e = ace.edit("editor")
22 22 e.setOptions({ maxLines: Infinity })
23 23 e.setValue($("#data").text())
24 24 e.gotoLine(1)
25 25 e.getSession().setMode("#{get_ace_mode(@submission.language)}")
26 26 e.setReadOnly(true)
27 27 .col-md-5
28 28 %table.table.table-striped
29 29 %tr
30 30 %td.text-right
31 31 %strong User
32 32 %td
33 33 - if @submission.user
34 34 = link_to "#{@submission.user.login}", stat_user_path(@submission.user)
35 35 = @submission.user.full_name
36 36 - else
37 37 = "(n/a)"
38 38 %tr
39 39 %td.text-right
40 40 %strong Task
41 41 %td
42 42 - if @submission.problem!=nil
43 43 = link_to "[#{@submission.problem.name}]", stat_problem_path(@submission.problem)
44 44 = @submission.problem.full_name
45 45 - else
46 46 = "(n/a)"
47 47 %tr
48 48 %td.text-right
49 49 %strong Tries
50 50 %td= @submission.number
51 51 %tr
52 52 %td.text-right
53 53 %strong Language
54 54 %td= @submission.language.pretty_name
55 55 %tr
56 56 %td.text-right
57 57 %strong Submitted
58 58 %td #{time_ago_in_words(@submission.submitted_at)} ago (at #{@submission.submitted_at.to_formatted_s(:long)})
59 59 %tr
60 60 %td.text-right
61 61 %strong Graded
62 62 - if @submission.graded_at
63 63 %td #{time_ago_in_words(@submission.graded_at)} ago (at #{@submission.graded_at.to_formatted_s(:long)})
64 64 - else
65 65 %td -
66 66 %tr
67 67 %td.text-right
68 68 %strong Points
69 69 %td #{@submission.points}/#{@submission.problem.full_score}
70 70 %tr
71 71 %td.text-right
72 72 %strong Comment
73 73 %td #{@submission.grader_comment}
74 74 %tr
75 75 %td.text-right
76 76 %strong Runtime (s)
77 77 %td #{@submission.max_runtime}
78 78 %tr
79 79 %td.text-right
80 80 %strong Memory (kb)
81 81 %td #{@submission.peak_memory}
82 82 %tr
83 83 %td.text-right
84 84 %strong Compiler result
85 85 %td
86 86 %button.btn.btn-info.btn-xs{type: 'button', data: {toggle: 'modal', target: '#compiler'}}
87 87 view
88 + - if session[:admin]
89 + %tr
90 + %td.text-right
91 + %strong IP
92 + %td #{@submission.ip_address}
93 +
88 94 .modal.fade#compiler{tabindex: -1,role: 'dialog'}
89 95 .modal-dialog.modal-lg{role:'document'}
90 96 .modal-content
91 97 .modal-header
92 98 %button.close{type: 'button', data: {dismissed: :modal}, aria: {label: 'close'}}
93 99 %span{aria: {hidden: 'true'}, data: {dismiss: 'modal'}} &times;
94 100 %h4 Compiler message
95 101 .modal-body
96 - %pre= @submission.compiler_message
102 + %pre#compiler_msg= @submission.compiler_message
97 103 .modal-footer
98 104 %button.btn.btn-default{type: 'button', data: {dismiss: 'modal'}} Close
99 - - if session[:admin]
100 - %tr
101 - %td.text-right
102 - %strong IP
103 - %td #{@submission.ip_address}
@@ -1,40 +1,42
1 1 CafeGrader::Application.configure do
2 2 # Settings specified here will take precedence over those in config/application.rb
3 3
4 4 # In the development environment your application's code is reloaded on
5 5 # every request. This slows down response time but is perfect for development
6 6 # since you don't have to restart the web server when you make code changes.
7 7 config.cache_classes = false
8 8
9 - # Log error messages when you accidentally call methods on nil.
10 - config.whiny_nils = true
9 + # Log error messages when you accidentally call methods on nil. //DEPRICATED
10 + # config.whiny_nils = true // DEPRICATED
11 11
12 12 # Show full error reports and disable caching
13 13 config.consider_all_requests_local = true
14 14 config.action_controller.perform_caching = false
15 15
16 16 # Don't care if the mailer can't send
17 17 config.action_mailer.raise_delivery_errors = false
18 18
19 19 # Print deprecation notices to the Rails logger
20 20 config.active_support.deprecation = :log
21 21
22 22 # Only use best-standards-support built into browsers
23 23 config.action_dispatch.best_standards_support = :builtin
24 24
25 25 # Raise exception on mass assignment protection for Active Record models
26 - config.active_record.mass_assignment_sanitizer = :strict
26 + # config.active_record.mass_assignment_sanitizer = :strict //DEPRICATED
27 27
28 - # Log the query plan for queries taking more than this (works
29 - # with SQLite, MySQL, and PostgreSQL)
30 - config.active_record.auto_explain_threshold_in_seconds = 0.5
28 + # Log the query plan for queries taking more than this (works // DEPRICATED
29 + # with SQLite, MySQL, and PostgreSQL) // DEPRICATED
30 + # config.active_record.auto_explain_threshold_in_seconds = 0.5 // DEPRICATED
31 31
32 32 # Do not compress assets
33 33 config.assets.compress = false
34 34
35 35 # Expands the lines which load the assets
36 36 config.assets.debug = true
37 37
38 38 # Prevents assets from rendering twice
39 - config.serve_static_assets = true
39 + config.serve_static_files = true
40 +
41 + config.eager_load = false
40 42 end
@@ -1,67 +1,69
1 1 CafeGrader::Application.configure do
2 2 # Settings specified here will take precedence over those in config/application.rb
3 3
4 4 # Code is not reloaded between requests
5 5 config.cache_classes = true
6 6
7 7 # Full error reports are disabled and caching is turned on
8 8 config.consider_all_requests_local = false
9 9 config.action_controller.perform_caching = true
10 10
11 11 # Disable Rails's static asset server (Apache or nginx will already do this)
12 - config.serve_static_assets = false
12 + config.serve_static_files = false
13 13
14 14 # Compress JavaScripts and CSS
15 15 config.assets.compress = true
16 16
17 17 # Don't fallback to assets pipeline if a precompiled asset is missed
18 18 config.assets.compile = false
19 19
20 20 # Generate digests for assets URLs
21 21 config.assets.digest = true
22 22
23 23 # Defaults to nil and saved in location specified by config.assets.prefix
24 24 # config.assets.manifest = YOUR_PATH
25 25
26 26 # Specifies the header that your server uses for sending files
27 27 # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
28 28 # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
29 29
30 30 # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31 31 # config.force_ssl = true
32 32
33 33 # See everything in the log (default is :info)
34 34 # config.log_level = :debug
35 35
36 36 # Prepend all log lines with the following tags
37 37 # config.log_tags = [ :subdomain, :uuid ]
38 38
39 39 # Use a different logger for distributed setups
40 40 # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
41 41
42 42 # Use a different cache store in production
43 43 # config.cache_store = :mem_cache_store
44 44
45 45 # Enable serving of images, stylesheets, and JavaScripts from an asset server
46 46 # config.action_controller.asset_host = "http://assets.example.com"
47 47
48 48 # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
49 49 # config.assets.precompile += %w( search.js )
50 50
51 51 # Disable delivery errors, bad email addresses will be ignored
52 52 # config.action_mailer.raise_delivery_errors = false
53 53
54 54 # Enable threaded mode
55 55 # config.threadsafe!
56 56
57 57 # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
58 58 # the I18n.default_locale when a translation can not be found)
59 59 config.i18n.fallbacks = true
60 60
61 61 # Send deprecation notices to registered listeners
62 62 config.active_support.deprecation = :notify
63 63
64 64 # Log the query plan for queries taking more than this (works
65 65 # with SQLite, MySQL, and PostgreSQL)
66 66 # config.active_record.auto_explain_threshold_in_seconds = 0.5
67 +
68 + config.eager_load = true
67 69 end
@@ -1,37 +1,43
1 1 CafeGrader::Application.configure do
2 2 # Settings specified here will take precedence over those in config/application.rb
3 3
4 4 # The test environment is used exclusively to run your application's
5 5 # test suite. You never need to work with it otherwise. Remember that
6 6 # your test database is "scratch space" for the test suite and is wiped
7 7 # and recreated between test runs. Don't rely on the data there!
8 8 config.cache_classes = true
9 9
10 10 # Configure static asset server for tests with Cache-Control for performance
11 - config.serve_static_assets = true
11 + config.serve_static_files = true
12 12 config.static_cache_control = "public, max-age=3600"
13 13
14 14 # Log error messages when you accidentally call methods on nil
15 15 config.whiny_nils = true
16 16
17 17 # Show full error reports and disable caching
18 18 config.consider_all_requests_local = true
19 19 config.action_controller.perform_caching = false
20 20
21 21 # Raise exceptions instead of rendering exception templates
22 22 config.action_dispatch.show_exceptions = false
23 23
24 24 # Disable request forgery protection in test environment
25 25 config.action_controller.allow_forgery_protection = false
26 26
27 27 # Tell Action Mailer not to deliver emails to the real world.
28 28 # The :test delivery method accumulates sent emails in the
29 29 # ActionMailer::Base.deliveries array.
30 30 config.action_mailer.delivery_method = :test
31 31
32 32 # Raise exception on mass assignment protection for Active Record models
33 - config.active_record.mass_assignment_sanitizer = :strict
33 + #config.active_record.mass_assignment_sanitizer = :strict // DEPRICATED
34 34
35 35 # Print deprecation notices to the stderr
36 36 config.active_support.deprecation = :stderr
37 +
38 + config.eager_load = false
39 +
40 + #test order
41 + config.active_support.test_order = :sorted
42 +
37 43 end
@@ -1,6 +1,5
1 1 # Be sure to restart your server when you modify this file.
2 2
3 3 # Add new mime types for use in respond_to blocks:
4 4 # Mime::Type.register "text/richtext", :rtf
5 5 # Mime::Type.register_alias "text/html", :iphone
6 - Mime::Type.register 'application/pdf', :pdf
@@ -1,73 +1,77
1 1 CafeGrader::Application.routes.draw do
2 2 get "sources/direct_edit"
3 3
4 4 root :to => 'main#login'
5 5
6 6 resources :contests
7 7
8 8 resources :sites
9 9
10 10 resources :announcements do
11 11 member do
12 12 get 'toggle','toggle_front'
13 13 end
14 14 end
15 15
16 16 resources :problems do
17 17 member do
18 18 get 'toggle'
19 19 get 'toggle_test'
20 20 get 'stat'
21 21 end
22 22 collection do
23 23 get 'turn_all_off'
24 24 get 'turn_all_on'
25 25 get 'import'
26 26 get 'manage'
27 27 end
28 28 end
29 29
30 30 resources :grader_configuration, controller: 'configurations'
31 31
32 32 resources :users do
33 33 member do
34 34 get 'toggle_activate', 'toggle_enable'
35 35 get 'stat'
36 36 end
37 37 end
38 38
39 39 resources :submissions do
40 + member do
41 + get 'download'
42 + get 'compiler_msg'
43 + end
40 44 collection do
41 45 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
42 46 get 'direct_edit_problem/:problem_id', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
43 47 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
44 48 end
45 49 end
46 50
47 - match 'tasks/view/:file.:ext' => 'tasks#view'
48 - match 'tasks/download/:id/:file.:ext' => 'tasks#download'
49 - match 'heartbeat/:id/edit' => 'heartbeat#edit'
51 + get 'tasks/view/:file.:ext' => 'tasks#view'
52 + get 'tasks/download/:id/:file.:ext' => 'tasks#download'
53 + get 'heartbeat/:id/edit' => 'heartbeat#edit'
50 54
51 55 #main
52 56 get "main/list"
53 57 get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
54 58
55 59 #report
56 60 get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
57 61 get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
58 62 get "report/login"
59 63 get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
60 64 post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
61 65
62 66 #grader
63 67 get 'graders/list', to: 'graders#list', as: 'grader_list'
64 68
65 69
66 - match 'heartbeat/:id/edit' => 'heartbeat#edit'
70 + get 'heartbeat/:id/edit' => 'heartbeat#edit'
67 71
68 72 # See how all your routes lay out with "rake routes"
69 73
70 74 # This is a legacy wild controller route that's not recommended for RESTful applications.
71 75 # Note: This route will make all actions in every controller accessible via GET requests.
72 - match ':controller(/:action(/:id))(.:format)'
76 + match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
73 77 end
@@ -1,280 +1,280
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 - # It's strongly recommended to check this file into your version control system.
12 + # It's strongly recommended that you check this file into your version control system.
13 13
14 - ActiveRecord::Schema.define(:version => 20161031063337) do
14 + ActiveRecord::Schema.define(version: 20161031063337) do
15 15
16 - create_table "announcements", :force => true do |t|
16 + create_table "announcements", force: :cascade do |t|
17 17 t.string "author"
18 18 t.text "body"
19 19 t.boolean "published"
20 - t.datetime "created_at", :null => false
21 - t.datetime "updated_at", :null => false
22 - t.boolean "frontpage", :default => false
23 - t.boolean "contest_only", :default => false
20 + t.datetime "created_at", null: false
21 + t.datetime "updated_at", null: false
22 + t.boolean "frontpage", default: false
23 + t.boolean "contest_only", default: false
24 24 t.string "title"
25 25 t.string "notes"
26 26 end
27 27
28 - create_table "contests", :force => true do |t|
28 + create_table "contests", force: :cascade do |t|
29 29 t.string "title"
30 30 t.boolean "enabled"
31 - t.datetime "created_at", :null => false
32 - t.datetime "updated_at", :null => false
31 + t.datetime "created_at", null: false
32 + t.datetime "updated_at", null: false
33 33 t.string "name"
34 34 end
35 35
36 - create_table "contests_problems", :id => false, :force => true do |t|
36 + create_table "contests_problems", id: false, force: :cascade do |t|
37 37 t.integer "contest_id"
38 38 t.integer "problem_id"
39 39 end
40 40
41 - create_table "contests_users", :id => false, :force => true do |t|
41 + create_table "contests_users", id: false, force: :cascade do |t|
42 42 t.integer "contest_id"
43 43 t.integer "user_id"
44 44 end
45 45
46 - create_table "countries", :force => true do |t|
46 + create_table "countries", force: :cascade do |t|
47 47 t.string "name"
48 - t.datetime "created_at", :null => false
49 - t.datetime "updated_at", :null => false
48 + t.datetime "created_at", null: false
49 + t.datetime "updated_at", null: false
50 50 end
51 51
52 - create_table "descriptions", :force => true do |t|
52 + create_table "descriptions", force: :cascade do |t|
53 53 t.text "body"
54 54 t.boolean "markdowned"
55 - t.datetime "created_at", :null => false
56 - t.datetime "updated_at", :null => false
55 + t.datetime "created_at", null: false
56 + t.datetime "updated_at", null: false
57 57 end
58 58
59 - create_table "grader_configurations", :force => true do |t|
59 + create_table "grader_configurations", force: :cascade do |t|
60 60 t.string "key"
61 61 t.string "value_type"
62 62 t.string "value"
63 - t.datetime "created_at", :null => false
64 - t.datetime "updated_at", :null => false
63 + t.datetime "created_at", null: false
64 + t.datetime "updated_at", null: false
65 65 t.text "description"
66 66 end
67 67
68 - create_table "grader_processes", :force => true do |t|
68 + create_table "grader_processes", force: :cascade do |t|
69 69 t.string "host"
70 70 t.integer "pid"
71 71 t.string "mode"
72 72 t.boolean "active"
73 - t.datetime "created_at", :null => false
74 - t.datetime "updated_at", :null => false
73 + t.datetime "created_at", null: false
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 - add_index "grader_processes", ["host", "pid"], :name => "index_grader_processes_on_ip_and_pid"
80 + add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_ip_and_pid"
81 81
82 - create_table "heart_beats", :force => true do |t|
82 + create_table "heart_beats", force: :cascade do |t|
83 83 t.integer "user_id"
84 84 t.string "ip_address"
85 - t.datetime "created_at", :null => false
86 - t.datetime "updated_at", :null => false
85 + t.datetime "created_at", null: false
86 + t.datetime "updated_at", null: false
87 87 t.string "status"
88 88 end
89 89
90 - add_index "heart_beats", ["updated_at"], :name => "index_heart_beats_on_updated_at"
90 + add_index "heart_beats", ["updated_at"], name: "index_heart_beats_on_updated_at"
91 91
92 - create_table "languages", :force => true do |t|
93 - t.string "name", :limit => 10
92 + create_table "languages", force: :cascade do |t|
93 + t.string "name", limit: 10
94 94 t.string "pretty_name"
95 - t.string "ext", :limit => 10
95 + t.string "ext", limit: 10
96 96 t.string "common_ext"
97 97 end
98 98
99 - create_table "logins", :force => true do |t|
99 + create_table "logins", force: :cascade do |t|
100 100 t.integer "user_id"
101 101 t.string "ip_address"
102 - t.datetime "created_at", :null => false
103 - t.datetime "updated_at", :null => false
102 + t.datetime "created_at", null: false
103 + t.datetime "updated_at", null: false
104 104 end
105 105
106 - create_table "messages", :force => true do |t|
106 + create_table "messages", force: :cascade do |t|
107 107 t.integer "sender_id"
108 108 t.integer "receiver_id"
109 109 t.integer "replying_message_id"
110 110 t.text "body"
111 111 t.boolean "replied"
112 - t.datetime "created_at", :null => false
113 - t.datetime "updated_at", :null => false
112 + t.datetime "created_at", null: false
113 + t.datetime "updated_at", null: false
114 114 end
115 115
116 - create_table "problems", :force => true do |t|
117 - t.string "name", :limit => 30
116 + create_table "problems", force: :cascade do |t|
117 + t.string "name", limit: 30
118 118 t.string "full_name"
119 119 t.integer "full_score"
120 120 t.date "date_added"
121 121 t.boolean "available"
122 122 t.string "url"
123 123 t.integer "description_id"
124 124 t.boolean "test_allowed"
125 125 t.boolean "output_only"
126 126 t.string "description_filename"
127 127 end
128 128
129 - create_table "rights", :force => true do |t|
129 + create_table "rights", force: :cascade do |t|
130 130 t.string "name"
131 131 t.string "controller"
132 132 t.string "action"
133 133 end
134 134
135 - create_table "rights_roles", :id => false, :force => true do |t|
135 + create_table "rights_roles", id: false, force: :cascade do |t|
136 136 t.integer "right_id"
137 137 t.integer "role_id"
138 138 end
139 139
140 - add_index "rights_roles", ["role_id"], :name => "index_rights_roles_on_role_id"
140 + add_index "rights_roles", ["role_id"], name: "index_rights_roles_on_role_id"
141 141
142 - create_table "roles", :force => true do |t|
142 + create_table "roles", force: :cascade do |t|
143 143 t.string "name"
144 144 end
145 145
146 - create_table "roles_users", :id => false, :force => true do |t|
146 + create_table "roles_users", id: false, force: :cascade do |t|
147 147 t.integer "role_id"
148 148 t.integer "user_id"
149 149 end
150 150
151 - add_index "roles_users", ["user_id"], :name => "index_roles_users_on_user_id"
151 + add_index "roles_users", ["user_id"], name: "index_roles_users_on_user_id"
152 152
153 - create_table "sessions", :force => true do |t|
153 + create_table "sessions", force: :cascade do |t|
154 154 t.string "session_id"
155 155 t.text "data"
156 156 t.datetime "updated_at"
157 157 end
158 158
159 - add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
160 - add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
159 + add_index "sessions", ["session_id"], name: "index_sessions_on_session_id"
160 + add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at"
161 161
162 - create_table "sites", :force => true do |t|
162 + create_table "sites", force: :cascade do |t|
163 163 t.string "name"
164 164 t.boolean "started"
165 165 t.datetime "start_time"
166 - t.datetime "created_at", :null => false
167 - t.datetime "updated_at", :null => false
166 + t.datetime "created_at", null: false
167 + t.datetime "updated_at", null: false
168 168 t.integer "country_id"
169 169 t.string "password"
170 170 end
171 171
172 - create_table "submission_view_logs", :force => true do |t|
172 + create_table "submission_view_logs", force: :cascade do |t|
173 173 t.integer "user_id"
174 174 t.integer "submission_id"
175 - t.datetime "created_at", :null => false
176 - t.datetime "updated_at", :null => false
175 + t.datetime "created_at", null: false
176 + t.datetime "updated_at", null: false
177 177 end
178 178
179 - create_table "submissions", :force => true do |t|
179 + create_table "submissions", force: :cascade do |t|
180 180 t.integer "user_id"
181 181 t.integer "problem_id"
182 182 t.integer "language_id"
183 183 t.text "source"
184 184 t.binary "binary"
185 185 t.datetime "submitted_at"
186 186 t.datetime "compiled_at"
187 187 t.text "compiler_message"
188 188 t.datetime "graded_at"
189 189 t.integer "points"
190 190 t.text "grader_comment"
191 191 t.integer "number"
192 192 t.string "source_filename"
193 193 t.float "max_runtime"
194 194 t.integer "peak_memory"
195 195 t.integer "effective_code_length"
196 196 t.string "ip_address"
197 197 end
198 198
199 - add_index "submissions", ["user_id", "problem_id", "number"], :name => "index_submissions_on_user_id_and_problem_id_and_number", :unique => true
200 - add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id"
199 + add_index "submissions", ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true
200 + add_index "submissions", ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id"
201 201
202 - create_table "tasks", :force => true do |t|
202 + create_table "tasks", force: :cascade do |t|
203 203 t.integer "submission_id"
204 204 t.datetime "created_at"
205 205 t.integer "status"
206 206 t.datetime "updated_at"
207 207 end
208 208
209 - create_table "test_pairs", :force => true do |t|
209 + create_table "test_pairs", force: :cascade do |t|
210 210 t.integer "problem_id"
211 - t.text "input", :limit => 16777215
212 - t.text "solution", :limit => 16777215
213 - t.datetime "created_at", :null => false
214 - t.datetime "updated_at", :null => false
211 + t.text "input", limit: 16777215
212 + t.text "solution", limit: 16777215
213 + t.datetime "created_at", null: false
214 + t.datetime "updated_at", null: false
215 215 end
216 216
217 - create_table "test_requests", :force => true do |t|
217 + create_table "test_requests", force: :cascade do |t|
218 218 t.integer "user_id"
219 219 t.integer "problem_id"
220 220 t.integer "submission_id"
221 221 t.string "input_file_name"
222 222 t.string "output_file_name"
223 223 t.string "running_stat"
224 224 t.integer "status"
225 - t.datetime "updated_at", :null => false
225 + t.datetime "updated_at", null: false
226 226 t.datetime "submitted_at"
227 227 t.datetime "compiled_at"
228 228 t.text "compiler_message"
229 229 t.datetime "graded_at"
230 230 t.string "grader_comment"
231 - t.datetime "created_at", :null => false
231 + t.datetime "created_at", null: false
232 232 t.float "running_time"
233 233 t.string "exit_status"
234 234 t.integer "memory_usage"
235 235 end
236 236
237 - add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
237 + add_index "test_requests", ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id"
238 238
239 - create_table "testcases", :force => true do |t|
239 + create_table "testcases", force: :cascade do |t|
240 240 t.integer "problem_id"
241 241 t.integer "num"
242 242 t.integer "group"
243 243 t.integer "score"
244 244 t.text "input"
245 245 t.text "sol"
246 - t.datetime "created_at", :null => false
247 - t.datetime "updated_at", :null => false
246 + t.datetime "created_at", null: false
247 + t.datetime "updated_at", null: false
248 248 end
249 249
250 - add_index "testcases", ["problem_id"], :name => "index_testcases_on_problem_id"
250 + add_index "testcases", ["problem_id"], name: "index_testcases_on_problem_id"
251 251
252 - create_table "user_contest_stats", :force => true do |t|
252 + create_table "user_contest_stats", force: :cascade do |t|
253 253 t.integer "user_id"
254 254 t.datetime "started_at"
255 - t.datetime "created_at", :null => false
256 - t.datetime "updated_at", :null => false
255 + t.datetime "created_at", null: false
256 + t.datetime "updated_at", null: false
257 257 t.boolean "forced_logout"
258 258 end
259 259
260 - create_table "users", :force => true do |t|
261 - t.string "login", :limit => 50
260 + create_table "users", force: :cascade do |t|
261 + t.string "login", limit: 50
262 262 t.string "full_name"
263 263 t.string "hashed_password"
264 - t.string "salt", :limit => 5
264 + t.string "salt", limit: 5
265 265 t.string "alias"
266 266 t.string "email"
267 267 t.integer "site_id"
268 268 t.integer "country_id"
269 - t.boolean "activated", :default => false
269 + t.boolean "activated", default: false
270 270 t.datetime "created_at"
271 271 t.datetime "updated_at"
272 - t.boolean "enabled", :default => true
272 + t.boolean "enabled", default: true
273 273 t.string "remark"
274 274 t.string "last_ip"
275 275 t.string "section"
276 276 end
277 277
278 - add_index "users", ["login"], :name => "index_users_on_login", :unique => true
278 + add_index "users", ["login"], name: "index_users_on_login", unique: true
279 279
280 280 end
@@ -1,230 +1,245
1 1 CONFIGURATIONS =
2 2 [
3 3 {
4 4 :key => 'system.single_user_mode',
5 5 :value_type => 'boolean',
6 6 :default_value => 'false',
7 7 :description => 'Only admins can log in to the system when running under single user mode.'
8 8 },
9 9
10 10 {
11 11 :key => 'ui.front.title',
12 12 :value_type => 'string',
13 13 :default_value => 'Grader'
14 14 },
15 15
16 16 {
17 17 :key => 'ui.front.welcome_message',
18 18 :value_type => 'string',
19 19 :default_value => 'Welcome!'
20 20 },
21 21
22 22 {
23 23 :key => 'ui.show_score',
24 24 :value_type => 'boolean',
25 25 :default_value => 'true'
26 26 },
27 27
28 28 {
29 29 :key => 'contest.time_limit',
30 30 :value_type => 'string',
31 31 :default_value => 'unlimited',
32 32 :description => 'Time limit in format hh:mm, or "unlimited" for contests with no time limits. This config is CACHED. Restart the server before the change can take effect.'
33 33 },
34 34
35 35 {
36 36 :key => 'system.mode',
37 37 :value_type => 'string',
38 38 :default_value => 'standard',
39 39 :description => 'Current modes are "standard", "contest", "indv-contest", and "analysis".'
40 40 },
41 41
42 42 {
43 43 :key => 'contest.name',
44 44 :value_type => 'string',
45 45 :default_value => 'Grader',
46 46 :description => 'This name will be shown on the user header bar.'
47 47 },
48 48
49 49 {
50 50 :key => 'contest.multisites',
51 51 :value_type => 'boolean',
52 52 :default_value => 'false',
53 53 :description => 'If the server is in contest mode and this option is true, on the log in of the admin a menu for site selections is shown.'
54 54 },
55 55
56 + #---------------------------- right --------------------------------
56 57 {
57 58 :key => 'right.user_hall_of_fame',
58 59 :value_type => 'boolean',
59 60 :default_value => 'false',
60 61 :description => 'If true, any user can access hall of fame page.'
61 62 },
62 63
63 64 {
64 65 :key => 'right.multiple_ip_login',
65 66 :value_type => 'boolean',
66 67 :default_value => 'true',
67 68 :description => 'When change from true to false, a user can login from the first IP they logged into afterward.'
68 69 },
69 70
70 71 {
71 72 :key => 'right.user_view_submission',
72 73 :value_type => 'boolean',
73 74 :default_value => 'false',
74 75 :description => 'If true, any user can view submissions of every one.'
75 76 },
76 77
78 + {
79 + :key => 'right.bypass_agreement',
80 + :value_type => 'boolean',
81 + :default_value => 'true',
82 + :description => 'When false, a user must accept usage agreement before login'
83 + },
84 +
85 + {
86 + :key => 'right.heartbeat_response',
87 + :value_type => 'string',
88 + :default_value => 'OK',
89 + :description => 'Heart beat response text'
90 + },
91 +
77 92 # If Configuration['system.online_registration'] is true, the
78 93 # system allows online registration, and will use these
79 94 # information for sending confirmation emails.
80 95 {
81 96 :key => 'system.online_registration.smtp',
82 97 :value_type => 'string',
83 98 :default_value => 'smtp.somehost.com'
84 99 },
85 100
86 101 {
87 102 :key => 'system.online_registration.from',
88 103 :value_type => 'string',
89 104 :default_value => 'your.email@address'
90 105 },
91 106
92 107 {
93 108 :key => 'system.admin_email',
94 109 :value_type => 'string',
95 110 :default_value => 'admin@admin.email'
96 111 },
97 112
98 113 {
99 114 :key => 'system.user_setting_enabled',
100 115 :value_type => 'boolean',
101 116 :default_value => 'true',
102 117 :description => 'If this option is true, users can change their settings'
103 118 },
104 119
105 120 {
106 121 :key => 'system.user_setting_enabled',
107 122 :value_type => 'boolean',
108 123 :default_value => 'true',
109 124 :description => 'If this option is true, users can change their settings'
110 125 },
111 126
112 127 # If Configuration['contest.test_request.early_timeout'] is true
113 128 # the user will not be able to use test request at 30 minutes
114 129 # before the contest ends.
115 130 {
116 131 :key => 'contest.test_request.early_timeout',
117 132 :value_type => 'boolean',
118 133 :default_value => 'false'
119 134 },
120 135
121 136 {
122 137 :key => 'system.multicontests',
123 138 :value_type => 'boolean',
124 139 :default_value => 'false'
125 140 },
126 141
127 142 {
128 143 :key => 'contest.confirm_indv_contest_start',
129 144 :value_type => 'boolean',
130 145 :default_value => 'false'
131 146 },
132 147
133 148 {
134 149 :key => 'contest.default_contest_name',
135 150 :value_type => 'string',
136 151 :default_value => 'none',
137 152 :description => "New user will be assigned to this contest automatically, if it exists. Set to 'none' if there is no default contest."
138 153 }
139 154
140 155 ]
141 156
142 157
143 158 def create_configuration_key(key,
144 159 value_type,
145 160 default_value,
146 161 description='')
147 162 conf = (GraderConfiguration.find_by_key(key) ||
148 163 GraderConfiguration.new(:key => key,
149 164 :value_type => value_type,
150 165 :value => default_value))
151 166 conf.description = description
152 167 conf.save
153 168 end
154 169
155 170 def seed_config
156 171 CONFIGURATIONS.each do |conf|
157 172 if conf.has_key? :description
158 173 desc = conf[:description]
159 174 else
160 175 desc = ''
161 176 end
162 177 create_configuration_key(conf[:key],
163 178 conf[:value_type],
164 179 conf[:default_value],
165 180 desc)
166 181 end
167 182 end
168 183
169 184 def seed_roles
170 185 return if Role.find_by_name('admin')
171 186
172 187 role = Role.create(:name => 'admin')
173 188 user_admin_right = Right.create(:name => 'user_admin',
174 189 :controller => 'user_admin',
175 190 :action => 'all')
176 191 problem_admin_right = Right.create(:name=> 'problem_admin',
177 192 :controller => 'problems',
178 193 :action => 'all')
179 194
180 195 graders_right = Right.create(:name => 'graders_admin',
181 196 :controller => 'graders',
182 197 :action => 'all')
183 198
184 199 role.rights << user_admin_right;
185 200 role.rights << problem_admin_right;
186 201 role.rights << graders_right;
187 202 role.save
188 203 end
189 204
190 205 def seed_root
191 206 return if User.find_by_login('root')
192 207
193 208 root = User.new(:login => 'root',
194 209 :full_name => 'Administrator',
195 210 :alias => 'root')
196 211 root.password = 'ioionrails';
197 212
198 213 class << root
199 214 public :encrypt_new_password
200 215 def valid?(context=nil)
201 216 true
202 217 end
203 218 end
204 219
205 220 root.encrypt_new_password
206 221
207 222 root.roles << Role.find_by_name('admin')
208 223
209 224 root.activated = true
210 225 root.save
211 226 end
212 227
213 228 def seed_users_and_roles
214 229 seed_roles
215 230 seed_root
216 231 end
217 232
218 233 def seed_more_languages
219 234 Language.delete_all
220 235 Language.create( name: 'c', pretty_name: 'C', ext: 'c', common_ext: 'c' )
221 236 Language.create( name: 'cpp', pretty_name: 'C++', ext: 'cpp', common_ext: 'cpp,cc' )
222 237 Language.create( name: 'pas', pretty_name: 'Pascal', ext: 'pas', common_ext: 'pas' )
223 238 Language.create( name: 'ruby', pretty_name: 'Ruby', ext: 'rb', common_ext: 'rb' )
224 239 Language.create( name: 'python', pretty_name: 'Python', ext: 'py', common_ext: 'py' )
225 240 Language.create( name: 'java', pretty_name: 'Java', ext: 'java', common_ext: 'java' )
226 241 end
227 242
228 243 seed_config
229 244 seed_users_and_roles
230 245 seed_more_languages
@@ -1,18 +1,18
1 1 ENV["RAILS_ENV"] = "test"
2 2 require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
3 3
4 4 def clear_all_tasks
5 - Task.find(:all).each do |task|
5 + Task.all.each do |task|
6 6 task.destroy
7 7 end
8 8 end
9 9
10 10
11 11 clear_all_tasks
12 12
13 13 (1..1000).each do |i|
14 14 Task.create(:id => i,
15 15 :submission_id => i,
16 16 :status => Task::STATUS_INQUEUE)
17 17 end
18 18
@@ -1,14 +1,13
1 1 ENV["RAILS_ENV"] = "test"
2 2 require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
3 3
4 4 def clear_all_tasks
5 - Task.find(:all).each do |task|
5 + Task.all.each do |task|
6 6 task.destroy
7 7 end
8 8 end
9 9
10 - puts Task.find(:all,
11 - :conditions => {:status => Task::STATUS_COMPLETE}).length
10 + puts Task.where(status: Task::STATUS_COMPLETE).length
12 11
13 12 clear_all_tasks
14 13
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
deleted file
You need to be logged in to leave comments. Login now