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

r790:7ddf708c67bd - - 16 files changed: 374 inserted, 350 deleted

@@ -0,0 +1,20
1 + .panel.panel-primary
2 + .panel-heading
3 + Date ranges
4 + .panel-body
5 + .row.form-group
6 + .col-md-4
7 + From Date:
8 + .col-md-6
9 + .input-group.date
10 + = text_field_tag :since_datetime, nil, class: 'form-control'
11 + %span.input-group-addon
12 + %span.glyphicon.glyphicon-calendar
13 + .row.form-group
14 + .col-md-4
15 + Until Date:
16 + .col-md-6
17 + .input-group.date
18 + = text_field_tag :until_datetime, nil, class: 'form-control'
19 + %span.input-group-addon
20 + %span.glyphicon.glyphicon-calendar
@@ -0,0 +1,5
1 + class AddIndexToSubmission < ActiveRecord::Migration[5.2]
2 + def change
3 + add_index :submissions, :submitted_at
4 + end
5 + end
@@ -0,0 +1,5
1 + class AddIdToGroupUser < ActiveRecord::Migration[5.2]
2 + def change
3 + add_column :groups_users, :id, :primary_key
4 + end
5 + end
@@ -1,116 +1,116
1 1 source 'https://rubygems.org'
2 2 git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 3
4 4 #rails
5 5 gem 'rails', '~>5.2'
6 6 gem 'activerecord-session_store'
7 7 gem 'puma'
8 8
9 9 # Reduces boot times through caching; required in config/boot.rb
10 10 gem 'bootsnap', '>= 1.1.0', require: false
11 11
12 12 # Bundle edge Rails instead:
13 13 # gem 'rails', :git => 'git://github.com/rails/rails.git'
14 14
15 15 #---------------- database ---------------------
16 16 #the database
17 17 gem 'mysql2'
18 18 #for testing
19 19 gem 'sqlite3'
20 20 gem 'rails-controller-testing'
21 21 #for dumping database into yaml
22 22 gem 'yaml_db'
23 23
24 24
25 25 #------------- assset pipeline -----------------
26 26 # Gems used only for assets and not required
27 27 # in production environments by default.
28 28 #sass-rails is depricated
29 29 #gem 'sass-rails'
30 30 gem 'sassc-rails'
31 31 gem 'coffee-rails'
32 32
33 33 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
34 34 # gem 'therubyracer', :platforms => :ruby
35 35
36 36 gem 'uglifier'
37 37
38 38 gem 'haml'
39 39 gem 'haml-rails'
40 40
41 41 # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
42 42 #gem 'turbolinks', '~> 5'
43 43 # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
44 44 gem 'jbuilder', '~> 2.5'
45 45
46 46
47 47 #in-place editor
48 48 gem 'best_in_place', '~> 3.0.1'
49 49
50 50 # jquery addition
51 51 gem 'jquery-rails'
52 52 gem 'jquery-ui-rails'
53 - gem 'jquery-timepicker-addon-rails'
53 + #gem 'jquery-timepicker-addon-rails'
54 54 gem 'jquery-tablesorter'
55 55 gem 'jquery-countdown-rails'
56 56
57 57 #syntax highlighter
58 58 gem 'rouge'
59 59
60 60 #bootstrap add-ons
61 61 gem 'bootstrap-sass', '~> 3.4.1'
62 62 gem 'bootstrap-switch-rails'
63 63 gem 'bootstrap-toggle-rails'
64 64 gem 'autoprefixer-rails'
65 - gem 'momentjs-rails'
65 + gem 'momentjs-rails', '>= 2.9.0'
66 66 gem 'rails_bootstrap_sortable'
67 - gem 'bootstrap-datepicker-rails'
68 - gem 'bootstrap3-datetimepicker-rails'
67 + #gem 'bootstrap-datepicker-rails'
68 + gem 'bootstrap3-datetimepicker-rails', '~> 4.17.47'
69 69 #gem 'jquery-datatables-rails'
70 70
71 71 #----------- user interface -----------------
72 72 gem 'simple_form'
73 73 #select 2
74 74 gem 'select2-rails'
75 75 #ace editor
76 76 gem 'ace-rails-ap'
77 77 #paginator
78 - gem 'will_paginate', '~> 3.0.7'
78 + #gem 'will_paginate', '~> 3.0.7'
79 79
80 80 gem 'mail'
81 81 gem 'rdiscount'
82 82 gem 'dynamic_form'
83 83 gem 'in_place_editing'
84 84 #gem 'verification', :git => 'https://github.com/sikachu/verification.git'
85 85
86 86
87 87 #---------------- testiing -----------------------
88 88 gem 'minitest-reporters'
89 89
90 90 #---------------- for console --------------------
91 91 gem 'fuzzy-string-match'
92 92
93 93
94 94 group :development, :test do
95 95 # Call 'byebug' anywhere in the code to stop execution and get a debugger console
96 96 gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
97 97 end
98 98
99 99 group :development do
100 100 # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
101 101 gem 'web-console', '>= 3.3.0'
102 102 gem 'listen', '>= 3.0.5', '< 3.2'
103 103 # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
104 104 gem 'spring'
105 105 gem 'spring-watcher-listen', '~> 2.0.0'
106 106 end
107 107
108 108 group :test do
109 109 # Adds support for Capybara system testing and selenium driver
110 110 gem 'capybara', '>= 2.15'
111 111 gem 'selenium-webdriver'
112 112 # Easy installation and use of chromedriver to run system tests with Chrome
113 113 #gem 'chromedriver-helper'
114 114 gem 'webdriver'
115 115 end
116 116
@@ -1,336 +1,314
1 1 GEM
2 2 remote: https://rubygems.org/
3 3 specs:
4 4 RubyInline (3.12.4)
5 5 ZenTest (~> 4.3)
6 6 ZenTest (4.11.2)
7 7 ace-rails-ap (4.2)
8 8 actioncable (5.2.4.2)
9 9 actionpack (= 5.2.4.2)
10 10 nio4r (~> 2.0)
11 11 websocket-driver (>= 0.6.1)
12 12 actionmailer (5.2.4.2)
13 13 actionpack (= 5.2.4.2)
14 14 actionview (= 5.2.4.2)
15 15 activejob (= 5.2.4.2)
16 16 mail (~> 2.5, >= 2.5.4)
17 17 rails-dom-testing (~> 2.0)
18 18 actionpack (5.2.4.2)
19 19 actionview (= 5.2.4.2)
20 20 activesupport (= 5.2.4.2)
21 21 rack (~> 2.0, >= 2.0.8)
22 22 rack-test (>= 0.6.3)
23 23 rails-dom-testing (~> 2.0)
24 24 rails-html-sanitizer (~> 1.0, >= 1.0.2)
25 25 actionview (5.2.4.2)
26 26 activesupport (= 5.2.4.2)
27 27 builder (~> 3.1)
28 28 erubi (~> 1.4)
29 29 rails-dom-testing (~> 2.0)
30 30 rails-html-sanitizer (~> 1.0, >= 1.0.3)
31 31 activejob (5.2.4.2)
32 32 activesupport (= 5.2.4.2)
33 33 globalid (>= 0.3.6)
34 34 activemodel (5.2.4.2)
35 35 activesupport (= 5.2.4.2)
36 36 activerecord (5.2.4.2)
37 37 activemodel (= 5.2.4.2)
38 38 activesupport (= 5.2.4.2)
39 39 arel (>= 9.0)
40 40 activerecord-session_store (1.1.3)
41 41 actionpack (>= 4.0)
42 42 activerecord (>= 4.0)
43 43 multi_json (~> 1.11, >= 1.11.2)
44 44 rack (>= 1.5.2, < 3)
45 45 railties (>= 4.0)
46 46 activestorage (5.2.4.2)
47 47 actionpack (= 5.2.4.2)
48 48 activerecord (= 5.2.4.2)
49 49 marcel (~> 0.3.1)
50 50 activesupport (5.2.4.2)
51 51 concurrent-ruby (~> 1.0, >= 1.0.2)
52 52 i18n (>= 0.7, < 2)
53 53 minitest (~> 5.1)
54 54 tzinfo (~> 1.1)
55 55 addressable (2.7.0)
56 56 public_suffix (>= 2.0.2, < 5.0)
57 57 ansi (1.5.0)
58 58 arel (9.0.0)
59 59 autoprefixer-rails (9.5.1)
60 60 execjs
61 61 best_in_place (3.0.3)
62 62 actionpack (>= 3.2)
63 63 railties (>= 3.2)
64 64 bindex (0.8.1)
65 65 bootsnap (1.4.6)
66 66 msgpack (~> 1.0)
67 - bootstrap-datepicker-rails (1.8.0.1)
68 - railties (>= 3.0)
69 67 bootstrap-sass (3.4.1)
70 68 autoprefixer-rails (>= 5.2.1)
71 69 sassc (>= 2.0.0)
72 70 bootstrap-switch-rails (3.3.4)
73 71 bootstrap-toggle-rails (2.2.1.0)
74 72 bootstrap3-datetimepicker-rails (4.17.47)
75 73 momentjs-rails (>= 2.8.1)
76 74 builder (3.2.4)
77 75 byebug (11.0.1)
78 76 capybara (3.15.1)
79 77 addressable
80 78 mini_mime (>= 0.1.3)
81 79 nokogiri (~> 1.8)
82 80 rack (>= 1.6.0)
83 81 rack-test (>= 0.6.3)
84 82 regexp_parser (~> 1.2)
85 83 xpath (~> 3.2)
86 84 childprocess (3.0.0)
87 85 coffee-rails (4.2.2)
88 86 coffee-script (>= 2.2.0)
89 87 railties (>= 4.0.0)
90 88 coffee-script (2.4.1)
91 89 coffee-script-source
92 90 execjs
93 91 coffee-script-source (1.12.2)
94 92 concurrent-ruby (1.1.6)
95 93 crass (1.0.6)
96 94 dynamic_form (1.1.4)
97 95 erubi (1.9.0)
98 96 erubis (2.7.0)
99 97 execjs (2.7.0)
100 98 ffi (1.10.0)
101 99 fuzzy-string-match (1.0.1)
102 100 RubyInline (>= 3.8.6)
103 101 globalid (0.4.2)
104 102 activesupport (>= 4.2.0)
105 103 haml (5.0.4)
106 104 temple (>= 0.8.0)
107 105 tilt
108 106 haml-rails (1.0.0)
109 107 actionpack (>= 4.0.1)
110 108 activesupport (>= 4.0.1)
111 109 haml (>= 4.0.6, < 6.0)
112 110 html2haml (>= 1.0.1)
113 111 railties (>= 4.0.1)
114 112 html2haml (2.2.0)
115 113 erubis (~> 2.7.0)
116 114 haml (>= 4.0, < 6)
117 115 nokogiri (>= 1.6.0)
118 116 ruby_parser (~> 3.5)
119 117 i18n (1.8.2)
120 118 concurrent-ruby (~> 1.0)
121 119 in_place_editing (1.2.0)
122 120 jbuilder (2.10.0)
123 121 activesupport (>= 5.0.0)
124 122 jquery-countdown-rails (2.0.2)
125 - jquery-datatables-rails (3.4.0)
126 - actionpack (>= 3.1)
127 - jquery-rails
128 - railties (>= 3.1)
129 - sass-rails
130 123 jquery-rails (4.3.3)
131 124 rails-dom-testing (>= 1, < 3)
132 125 railties (>= 4.2.0)
133 126 thor (>= 0.14, < 2.0)
134 127 jquery-tablesorter (1.26.1)
135 128 railties (>= 3.2, < 6)
136 129 jquery-timepicker-addon-rails (1.4.1)
137 130 railties (>= 3.1)
138 131 jquery-ui-rails (6.0.1)
139 132 railties (>= 3.2.16)
140 133 listen (3.1.5)
141 134 rb-fsevent (~> 0.9, >= 0.9.4)
142 135 rb-inotify (~> 0.9, >= 0.9.7)
143 136 ruby_dep (~> 1.2)
144 137 loofah (2.4.0)
145 138 crass (~> 1.0.2)
146 139 nokogiri (>= 1.5.9)
147 140 mail (2.7.1)
148 141 mini_mime (>= 0.1.1)
149 142 marcel (0.3.3)
150 143 mimemagic (~> 0.3.2)
151 144 method_source (1.0.0)
152 145 mimemagic (0.3.4)
153 146 mini_mime (1.0.2)
154 147 mini_portile2 (2.4.0)
155 148 minitest (5.14.0)
156 149 minitest-reporters (1.3.6)
157 150 ansi
158 151 builder
159 152 minitest (>= 5.0)
160 153 ruby-progressbar
161 154 momentjs-rails (2.20.1)
162 155 railties (>= 3.1)
163 156 msgpack (1.3.3)
164 157 multi_json (1.13.1)
165 158 mysql2 (0.5.2)
166 159 nio4r (2.5.2)
167 160 nokogiri (1.10.9)
168 161 mini_portile2 (~> 2.4.0)
169 162 public_suffix (4.0.3)
170 163 puma (4.3.3)
171 164 nio4r (~> 2.0)
172 165 rack (2.2.2)
173 166 rack-test (1.1.0)
174 167 rack (>= 1.0, < 3)
175 168 rails (5.2.4.2)
176 169 actioncable (= 5.2.4.2)
177 170 actionmailer (= 5.2.4.2)
178 171 actionpack (= 5.2.4.2)
179 172 actionview (= 5.2.4.2)
180 173 activejob (= 5.2.4.2)
181 174 activemodel (= 5.2.4.2)
182 175 activerecord (= 5.2.4.2)
183 176 activestorage (= 5.2.4.2)
184 177 activesupport (= 5.2.4.2)
185 178 bundler (>= 1.3.0)
186 179 railties (= 5.2.4.2)
187 180 sprockets-rails (>= 2.0.0)
188 181 rails-controller-testing (1.0.4)
189 182 actionpack (>= 5.0.1.x)
190 183 actionview (>= 5.0.1.x)
191 184 activesupport (>= 5.0.1.x)
192 185 rails-dom-testing (2.0.3)
193 186 activesupport (>= 4.2.0)
194 187 nokogiri (>= 1.6)
195 188 rails-html-sanitizer (1.3.0)
196 189 loofah (~> 2.3)
197 190 rails_bootstrap_sortable (2.0.6)
198 191 momentjs-rails (>= 2.8.3)
199 192 railties (5.2.4.2)
200 193 actionpack (= 5.2.4.2)
201 194 activesupport (= 5.2.4.2)
202 195 method_source
203 196 rake (>= 0.8.7)
204 197 thor (>= 0.19.0, < 2.0)
205 198 rake (13.0.1)
206 199 rb-fsevent (0.10.3)
207 200 rb-inotify (0.10.0)
208 201 ffi (~> 1.0)
209 202 rdiscount (2.2.0.1)
210 203 regexp_parser (1.7.0)
211 204 rouge (3.3.0)
212 205 ruby-progressbar (1.10.0)
213 206 ruby_dep (1.5.0)
214 207 ruby_parser (3.13.1)
215 208 sexp_processor (~> 4.9)
216 209 rubyzip (1.3.0)
217 - sass (3.7.4)
218 - sass-listen (~> 4.0.0)
219 - sass-listen (4.0.0)
220 - rb-fsevent (~> 0.9, >= 0.9.4)
221 - rb-inotify (~> 0.9, >= 0.9.7)
222 - sass-rails (5.0.7)
223 - railties (>= 4.0.0, < 6)
224 - sass (~> 3.1)
225 - sprockets (>= 2.8, < 4.0)
226 - sprockets-rails (>= 2.0, < 4.0)
227 - tilt (>= 1.1, < 3)
228 210 sassc (2.2.1)
229 211 ffi (~> 1.9)
230 212 sassc-rails (2.1.2)
231 213 railties (>= 4.0.0)
232 214 sassc (>= 2.0)
233 215 sprockets (> 3.0)
234 216 sprockets-rails
235 217 tilt
236 218 select2-rails (4.0.3)
237 219 thor (~> 0.14)
238 220 selenium-webdriver (3.142.7)
239 221 childprocess (>= 0.5, < 4.0)
240 222 rubyzip (>= 1.2.2)
241 223 sexp_processor (4.12.0)
242 224 simple_form (5.0.2)
243 225 actionpack (>= 5.0)
244 226 activemodel (>= 5.0)
245 227 spring (2.0.2)
246 228 activesupport (>= 4.2)
247 229 spring-watcher-listen (2.0.1)
248 230 listen (>= 2.7, < 4.0)
249 231 spring (>= 1.2, < 3.0)
250 232 sprockets (3.7.2)
251 233 concurrent-ruby (~> 1.0)
252 234 rack (> 1, < 3)
253 235 sprockets-rails (3.2.1)
254 236 actionpack (>= 4.0)
255 237 activesupport (>= 4.0)
256 238 sprockets (>= 3.0.0)
257 239 sqlite3 (1.4.1)
258 240 temple (0.8.1)
259 241 thor (0.20.3)
260 242 thread_safe (0.3.6)
261 243 tilt (2.0.9)
262 244 tzinfo (1.2.7)
263 245 thread_safe (~> 0.1)
264 246 uglifier (4.1.20)
265 247 execjs (>= 0.3.0, < 3)
266 248 web-console (3.7.0)
267 249 actionview (>= 5.0)
268 250 activemodel (>= 5.0)
269 251 bindex (>= 0.4.0)
270 252 railties (>= 5.0)
271 253 webdriver (0.5.0)
272 254 websocket-driver (0.7.1)
273 255 websocket-extensions (>= 0.1.0)
274 256 websocket-extensions (0.1.4)
275 - will_paginate (3.0.12)
276 257 xpath (3.2.0)
277 258 nokogiri (~> 1.8)
278 259 yaml_db (0.7.0)
279 260 rails (>= 3.0)
280 261 rake (>= 0.8.7)
281 262
282 263 PLATFORMS
283 264 ruby
284 265
285 266 DEPENDENCIES
286 267 ace-rails-ap
287 268 activerecord-session_store
288 269 autoprefixer-rails
289 270 best_in_place (~> 3.0.1)
290 271 bootsnap (>= 1.1.0)
291 - bootstrap-datepicker-rails
292 272 bootstrap-sass (~> 3.4.1)
293 273 bootstrap-switch-rails
294 274 bootstrap-toggle-rails
295 - bootstrap3-datetimepicker-rails
275 + bootstrap3-datetimepicker-rails (~> 4.17.47)
296 276 byebug
297 277 capybara (>= 2.15)
298 278 coffee-rails
299 279 dynamic_form
300 280 fuzzy-string-match
301 281 haml
302 282 haml-rails
303 283 in_place_editing
304 284 jbuilder (~> 2.5)
305 285 jquery-countdown-rails
306 - jquery-datatables-rails
307 286 jquery-rails
308 287 jquery-tablesorter
309 288 jquery-timepicker-addon-rails
310 289 jquery-ui-rails
311 290 listen (>= 3.0.5, < 3.2)
312 291 mail
313 292 minitest-reporters
314 - momentjs-rails
293 + momentjs-rails (>= 2.9.0)
315 294 mysql2
316 295 puma
317 296 rails (~> 5.2)
318 297 rails-controller-testing
319 298 rails_bootstrap_sortable
320 299 rdiscount
321 300 rouge
322 301 sassc-rails
323 302 select2-rails
324 303 selenium-webdriver
325 304 simple_form
326 305 spring
327 306 spring-watcher-listen (~> 2.0.0)
328 307 sqlite3
329 308 uglifier
330 309 web-console (>= 3.3.0)
331 310 webdriver
332 - will_paginate (~> 3.0.7)
333 311 yaml_db
334 312
335 313 BUNDLED WITH
336 - 1.16.2
314 + 1.17.2
@@ -1,55 +1,55
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 15 //= require jszip/dist/jszip.min
16 16 //= require pdfmake/build/pdfmake.min
17 17 //= require datatables.net/js/jquery.dataTables.min
18 18 //= require datatables.net-bs/js/dataTables.bootstrap.min
19 19 //= require datatables.net-buttons/js/dataTables.buttons.min
20 20 //= require datatables.net-buttons-bs/js/buttons.bootstrap.min
21 21 //= require datatables.net-buttons/js/buttons.colVis.min
22 22 //= require datatables.net-buttons/js/buttons.flash.min
23 23 //= require datatables.net-buttons/js/buttons.html5.min
24 24 //= require datatables.net-buttons/js/buttons.print.min
25 25 //= require jquery-ui
26 26 //= require bootstrap-sprockets
27 27 //= require moment
28 28 //= require moment/th
29 29 //= require bootstrap-sortable
30 30 //= require bootstrap-datetimepicker
31 31 //= require select2
32 32 //= require ace-rails-ap
33 33 //= require ace/mode-c_cpp
34 34 //= require ace/mode-python
35 35 //= require ace/mode-ruby
36 36 //= require ace/mode-pascal
37 37 //= require ace/mode-javascript
38 38 //= require ace/mode-java
39 39 //= require ace/theme-merbivore
40 40 //= require custom
41 41 //= require jquery.countdown
42 42 //-------------- addition from local_jquery -----------
43 43 //= require jquery-tablesorter
44 44 //= require best_in_place
45 45 //= require best_in_place.jquery-ui
46 46 //= require brython
47 - //= require bootstrap-datepicker
47 + //= re quire bootstrap-datepicker
48 48 //= require bootstrap-datetimepicker
49 49
50 50 // since this is after blank line, it is not downloaded
51 51 //x= require prototype
52 52 //x= require prototype_ujs
53 53 //x= require effects
54 54 //x= require dragdrop
55 55 //x= require controls
@@ -1,52 +1,53
1 1 $(document).on 'change', '.btn-file :file', ->
2 2 input = $(this)
3 3 numFiles = if input.get(0).files then input.get(0).files.length else 1
4 4 label = input.val().replace(/\\/g, '/').replace(/.*\//, '')
5 5 input.trigger 'fileselect', [
6 6 numFiles
7 7 label
8 8 ]
9 9 return
10 10
11 11
12 12 # document ready
13 13
14 14 $ ->
15 - $(".select2").select2()
15 + $(".select2").select2({
16 + })
16 17 #$(".bootstrap-switch").bootstrapSwitch()
17 18 #$(".bootstrap-toggle").bootstrapToggle()
18 19 $('.btn-file :file').on 'fileselect', (event, numFiles, label) ->
19 20 input = $(this).parents('.input-group').find(':text')
20 21 log = if numFiles > 1 then numFiles + ' files selected' else label
21 22 if input.length
22 23 input.val log
23 24 else
24 25 if log
25 26 alert log
26 27 return
27 28 $(".go-button").on 'click', (event) ->
28 29 link = $(this).attr("data-source")
29 30 url = $(link).val()
30 31 if url
31 32 window.location.href = url
32 33 return
33 34 $('.ajax-toggle').on 'click', (event) ->
34 35 target = $(event.target)
35 36 target.removeClass 'btn-default'
36 37 target.removeClass 'btn-success'
37 38 target.addClass 'btn-warning'
38 39 target.text '...'
39 40 return
40 41
41 42
42 43 #ace editor
43 44 if $("#editor").length > 0
44 45 e = ace.edit("editor")
45 46 e.setTheme('ace/theme/merbivore')
46 47 e.getSession().setTabSize(2)
47 48 e.getSession().setUseSoftTabs(true)
48 49
49 50 #best in place
50 51 jQuery(".best_in_place").best_in_place()
51 52
52 53 return
@@ -1,228 +1,228
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 17 @import "jquery-ui";
18 18 //@import "jquery.ui.core";
19 19 //@import "jquery.ui.theme";
20 20 //@import "jquery.ui.datepicker";
21 21 //@import "jquery.ui.slider";
22 - @import "jquery-ui-timepicker-addon";
23 - @import "jquery-tablesorter/theme.metro-dark";
22 + //@import "jquery-ui-timepicker-addon";
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 - @import "bootstrap-datepicker3";
36 + //@import "bootstrap-datepicker3";
37 37 @import "bootstrap-datetimepicker";
38 38 @import "datatables.net-bs/css/dataTables.bootstrap.min";
39 39 @import "datatables.net-buttons-bs/css/buttons.bootstrap.min";
40 40
41 41 //bootstrap navbar color (from)
42 42 $bgDefault: #19197b;
43 43 $bgHighlight: #06064b;
44 44 $colDefault: #8e8eb4;
45 45 $colHighlight: #ffffff;
46 46 $dropDown: false;
47 47
48 48 @font-face {
49 49 font-family: 'Glyphicons Halflings';
50 50 src: font-path('bootstrap/glyphicons-halflings-regular.eot');
51 51 src: font-path('bootstrap/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
52 52 font-path('bootstrap/glyphicons-halflings-regular.woff') format('woff'),
53 53 font-path('bootstrap/glyphicons-halflings-regular.ttf') format('truetype'),
54 54 font-path('bootstrap/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
55 55 }
56 56
57 57
58 58 .navbar-default {
59 59 background-color: $bgDefault;
60 60 border-color: $bgHighlight;
61 61
62 62 .navbar-brand {
63 63 color: $colDefault;
64 64
65 65 &:hover, &:focus {
66 66 color: $colHighlight;
67 67 }
68 68 }
69 69
70 70 .navbar-text {
71 71 color: $colDefault;
72 72 }
73 73
74 74 .navbar-nav {
75 75 > li {
76 76 > a {
77 77 color: $colDefault;
78 78
79 79 &:hover, &:focus {
80 80 color: $colHighlight;
81 81 }
82 82 }
83 83
84 84 @if $dropDown {
85 85 > .dropdown-menu {
86 86 background-color: $bgDefault;
87 87
88 88 > li {
89 89 > a {
90 90 color: $colDefault;
91 91
92 92 &:hover, &:focus {
93 93 color: $colHighlight;
94 94 background-color: $bgHighlight;
95 95 }
96 96 }
97 97
98 98 > .divider {
99 99 background-color: $bgHighlight;
100 100 }
101 101 }
102 102 }
103 103 }
104 104 }
105 105
106 106 @if $dropDown {
107 107 .open .dropdown-menu > .active {
108 108 > a, > a:hover, > a:focus {
109 109 color: $colHighlight;
110 110 background-color: $bgHighlight;
111 111 }
112 112 }
113 113 }
114 114
115 115 > .active {
116 116 > a, > a:hover, > a:focus {
117 117 color: $colHighlight;
118 118 background-color: $bgHighlight;
119 119 }
120 120 }
121 121
122 122 > .open {
123 123 > a, > a:hover, > a:focus {
124 124 color: $colHighlight;
125 125 background-color: $bgHighlight;
126 126 }
127 127 }
128 128 }
129 129
130 130 .navbar-toggle {
131 131 border-color: $bgHighlight;
132 132
133 133 &:hover, &:focus {
134 134 background-color: $bgHighlight;
135 135 }
136 136
137 137 .icon-bar {
138 138 background-color: $colDefault;
139 139 }
140 140 }
141 141
142 142 .navbar-collapse,
143 143 .navbar-form {
144 144 border-color: $colDefault;
145 145 }
146 146
147 147 .navbar-link {
148 148 color: $colDefault;
149 149
150 150 &:hover {
151 151 color: $colHighlight;
152 152 }
153 153 }
154 154 }
155 155
156 156 @media (max-width: 767px) {
157 157 .navbar-default .navbar-nav .open .dropdown-menu {
158 158 > li > a {
159 159 color: $colDefault;
160 160
161 161 &:hover, &:focus {
162 162 color: $colHighlight;
163 163 }
164 164 }
165 165
166 166 > .active {
167 167 > a, > a:hover, > a:focus {
168 168 color: $colHighlight;
169 169 background-color: $bgHighlight;
170 170 }
171 171 }
172 172 }
173 173 }
174 174
175 175 .secondnavbar {
176 176 top: 50px;
177 177 }
178 178
179 179 // --------------- bootstrap file upload ----------------------
180 180 .btn-file {
181 181 position: relative;
182 182 overflow: hidden;
183 183 }
184 184
185 185 .btn-file input[type=file] {
186 186 position: absolute;
187 187 top: 0;
188 188 right: 0;
189 189 min-width: 100%;
190 190 min-height: 100%;
191 191 font-size: 100px;
192 192 text-align: right;
193 193 filter: alpha(opacity = 0);
194 194 opacity: 0;
195 195 outline: none;
196 196 background: white;
197 197 cursor: inherit;
198 198 display: block;
199 199 }
200 200
201 201 body {
202 202 background: white image-url("topbg.jpg") repeat-x top center;
203 203
204 204 //font-size: 13px
205 205 //font-family: Tahoma, "sans-serif"
206 206 margin: 10px;
207 207 padding: 10px;
208 208 padding-top: 60px;
209 209 }
210 210
211 211 // ------------------ bootstrap sortable --------------------
212 212 table.sortable th {
213 213 padding-right: 20px !important;
214 214
215 215 span.sign {
216 216 right: (-15px) !important;
217 217 }
218 218
219 219 &.text-right {
220 220 padding-left: 20px !important;
221 221 padding-right: 8px !important;
222 222
223 223 &:after, span.sign {
224 224 left: (-15px) !important;
225 225 }
226 226 }
227 227 }
228 228
@@ -1,228 +1,238
1 1 require 'ipaddr'
2 2
3 3 class ApplicationController < ActionController::Base
4 4 protect_from_forgery
5 5
6 6 before_action :current_user
7 7
8 8 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
9 9 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
10 10 WHITELIST_IGNORE_CONF_KEY = 'right.whitelist_ignore'
11 11 WHITELIST_IP_CONF_KEY = 'right.whitelist_ip'
12 12
13 13 #report and redirect for unauthorized activities
14 14 def unauthorized_redirect(notice = 'You are not authorized to view the page you requested')
15 15 flash[:notice] = notice
16 16 redirect_to login_main_path
17 17 end
18 18
19 19 # Returns the current logged-in user (if any).
20 20 def current_user
21 21 return nil unless session[:user_id]
22 22 @current_user ||= User.find(session[:user_id])
23 23 end
24 24
25 25 def admin_authorization
26 26 return false unless check_valid_login
27 27 user = User.includes(:roles).find(session[:user_id])
28 28 unless user.admin?
29 29 unauthorized_redirect
30 30 return false
31 31 end
32 32 return true
33 33 end
34 34
35 35 def authorization_by_roles(allowed_roles)
36 36 return false unless check_valid_login
37 37 user = User.find(session[:user_id])
38 38 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
39 39 unauthorized_redirect
40 40 return false
41 41 end
42 42 end
43 43
44 44 def testcase_authorization
45 45 #admin always has privileged
46 46 if @current_user.admin?
47 47 return true
48 48 end
49 49
50 50 unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
51 51 end
52 52
53 53
54 54 protected
55 55
56 56 #redirect to root (and also force logout)
57 57 #if the user is not logged_in or the system is in "ADMIN ONLY" mode
58 58 def check_valid_login
59 59 #check if logged in
60 60 unless session[:user_id]
61 61 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
62 62 unauthorized_redirect('You need to login but you cannot log in at this time')
63 63 else
64 64 unauthorized_redirect('You need to login')
65 65 end
66 66 return false
67 67 end
68 68
69 69 # check if run in single user mode
70 70 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
71 71 if @current_user==nil || (!@current_user.admin?)
72 72 unauthorized_redirect('You cannot log in at this time')
73 73 return false
74 74 end
75 75 end
76 76
77 77 # check if the user is enabled
78 78 unless @current_user.enabled? || @current_user.admin?
79 79 unauthorized_redirect 'Your account is disabled'
80 80 return false
81 81 end
82 82
83 83 # check if user ip is allowed
84 84 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
85 85 unless is_request_ip_allowed?
86 86 unauthorized_redirect 'Your IP is not allowed to login at this time.'
87 87 return false
88 88 end
89 89 end
90 90
91 91 if GraderConfiguration.multicontests?
92 92 return true if @current_user.admin?
93 93 begin
94 94 if @current_user.contest_stat(true).forced_logout
95 95 flash[:notice] = 'You have been automatically logged out.'
96 96 redirect_to :controller => 'main', :action => 'index'
97 97 end
98 98 rescue
99 99 end
100 100 end
101 101 return true
102 102 end
103 103
104 104 #redirect to root (and also force logout)
105 105 #if the user use different ip from the previous connection
106 106 # only applicable when MULTIPLE_IP_LOGIN options is false only
107 107 def authenticate_by_ip_address
108 108 #this assume that we have already authenticate normally
109 109 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
110 110 user = User.find(session[:user_id])
111 111 if (!user.admin? && user.last_ip && user.last_ip != request.remote_ip)
112 112 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
113 113 redirect_to :controller => 'main', :action => 'login'
114 114 return false
115 115 end
116 116 unless user.last_ip
117 117 user.last_ip = request.remote_ip
118 118 user.save
119 119 end
120 120 end
121 121 return true
122 122 end
123 123
124 124 def authorization
125 125 return false unless check_valid_login
126 126 user = User.find(session[:user_id])
127 127 unless user.roles.detect { |role|
128 128 role.rights.detect{ |right|
129 129 right.controller == self.class.controller_name and
130 130 (right.action == 'all' || right.action == action_name)
131 131 }
132 132 }
133 133 flash[:notice] = 'You are not authorized to view the page you requested'
134 134 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
135 135 redirect_to :controller => 'main', :action => 'login'
136 136 return false
137 137 end
138 138 end
139 139
140 140 def verify_time_limit
141 141 return true if session[:user_id]==nil
142 142 user = User.find(session[:user_id], :include => :site)
143 143 return true if user==nil || user.site == nil
144 144 if user.contest_finished?
145 145 flash[:notice] = 'Error: the contest you are participating is over.'
146 146 redirect_to :back
147 147 return false
148 148 end
149 149 return true
150 150 end
151 151
152 152 def is_request_ip_allowed?
153 153 unless GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
154 154 user_ip = IPAddr.new(request.remote_ip)
155 155
156 156 GraderConfiguration[WHITELIST_IP_CONF_KEY].delete(' ').split(',').each do |ips|
157 157 allow_ips = IPAddr.new(ips)
158 158 if allow_ips.include?(user_ip)
159 159 return true
160 160 end
161 161 end
162 162 return false
163 163 end
164 164 return true
165 165 end
166 166
167 167 #function for datatable ajax query
168 168 #return record,total_count,filter_count
169 169 def process_query_record(record,
170 - total_count: nil,
171 - select: '',
172 - global_search: [],
173 - no_search: false,
174 - force_order: '',
175 - date_filter: '', date_param_since: 'date_since',date_param_until: 'date_until')
170 + total_count: nil,
171 + select: '',
172 + global_search: [],
173 + no_search: false,
174 + force_order: '',
175 + date_filter: '', date_param_since: 'date_since',date_param_until: 'date_until',
176 + hard_limit: nil)
176 177 arel_table = record.model.arel_table
177 178
178 179 if !no_search && params['search']
179 180 global_value = record.model.sanitize_sql(params['search']['value'].strip.downcase)
180 181 if !global_value.blank?
181 182 global_value.split.each do |value|
182 183 global_where = global_search.map{|f| "LOWER(#{f}) like '%#{value}%'"}.join(' OR ')
183 184 record = record.where(global_where)
184 185 end
185 186 end
186 187
187 188 params['columns'].each do |i, col|
188 189 if !col['search']['value'].blank?
189 190 record = record.where(arel_table[col['name']].lower.matches("%#{col['search']['value'].strip.downcase}%"))
190 191 end
191 192 end
192 193 end
193 194
194 195 if !date_filter.blank?
195 - date_since = Time.parse( params[:date_since] ) rescue Time.new(1,1,1)
196 - date_until = Time.parse( params[:date_until] ) rescue Time.zone.now()
197 - date_range = date_since..date_until
196 + param_since = params[date_param_since]
197 + param_until = params[date_param_until]
198 + date_since = Time.zone.parse( param_since ) || Time.new(1,1,1) rescue Time.new(1,1,1)
199 + date_until = Time.zone.parse( param_until ) || Time.zone.now() rescue Time.zone.now()
200 + date_range = date_since..(date_until.end_of_day)
198 201 record = record.where(date_filter.to_sym => date_range)
199 202 end
200 203
201 204 if force_order.blank?
202 205 if params['order']
203 206 params['order'].each do |i, o|
204 207 colName = params['columns'][o['column']]['name']
205 208 colName = "#{record.model.table_name}.#{colName}" if colName.upcase == 'ID'
206 209 record = record.order("#{colName} #{o['dir'].casecmp('desc') != 0 ? 'ASC' : 'DESC'}") unless colName.blank?
207 210 end
208 211 end
209 212 else
210 213 record = record.order(force_order)
211 214 end
212 215
213 216 filterCount = record.count(record.model.primary_key)
214 217 # if .group() is used, filterCount might be like {id_1: count_1, id_2: count_2, ...}
215 218 # so we should count the result again..
216 219 if filterCount.is_a? Hash
217 220 filterCount = filterCount.count
218 221 end
219 222
220 - record = record.offset(params['start'] || 0).limit(params['length'] || 100)
223 +
224 + record = record.offset(params['start'] || 0)
225 + record = record.limit(hard_limit)
226 + if (params['length'])
227 + limit = params['length'].to_i
228 + limit == hard_limit if (hard_limit && hard_limit < limit)
229 + record = record.limit(limit)
230 + end
221 231 if (!select.blank?)
222 232 record = record.select(select)
223 233 end
224 234
225 235 return record, total_count || record.model.count, filterCount
226 236 end
227 237
228 238 end
@@ -1,365 +1,364
1 1 require 'csv'
2 2
3 3 class ReportController < ApplicationController
4 4
5 5 before_action :check_valid_login
6 6
7 - before_action :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score, :current_score]
7 + before_action :admin_authorization, only: [:login_stat,:submission, :submission_query, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score, :current_score]
8 8
9 9 before_action(only: [:problem_hof]) { |c|
10 10 return false unless check_valid_login
11 11
12 12 admin_authorization unless GraderConfiguration["right.user_view_submission"]
13 13 }
14 14
15 15 def max_score
16 16 end
17 17
18 18 def current_score
19 19 @problems = Problem.available_problems
20 20 if params[:group_id]
21 21 @group = Group.find(params[:group_id])
22 22 @users = @group.users.where(enabled: true)
23 23 else
24 24 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
25 25 end
26 26 @scorearray = calculate_max_score(@problems, @users,0,0,true)
27 27
28 28 #rencer accordingly
29 29 if params[:button] == 'download' then
30 30 csv = gen_csv_from_scorearray(@scorearray,@problems)
31 31 send_data csv, filename: 'max_score.csv'
32 32 else
33 33 #render template: 'user_admin/user_stat'
34 34 render 'current_score'
35 35 end
36 36 end
37 37
38 38 def show_max_score
39 39 #process parameters
40 40 #problems
41 41 @problems = []
42 42 if params[:problem_id]
43 43 params[:problem_id].each do |id|
44 44 next unless id.strip != ""
45 45 pid = Problem.find_by_id(id.to_i)
46 46 @problems << pid if pid
47 47 end
48 48 end
49 49
50 50 #users
51 51 @users = if params[:users] == "all" then
52 52 User.includes(:contests).includes(:contest_stat)
53 53 else
54 54 User.includes(:contests).includes(:contest_stat).where(enabled: true)
55 55 end
56 56
57 57 #set up range from param
58 58 @since_id = params.fetch(:from_id, 0).to_i
59 59 @until_id = params.fetch(:to_id, 0).to_i
60 60 @since_id = nil if @since_id == 0
61 61 @until_id = nil if @until_id == 0
62 62
63 63 #calculate the routine
64 64 @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
65 65
66 66 #rencer accordingly
67 67 if params[:button] == 'download' then
68 68 csv = gen_csv_from_scorearray(@scorearray,@problems)
69 69 send_data csv, filename: 'max_score.csv'
70 70 else
71 71 #render template: 'user_admin/user_stat'
72 72 render 'max_score'
73 73 end
74 74
75 75 end
76 76
77 77 def score
78 78 if params[:commit] == 'download csv'
79 79 @problems = Problem.all
80 80 else
81 81 @problems = Problem.available_problems
82 82 end
83 83 @users = User.includes(:contests, :contest_stat).where(enabled: true)
84 84 @scorearray = Array.new
85 85 @users.each do |u|
86 86 ustat = Array.new
87 87 ustat[0] = u
88 88 @problems.each do |p|
89 89 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
90 90 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
91 91 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
92 92 else
93 93 ustat << [0,false]
94 94 end
95 95 end
96 96 @scorearray << ustat
97 97 end
98 98 if params[:commit] == 'download csv' then
99 99 csv = gen_csv_from_scorearray(@scorearray,@problems)
100 100 send_data csv, filename: 'last_score.csv'
101 101 else
102 102 render template: 'user_admin/user_stat'
103 103 end
104 104
105 105 end
106 106
107 107 def login_stat
108 108 @logins = Array.new
109 109
110 110 date_and_time = '%Y-%m-%d %H:%M'
111 111 begin
112 112 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
113 113 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
114 114 rescue
115 115 @since_time = DateTime.new(1000,1,1)
116 116 end
117 117 begin
118 118 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
119 119 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
120 120 rescue
121 121 @until_time = DateTime.new(3000,1,1)
122 122 end
123 123
124 124 User.all.each do |user|
125 125 @logins << { id: user.id,
126 126 login: user.login,
127 127 full_name: user.full_name,
128 128 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
129 129 user.id,@since_time,@until_time)
130 130 .count(:id),
131 131 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
132 132 user.id,@since_time,@until_time)
133 133 .minimum(:created_at),
134 134 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
135 135 user.id,@since_time,@until_time)
136 136 .maximum(:created_at),
137 137 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
138 138 user.id,@since_time,@until_time)
139 139 .select(:ip_address).uniq
140 140
141 141 }
142 142 end
143 143 end
144 144
145 145 def submission
146 - date_and_time = '%Y-%m-%d %H:%M'
147 - begin
148 - @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
149 - rescue
150 - @since_time = DateTime.new(1000,1,1)
151 - end
152 - begin
153 - @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
154 - rescue
155 - @until_time = DateTime.new(3000,1,1)
156 - end
157 -
158 - @submissions = Submission
159 - .joins(:problem).joins(:user)
160 146 end
161 147
162 148 def submission_query
163 149 @submissions = Submission
164 - .joins(:problem).joins(:user)
150 + .includes(:problem).includes(:user).includes(:language)
151 +
152 + if params[:problem]
153 + @submission = @submission.where(problem_id: params[:problem])
154 + end
155 +
156 + case params[:users]
157 + when 'enabled'
158 + @submissions = @submissions.where('user.enabled': true)
159 + when 'group'
160 + @submissions = @submissions.joins(user: :groups).where(user: {groups: {id: params[:groups]}}) if params[:groups]
161 + end
162 +
163 + #set default
164 + params[:since_datetime] = Date.today.to_s if params[:since_datetime].blank?
165 165
166 166 @submissions, @recordsTotal, @recordsFiltered = process_query_record( @submissions,
167 167 global_search: ['user.login','user.full_name','problem.name','problem.full_name','points'],
168 168 date_filter: 'submitted_at',
169 169 date_param_since: 'since_datetime',
170 170 date_param_until: 'until_datetime',
171 + hard_limit: 100_000
171 172 )
172 - puts '-------------------------------'
173 - puts @submissions
174 173 end
175 174
176 175 def problem_hof
177 176 # gen problem list
178 177 @user = User.find(session[:user_id])
179 178 @problems = @user.available_problems
180 179
181 180 # get selected problems or the default
182 181 if params[:id]
183 182 begin
184 183 @problem = Problem.available.find(params[:id])
185 184 rescue
186 185 redirect_to action: :problem_hof
187 186 flash[:notice] = 'Error: submissions for that problem are not viewable.'
188 187 return
189 188 end
190 189 end
191 190
192 191 return unless @problem
193 192
194 193 @by_lang = {} #aggregrate by language
195 194
196 195 range =65
197 196 @histogram = { data: Array.new(range,0), summary: {} }
198 197 @summary = {count: 0, solve: 0, attempt: 0}
199 198 user = Hash.new(0)
200 199 Submission.where(problem_id: @problem.id).find_each do |sub|
201 200 #histogram
202 201 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
203 202 @histogram[:data][d.to_i] += 1 if d < range
204 203
205 204 next unless sub.points
206 205 @summary[:count] += 1
207 206 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
208 207
209 208 lang = Language.find_by_id(sub.language_id)
210 209 next unless lang
211 210 next unless sub.points >= @problem.full_score
212 211
213 212 #initialize
214 213 unless @by_lang.has_key?(lang.pretty_name)
215 214 @by_lang[lang.pretty_name] = {
216 215 runtime: { avail: false, value: 2**30-1 },
217 216 memory: { avail: false, value: 2**30-1 },
218 217 length: { avail: false, value: 2**30-1 },
219 218 first: { avail: false, value: DateTime.new(3000,1,1) }
220 219 }
221 220 end
222 221
223 222 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
224 223 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
225 224 end
226 225
227 226 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
228 227 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
229 228 end
230 229
231 230 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and sub.user and
232 231 !sub.user.admin?
233 232 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
234 233 end
235 234
236 235 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
237 236 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
238 237 end
239 238 end
240 239
241 240 #process user_id
242 241 @by_lang.each do |lang,prop|
243 242 prop.each do |k,v|
244 243 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
245 244 end
246 245 end
247 246
248 247 #sum into best
249 248 if @by_lang and @by_lang.first
250 249 @best = @by_lang.first[1].clone
251 250 @by_lang.each do |lang,prop|
252 251 if @best[:runtime][:value] >= prop[:runtime][:value]
253 252 @best[:runtime] = prop[:runtime]
254 253 @best[:runtime][:lang] = lang
255 254 end
256 255 if @best[:memory][:value] >= prop[:memory][:value]
257 256 @best[:memory] = prop[:memory]
258 257 @best[:memory][:lang] = lang
259 258 end
260 259 if @best[:length][:value] >= prop[:length][:value]
261 260 @best[:length] = prop[:length]
262 261 @best[:length][:lang] = lang
263 262 end
264 263 if @best[:first][:value] >= prop[:first][:value]
265 264 @best[:first] = prop[:first]
266 265 @best[:first][:lang] = lang
267 266 end
268 267 end
269 268 end
270 269
271 270 @histogram[:summary][:max] = [@histogram[:data].max,1].max
272 271 @summary[:attempt] = user.count
273 272 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
274 273 end
275 274
276 275 def stuck #report struggling user,problem
277 276 # init
278 277 user,problem = nil
279 278 solve = true
280 279 tries = 0
281 280 @struggle = Array.new
282 281 record = {}
283 282 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
284 283 next unless sub.problem and sub.user
285 284 if user != sub.user_id or problem != sub.problem_id
286 285 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
287 286 record = {user: sub.user, problem: sub.problem}
288 287 user,problem = sub.user_id, sub.problem_id
289 288 solve = false
290 289 tries = 0
291 290 end
292 291 if sub.points >= sub.problem.full_score
293 292 solve = true
294 293 else
295 294 tries += 1
296 295 end
297 296 end
298 297 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
299 298 @struggle = @struggle[0..50]
300 299 end
301 300
302 301
303 302 def multiple_login
304 303 #user with multiple IP
305 304 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
306 305 last,count = 0,0
307 306 first = 0
308 307 @users = []
309 308 raw.each do |r|
310 309 if last != r.user.login
311 310 count = 1
312 311 last = r.user.login
313 312 first = r
314 313 else
315 314 @users << first if count == 1
316 315 @users << r
317 316 count += 1
318 317 end
319 318 end
320 319
321 320 #IP with multiple user
322 321 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
323 322 last,count = 0,0
324 323 first = 0
325 324 @ip = []
326 325 raw.each do |r|
327 326 if last != r.ip_address
328 327 count = 1
329 328 last = r.ip_address
330 329 first = r
331 330 else
332 331 @ip << first if count == 1
333 332 @ip << r
334 333 count += 1
335 334 end
336 335 end
337 336 end
338 337
339 338 def cheat_report
340 339 date_and_time = '%Y-%m-%d %H:%M'
341 340 begin
342 341 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
343 342 @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)
344 343 rescue
345 344 @since_time = Time.zone.now.ago( 90.minutes)
346 345 end
347 346 begin
348 347 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
349 348 @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)
350 349 rescue
351 350 @until_time = Time.zone.now
352 351 end
353 352
354 353 #multi login
355 354 @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")
356 355
357 356 st = <<-SQL
358 357 SELECT l2.*
359 358 FROM logins l2 INNER JOIN
360 359 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
361 360 FROM logins l
362 361 INNER JOIN users u ON l.user_id = u.id
363 362 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
364 363 GROUP BY u.id
365 364 HAVING count > 1
@@ -1,95 +1,95
1 1 %header.navbar.navbar-default.navbar-fixed-top
2 2 %nav
3 3 .container-fluid
4 4 .navbar-header
5 5 %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} }
6 6 %span.sr-only Togggle Navigation
7 7 %span.icon-bar
8 8 %span.icon-bar
9 9 %span.icon-bar
10 10 %a.navbar-brand{href: list_main_path}
11 11 %span.glyphicon.glyphicon-home
12 12 MAIN
13 13 .collapse.navbar-collapse#navbar-collapse
14 14 %ul.nav.navbar-nav
15 15 / submission
16 16 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
17 17 %li.dropdown
18 18 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
19 19 = "#{I18n.t 'menu.submissions'}"
20 20 %span.caret
21 21 %ul.dropdown-menu
22 22 = add_menu("View", 'submissions', 'index')
23 23 = add_menu("Self Test", 'test', 'index')
24 24 / hall of fame
25 25 - if GraderConfiguration['right.user_hall_of_fame']
26 26 = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
27 27 / display MODE button (with countdown in contest mode)
28 28 - if GraderConfiguration.analysis_mode?
29 29 %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE"
30 30 - elsif GraderConfiguration.time_limit_mode?
31 31 - if @current_user.contest_finished?
32 32 %div.navbar-btn.btn.btn-danger#countdown= "Contest is over"
33 33 - elsif !@current_user.contest_started?
34 34 %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
35 35 - else
36 36 %div.navbar-btn.btn.btn-primary#countdown asdf
37 37 :javascript
38 38 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
39 39 / admin section
40 40 - if (@current_user!=nil) and (session[:admin])
41 41 / management
42 42 %li.dropdown
43 43 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
44 44 Manage
45 45 %span.caret
46 46 %ul.dropdown-menu
47 47 = add_menu( 'Announcements', 'announcements', 'index')
48 48 = add_menu( 'Problems', 'problems', 'index')
49 49 = add_menu( 'Tags', 'tags', 'index')
50 50 = add_menu( 'Users', 'user_admin', 'index')
51 51 = add_menu( 'User Groups', 'groups', 'index')
52 52 = add_menu( 'Graders', 'graders', 'list')
53 53 = add_menu( 'Message ', 'messages', 'console')
54 54 %li.divider{role: 'separator'}
55 55 = add_menu( 'System config', 'configurations', 'index')
56 56 %li.divider{role: 'separator'}
57 57 = add_menu( 'Sites', 'sites', 'index')
58 58 = add_menu( 'Contests', 'contest_management', 'index')
59 59 / report
60 60 %li.dropdown
61 61 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
62 62 Report
63 63 %span.caret
64 64 %ul.dropdown-menu
65 65 = add_menu( 'Current Score', 'report', 'current_score')
66 66 = add_menu( 'Score Report', 'report', 'max_score')
67 - = add_menu( 'Report', 'report', 'multiple_login')
67 + = add_menu( 'Submission Report', 'report', 'submission')
68 68 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
69 69 =link_to "#{ungraded} backlogs!",
70 70 grader_list_path,
71 71 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
72 72
73 73 %ul.nav.navbar-nav.navbar-right
74 74 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
75 75 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'index', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
76 76 - if GraderConfiguration['system.user_setting_enabled']
77 77 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog', id: 'user_profile')}".html_safe, 'users', 'profile', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
78 78 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
79 79
80 80 /
81 81 - if (@current_user!=nil) and (session[:admin])
82 82 %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar
83 83 .container-fluid
84 84 .collapse.navbar-collapse
85 85 %ul.nav.navbar-nav
86 86 = add_menu( '[Announcements]', 'announcements', 'index')
87 87 = add_menu( '[Msg console]', 'messages', 'console')
88 88 = add_menu( '[Problems]', 'problems', 'index')
89 89 = add_menu( '[Users]', 'user_admin', 'index')
90 90 = add_menu( '[Results]', 'user_admin', 'user_stat')
91 91 = add_menu( '[Report]', 'report', 'multiple_login')
92 92 = add_menu( '[Graders]', 'graders', 'list')
93 93 = add_menu( '[Contests]', 'contest_management', 'index')
94 94 = add_menu( '[Sites]', 'sites', 'index')
95 95 = add_menu( '[System config]', 'configurations', 'index')
@@ -1,67 +1,77
1 1 - content_for :header do
2 2 = javascript_include_tag 'local_jquery'
3 3
4 4 %h1 Submissions detail
5 5
6 6 .row
7 7 .col-md-4
8 8 = render partial: 'shared/problem_select'
9 9 .col-md-4
10 - = render partial: 'shared/problem_select'
10 + = render partial: 'shared/date_filter'
11 11 .col-md-4
12 12 = render partial: 'shared/user_select'
13 13
14 + .row
15 + .col-md-6
16 + .alert.alert-info
17 + %ul
18 + %li Display a maximum of 100,000 entries to save computation power
19 + %li You have to click refresh when changing the filter above
14 20
15 21 .row
16 - .col-12
17 - %table.table.table-hover.table-condense.datatable
18 - -#
19 - %thead
20 - %tr
21 - %th User
22 - %th Problem
23 - %th Subbmission ID
24 - %th Submit Time
25 - %th Score
26 - %th detail
27 - %tbody
28 - - @submissions.each do |s|
29 - %tr
30 - %td= s.user.login
31 - %td= s.problem.long_name
32 - %td= s.id
33 - %td= s.submitted_at
34 - %td= s.points
35 - %td= s.grader_comment
22 + .col-sm-12
23 + %table.table.table-hover.table-condense.datatable{style: 'width: 100%'}
36 24
37 25
38 26 :javascript
39 - $('.datatable').DataTable({
40 - dom: 'Bfrtip',
41 - buttons: [
42 - 'copy', 'excel', 'pdf'
43 - ],
44 - columns: [
45 - {title: 'Sub ID', data: 'id'},
46 - {title: 'User', data: 'user.login'},
47 - {title: 'Problem', data: 'problem.long_name'},
48 - {title: 'Language', data: 'language.pretty_name'},
49 - {title: 'Submit at', data: 'submitted_at'},
50 - {title: 'Result', data: 'grader_comment'},
51 - {title: 'Score', data: 'points'},
52 - {title: 'IP', data: 'ip_address'},
53 - ],
54 - ajax: {
55 - url: '#{submission_query_report_path}',
56 - type: 'POST',
57 - data: (d) => {
58 - d.since_datetime = '1123'
59 - d.until_datetime = 'xxx'
60 - },
61 - dataType: 'json',
62 - beforeSend: (request) => {
63 - request.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
64 - },
65 - }, //end ajax
66 - 'pageLength': 50
27 + $(function() {
28 + submission_table = $('.datatable').DataTable({
29 + dom: "<'row'<'col-sm-3'B><'col-sm-3'l><'col-sm-6'f>>" + "<'row'<'col-sm-12'tr>>" + "<'row'<'col-sm-5'i><'col-sm-7'p>>",
30 + autoWidth: true,
31 + buttons: [
32 + {
33 + text: 'Refresh',
34 + action: (e,dt,node,config) => {
35 + submission_table.clear().draw()
36 + submission_table.ajax.reload( () => { submission_table.columns.adjust().draw() } )
37 + }
38 + },
39 + 'copy', 'excel'
40 + ],
41 + columns: [
42 + {title: 'Sub ID', data: 'id'},
43 + {title: 'User', data: 'user.login'},
44 + {title: 'Problem', data: 'problem.long_name'},
45 + {title: 'Language', data: 'language.pretty_name'},
46 + {title: 'Submit at', data: 'submitted_at'},
47 + {title: 'Result', data: 'grader_comment'},
48 + {title: 'Score', data: 'points'},
49 + {title: 'IP', data: 'ip_address'},
50 + ],
51 + ajax: {
52 + url: '#{submission_query_report_path}',
53 + type: 'POST',
54 + data: (d) => {
55 + d.since_datetime = $('#since_datetime').val()
56 + d.until_datetime = $('#until_datetime').val()
57 + d.users = $("input[name='users']:checked").val()
58 + d.problems = $("#problem_id").select2('val')
59 + d.groups = $("#group_id").select2('val')
60 + },
61 + dataType: 'json',
62 + beforeSend: (request) => {
63 + request.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
64 + },
65 + }, //end ajax
66 + 'pageLength': 50,
67 + processing: true,
68 + });
69 +
70 + $('.input-group.date').datetimepicker({
71 + format: 'YYYY-MM-DD',
72 + showTodayButton: true,
73 + locale: 'en',
74 + widgetPositioning: {horizontal: 'auto', vertical: 'bottom'},
75 + defaultDate: moment()
76 + });
67 77 });
@@ -1,18 +1,20
1 1 json.draw params['draw']&.to_i
2 2 json.recordsTotal @recordsTotal
3 3 json.recordsFiltered @recordsFiltered
4 4 json.data do
5 5 json.array! @submissions do |sub|
6 - json.extract! sub, :id, :points, :grader_comment, :ip_address
7 - json.submitted_at sub.submitted_at.to_s(:db)
6 + json.extract! sub, :grader_comment, :ip_address
7 + json.id "<a href='#{submission_path(sub)}'>#{sub.id}</a>"
8 + json.submitted_at sub.submitted_at.strftime('%Y-%m-%d %H:%M')
9 + json.points "#{sub.points}/#{sub.problem&.full_score}"
8 10 json.problem do
9 - json.long_name sub.problem&.long_name
11 + json.long_name sub.problem ? sub.problem.long_name : '-- deleted problem --'
10 12 end
11 13 json.user do
12 - json.login sub.user&.login
14 + json.login sub.user ? "<a href='#{stat_user_path(sub.user)}'>(#{sub.user&.login})</a> #{sub.user&.full_name}" : '-- deleted user --'
13 15 end
14 16 json.language do
15 17 json.pretty_name sub.language&.pretty_name
16 18 end
17 19 end
18 20 end
@@ -1,10 +1,10
1 1 .panel.panel-primary
2 2 .panel-heading
3 3 Problems
4 4 .panel-body
5 5 %p
6 - Select problem(s) that we wish to know the score.
6 + Select problem(s) to be included in the report
7 7 = label_tag :problem_id, "Problems"
8 8 = select_tag 'problem_id[]',
9 9 options_for_select(Problem.all.collect {|p| ["[#{p.name}] #{p.full_name}", p.id]},params[:problem_id]),
10 - { class: 'select2 form-control', multiple: "true" }
10 + { id: :problem_id, class: 'select2 form-control', multiple: "true" }
@@ -1,12 +1,19
1 1 .panel.panel-primary
2 2 .panel-heading
3 3 Users
4 4 .panel-body
5 5 .radio
6 6 %label
7 7 = radio_button_tag 'users', 'all', (params[:users] == "all")
8 8 All users
9 9 .radio
10 10 %label
11 11 = radio_button_tag 'users', 'enabled', (params[:users] == "enabled")
12 12 Only enabled users
13 + .radio
14 + %label
15 + = radio_button_tag 'users', 'group', (params[:users] == "group")
16 + Only these groups
17 + = select_tag 'group_id[]',
18 + options_for_select(Group.all.collect {|g| ["[#{g.name}] #{g.description}", g.id]}),
19 + { id: 'group_id', class: 'select2 form-control', multiple: "true"}
@@ -1,321 +1,308
1 - # encoding: UTF-8
2 1 # This file is auto-generated from the current state of the database. Instead
3 2 # of editing this file, please use the migrations feature of Active Record to
4 3 # incrementally modify your database, and then regenerate this schema definition.
5 4 #
6 5 # Note that this schema.rb definition is the authoritative source for your
7 6 # database schema. If you need to create the application database on another
8 7 # system, you should be using db:schema:load, not running all the migrations
9 8 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 9 # you'll amass, the slower it'll run and the greater likelihood for issues).
11 10 #
12 11 # It's strongly recommended that you check this file into your version control system.
13 12
14 - ActiveRecord::Schema.define(version: 20180612102327) do
13 + ActiveRecord::Schema.define(version: 2020_04_04_142959) do
15 14
16 - create_table "announcements", force: :cascade do |t|
17 - t.string "author", limit: 255
18 - t.text "body", limit: 65535
19 - t.boolean "published"
20 - t.datetime "created_at"
21 - t.datetime "updated_at"
22 - t.boolean "frontpage", default: false
23 - t.boolean "contest_only", default: false
24 - t.string "title", limit: 255
25 - t.string "notes", limit: 255
15 + create_table "announcements", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
16 + t.string "author"
17 + t.text "body", limit: 16777215
18 + t.boolean "published"
19 + t.datetime "created_at", null: false
20 + t.datetime "updated_at", null: false
21 + t.boolean "frontpage", default: false
22 + t.boolean "contest_only", default: false
23 + t.string "title"
24 + t.string "notes"
26 25 end
27 26
28 - create_table "contests", force: :cascade do |t|
29 - t.string "title", limit: 255
30 - t.boolean "enabled"
31 - t.datetime "created_at"
32 - t.datetime "updated_at"
33 - t.string "name", limit: 255
34 - end
35 -
36 - create_table "contests_problems", id: false, force: :cascade do |t|
37 - t.integer "contest_id", limit: 4
38 - t.integer "problem_id", limit: 4
39 - end
40 -
41 - create_table "contests_users", id: false, force: :cascade do |t|
42 - t.integer "contest_id", limit: 4
43 - t.integer "user_id", limit: 4
27 + create_table "contests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
28 + t.string "title"
29 + t.boolean "enabled"
30 + t.datetime "created_at", null: false
31 + t.datetime "updated_at", null: false
32 + t.string "name"
44 33 end
45 34
46 - create_table "countries", force: :cascade do |t|
47 - t.string "name", limit: 255
48 - t.datetime "created_at"
49 - t.datetime "updated_at"
35 + create_table "contests_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
36 + t.integer "contest_id"
37 + t.integer "problem_id"
50 38 end
51 39
52 - create_table "descriptions", force: :cascade do |t|
53 - t.text "body", limit: 65535
54 - t.boolean "markdowned"
55 - t.datetime "created_at"
56 - t.datetime "updated_at"
40 + create_table "contests_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
41 + t.integer "contest_id"
42 + t.integer "user_id"
57 43 end
58 44
59 - create_table "grader_configurations", force: :cascade do |t|
60 - t.string "key", limit: 255
61 - t.string "value_type", limit: 255
62 - t.string "value", limit: 255
63 - t.datetime "created_at"
64 - t.datetime "updated_at"
65 - t.text "description", limit: 65535
45 + create_table "countries", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
46 + t.string "name"
47 + t.datetime "created_at", null: false
48 + t.datetime "updated_at", null: false
66 49 end
67 50
68 - create_table "grader_processes", force: :cascade do |t|
69 - t.string "host", limit: 255
70 - t.integer "pid", limit: 4
71 - t.string "mode", limit: 255
72 - t.boolean "active"
73 - t.datetime "created_at"
74 - t.datetime "updated_at"
75 - t.integer "task_id", limit: 4
76 - t.string "task_type", limit: 255
77 - t.boolean "terminated"
51 + create_table "descriptions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
52 + t.text "body", limit: 16777215
53 + t.boolean "markdowned"
54 + t.datetime "created_at", null: false
55 + t.datetime "updated_at", null: false
56 + end
57 +
58 + create_table "grader_configurations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
59 + t.string "key"
60 + t.string "value_type"
61 + t.string "value"
62 + t.datetime "created_at", null: false
63 + t.datetime "updated_at", null: false
64 + t.text "description", limit: 16777215
78 65 end
79 66
80 - add_index "grader_processes", ["host", "pid"], name: "index_grader_processes_on_host_and_pid", using: :btree
81 -
82 - create_table "groups", force: :cascade do |t|
83 - t.string "name", limit: 255
84 - t.string "description", limit: 255
85 - end
86 -
87 - create_table "groups_problems", id: false, force: :cascade do |t|
88 - t.integer "problem_id", limit: 4, null: false
89 - t.integer "group_id", limit: 4, null: false
67 + create_table "grader_processes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
68 + t.string "host"
69 + t.integer "pid"
70 + t.string "mode"
71 + t.boolean "active"
72 + t.datetime "created_at", null: false
73 + t.datetime "updated_at", null: false
74 + t.integer "task_id"
75 + t.string "task_type"
76 + t.boolean "terminated"
77 + t.index ["host", "pid"], name: "index_grader_processes_on_ip_and_pid"
90 78 end
91 79
92 - add_index "groups_problems", ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id", using: :btree
93 -
94 - create_table "groups_users", id: false, force: :cascade do |t|
95 - t.integer "group_id", limit: 4, null: false
96 - t.integer "user_id", limit: 4, null: false
80 + create_table "groups", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
81 + t.string "name"
82 + t.string "description"
97 83 end
98 84
99 - add_index "groups_users", ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id", using: :btree
85 + create_table "groups_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
86 + t.integer "problem_id", null: false
87 + t.integer "group_id", null: false
88 + t.index ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id"
89 + end
100 90
101 - create_table "heart_beats", force: :cascade do |t|
102 - t.integer "user_id", limit: 4
103 - t.string "ip_address", limit: 255
104 - t.datetime "created_at"
105 - t.datetime "updated_at"
106 - t.string "status", limit: 255
91 + create_table "groups_users", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
92 + t.integer "group_id", null: false
93 + t.integer "user_id", null: false
94 + t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
107 95 end
108 96
109 - add_index "heart_beats", ["updated_at"], name: "index_heart_beats_on_updated_at", using: :btree
110 -
111 - create_table "languages", force: :cascade do |t|
112 - t.string "name", limit: 10
113 - t.string "pretty_name", limit: 255
114 - t.string "ext", limit: 10
115 - t.string "common_ext", limit: 255
97 + create_table "heart_beats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
98 + t.integer "user_id"
99 + t.string "ip_address"
100 + t.datetime "created_at", null: false
101 + t.datetime "updated_at", null: false
102 + t.string "status"
103 + t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
116 104 end
117 105
118 - create_table "logins", force: :cascade do |t|
119 - t.integer "user_id", limit: 4
120 - t.string "ip_address", limit: 255
121 - t.datetime "created_at"
122 - t.datetime "updated_at"
106 + create_table "languages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
107 + t.string "name", limit: 10
108 + t.string "pretty_name"
109 + t.string "ext", limit: 10
110 + t.string "common_ext"
123 111 end
124 112
125 - create_table "messages", force: :cascade do |t|
126 - t.integer "sender_id", limit: 4
127 - t.integer "receiver_id", limit: 4
128 - t.integer "replying_message_id", limit: 4
129 - t.text "body", limit: 65535
130 - t.boolean "replied"
131 - t.datetime "created_at"
132 - t.datetime "updated_at"
113 + create_table "logins", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
114 + t.integer "user_id"
115 + t.string "ip_address"
116 + t.datetime "created_at", null: false
117 + t.datetime "updated_at", null: false
133 118 end
134 119
135 - create_table "problems", force: :cascade do |t|
136 - t.string "name", limit: 30
137 - t.string "full_name", limit: 255
138 - t.integer "full_score", limit: 4
139 - t.date "date_added"
120 + create_table "messages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
121 + t.integer "sender_id"
122 + t.integer "receiver_id"
123 + t.integer "replying_message_id"
124 + t.text "body", limit: 16777215
125 + t.boolean "replied"
126 + t.datetime "created_at", null: false
127 + t.datetime "updated_at", null: false
128 + end
129 +
130 + create_table "problems", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
131 + t.string "name", limit: 100
132 + t.string "full_name"
133 + t.integer "full_score"
134 + t.date "date_added"
140 135 t.boolean "available"
141 - t.string "url", limit: 255
142 - t.integer "description_id", limit: 4
136 + t.string "url"
137 + t.integer "description_id"
143 138 t.boolean "test_allowed"
144 139 t.boolean "output_only"
145 - t.string "description_filename", limit: 255
140 + t.string "description_filename"
146 141 t.boolean "view_testcase"
147 142 end
148 143
149 - create_table "problems_tags", force: :cascade do |t|
150 - t.integer "problem_id", limit: 4
151 - t.integer "tag_id", limit: 4
144 + create_table "problems_tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
145 + t.integer "problem_id"
146 + t.integer "tag_id"
147 + t.index ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true
148 + t.index ["problem_id"], name: "index_problems_tags_on_problem_id"
149 + t.index ["tag_id"], name: "index_problems_tags_on_tag_id"
152 150 end
153 151
154 - add_index "problems_tags", ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true, using: :btree
155 - add_index "problems_tags", ["problem_id"], name: "index_problems_tags_on_problem_id", using: :btree
156 - add_index "problems_tags", ["tag_id"], name: "index_problems_tags_on_tag_id", using: :btree
157 -
158 - create_table "rights", force: :cascade do |t|
159 - t.string "name", limit: 255
160 - t.string "controller", limit: 255
161 - t.string "action", limit: 255
152 + create_table "rights", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
153 + t.string "name"
154 + t.string "controller"
155 + t.string "action"
162 156 end
163 157
164 - create_table "rights_roles", id: false, force: :cascade do |t|
165 - t.integer "right_id", limit: 4
166 - t.integer "role_id", limit: 4
158 + create_table "rights_roles", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
159 + t.integer "right_id"
160 + t.integer "role_id"
161 + t.index ["role_id"], name: "index_rights_roles_on_role_id"
167 162 end
168 163
169 - add_index "rights_roles", ["role_id"], name: "index_rights_roles_on_role_id", using: :btree
170 -
171 - create_table "roles", force: :cascade do |t|
172 - t.string "name", limit: 255
164 + create_table "roles", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
165 + t.string "name"
173 166 end
174 167
175 - create_table "roles_users", id: false, force: :cascade do |t|
176 - t.integer "role_id", limit: 4
177 - t.integer "user_id", limit: 4
168 + create_table "roles_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
169 + t.integer "role_id"
170 + t.integer "user_id"
171 + t.index ["user_id"], name: "index_roles_users_on_user_id"
178 172 end
179 173
180 - add_index "roles_users", ["user_id"], name: "index_roles_users_on_user_id", using: :btree
181 -
182 - create_table "sessions", force: :cascade do |t|
183 - t.string "session_id", limit: 255
184 - t.text "data", limit: 65535
174 + create_table "sessions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
175 + t.string "session_id"
176 + t.text "data", limit: 16777215
185 177 t.datetime "updated_at"
178 + t.index ["session_id"], name: "index_sessions_on_session_id"
179 + t.index ["updated_at"], name: "index_sessions_on_updated_at"
186 180 end
187 181
188 - add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", using: :btree
189 - add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at", using: :btree
182 + create_table "sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
183 + t.string "name"
184 + t.boolean "started"
185 + t.datetime "start_time"
186 + t.datetime "created_at", null: false
187 + t.datetime "updated_at", null: false
188 + t.integer "country_id"
189 + t.string "password"
190 + end
190 191
191 - create_table "sites", force: :cascade do |t|
192 - t.string "name", limit: 255
193 - t.boolean "started"
194 - t.datetime "start_time"
195 - t.datetime "created_at"
196 - t.datetime "updated_at"
197 - t.integer "country_id", limit: 4
198 - t.string "password", limit: 255
192 + create_table "submission_view_logs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
193 + t.integer "user_id"
194 + t.integer "submission_id"
195 + t.datetime "created_at", null: false
196 + t.datetime "updated_at", null: false
199 197 end
200 198
201 - create_table "submission_view_logs", force: :cascade do |t|
202 - t.integer "user_id", limit: 4
203 - t.integer "submission_id", limit: 4
204 - t.datetime "created_at"
205 - t.datetime "updated_at"
206 - end
207 -
208 - create_table "submissions", force: :cascade do |t|
209 - t.integer "user_id", limit: 4
210 - t.integer "problem_id", limit: 4
211 - t.integer "language_id", limit: 4
212 - t.text "source", limit: 16777215
213 - t.binary "binary", limit: 65535
199 + create_table "submissions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
200 + t.integer "user_id"
201 + t.integer "problem_id"
202 + t.integer "language_id"
203 + t.text "source", limit: 16777215
204 + t.binary "binary"
214 205 t.datetime "submitted_at"
215 206 t.datetime "compiled_at"
216 - t.text "compiler_message", limit: 65535
207 + t.text "compiler_message", limit: 16777215
217 208 t.datetime "graded_at"
218 - t.integer "points", limit: 4
219 - t.text "grader_comment", limit: 65535
220 - t.integer "number", limit: 4
221 - t.string "source_filename", limit: 255
222 - t.float "max_runtime", limit: 24
223 - t.integer "peak_memory", limit: 4
224 - t.integer "effective_code_length", limit: 4
225 - t.string "ip_address", limit: 255
209 + t.integer "points"
210 + t.text "grader_comment", limit: 16777215
211 + t.integer "number"
212 + t.string "source_filename"
213 + t.float "max_runtime"
214 + t.integer "peak_memory"
215 + t.integer "effective_code_length"
216 + t.string "ip_address"
217 + t.index ["submitted_at"], name: "index_submissions_on_submitted_at"
218 + t.index ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true
219 + t.index ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id"
226 220 end
227 221
228 - add_index "submissions", ["user_id", "problem_id", "number"], name: "index_submissions_on_user_id_and_problem_id_and_number", unique: true, using: :btree
229 - add_index "submissions", ["user_id", "problem_id"], name: "index_submissions_on_user_id_and_problem_id", using: :btree
230 -
231 - create_table "tags", force: :cascade do |t|
232 - t.string "name", limit: 255, null: false
233 - t.text "description", limit: 65535
234 - t.boolean "public"
235 - t.datetime "created_at", null: false
236 - t.datetime "updated_at", null: false
222 + create_table "tags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
223 + t.string "name", null: false
224 + t.text "description"
225 + t.boolean "public"
226 + t.datetime "created_at", null: false
227 + t.datetime "updated_at", null: false
237 228 end
238 229
239 - create_table "tasks", force: :cascade do |t|
240 - t.integer "submission_id", limit: 4
230 + create_table "tasks", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
231 + t.integer "submission_id"
241 232 t.datetime "created_at"
242 - t.integer "status", limit: 4
233 + t.integer "status"
243 234 t.datetime "updated_at"
235 + t.index ["submission_id"], name: "index_tasks_on_submission_id"
244 236 end
245 237
246 - add_index "tasks", ["submission_id"], name: "index_tasks_on_submission_id", using: :btree
247 -
248 - create_table "test_pairs", force: :cascade do |t|
249 - t.integer "problem_id", limit: 4
250 - t.text "input", limit: 16777215
251 - t.text "solution", limit: 16777215
252 - t.datetime "created_at"
253 - t.datetime "updated_at"
238 + create_table "test_pairs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
239 + t.integer "problem_id"
240 + t.text "input", limit: 4294967295
241 + t.text "solution", limit: 4294967295
242 + t.datetime "created_at", null: false
243 + t.datetime "updated_at", null: false
254 244 end
255 245
256 - create_table "test_requests", force: :cascade do |t|
257 - t.integer "user_id", limit: 4
258 - t.integer "problem_id", limit: 4
259 - t.integer "submission_id", limit: 4
260 - t.string "input_file_name", limit: 255
261 - t.string "output_file_name", limit: 255
262 - t.string "running_stat", limit: 255
263 - t.integer "status", limit: 4
264 - t.datetime "updated_at"
246 + create_table "test_requests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
247 + t.integer "user_id"
248 + t.integer "problem_id"
249 + t.integer "submission_id"
250 + t.string "input_file_name"
251 + t.string "output_file_name"
252 + t.string "running_stat"
253 + t.integer "status"
254 + t.datetime "updated_at", null: false
265 255 t.datetime "submitted_at"
266 256 t.datetime "compiled_at"
267 - t.text "compiler_message", limit: 65535
257 + t.text "compiler_message", limit: 16777215
268 258 t.datetime "graded_at"
269 - t.string "grader_comment", limit: 255
270 - t.datetime "created_at"
271 - t.float "running_time", limit: 24
272 - t.string "exit_status", limit: 255
273 - t.integer "memory_usage", limit: 4
259 + t.string "grader_comment"
260 + t.datetime "created_at", null: false
261 + t.float "running_time"
262 + t.string "exit_status"
263 + t.integer "memory_usage"
264 + t.index ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id"
274 265 end
275 266
276 - add_index "test_requests", ["user_id", "problem_id"], name: "index_test_requests_on_user_id_and_problem_id", using: :btree
277 -
278 - create_table "testcases", force: :cascade do |t|
279 - t.integer "problem_id", limit: 4
280 - t.integer "num", limit: 4
281 - t.integer "group", limit: 4
282 - t.integer "score", limit: 4
283 - t.text "input", limit: 4294967295
284 - t.text "sol", limit: 4294967295
285 - t.datetime "created_at"
286 - t.datetime "updated_at"
267 + create_table "testcases", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
268 + t.integer "problem_id"
269 + t.integer "num"
270 + t.integer "group"
271 + t.integer "score"
272 + t.text "input", limit: 4294967295
273 + t.text "sol", limit: 4294967295
274 + t.datetime "created_at", null: false
275 + t.datetime "updated_at", null: false
276 + t.index ["problem_id"], name: "index_testcases_on_problem_id"
287 277 end
288 278
289 - add_index "testcases", ["problem_id"], name: "index_testcases_on_problem_id", using: :btree
290 -
291 - create_table "user_contest_stats", force: :cascade do |t|
292 - t.integer "user_id", limit: 4
279 + create_table "user_contest_stats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
280 + t.integer "user_id"
293 281 t.datetime "started_at"
294 - t.datetime "created_at"
295 - t.datetime "updated_at"
296 - t.boolean "forced_logout"
282 + t.datetime "created_at", null: false
283 + t.datetime "updated_at", null: false
284 + t.boolean "forced_logout"
297 285 end
298 286
299 - create_table "users", force: :cascade do |t|
300 - t.string "login", limit: 50
301 - t.string "full_name", limit: 255
302 - t.string "hashed_password", limit: 255
303 - t.string "salt", limit: 5
304 - t.string "alias", limit: 255
305 - t.string "email", limit: 255
306 - t.integer "site_id", limit: 4
307 - t.integer "country_id", limit: 4
308 - t.boolean "activated", default: false
287 + create_table "users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
288 + t.string "login", limit: 50
289 + t.string "full_name"
290 + t.string "hashed_password"
291 + t.string "salt", limit: 5
292 + t.string "alias"
293 + t.string "email"
294 + t.integer "site_id"
295 + t.integer "country_id"
296 + t.boolean "activated", default: false
309 297 t.datetime "created_at"
310 298 t.datetime "updated_at"
311 - t.string "section", limit: 255
312 - t.boolean "enabled", default: true
313 - t.string "remark", limit: 255
314 - t.string "last_ip", limit: 255
299 + t.string "section"
300 + t.boolean "enabled", default: true
301 + t.string "remark"
302 + t.string "last_ip"
303 + t.index ["login"], name: "index_users_on_login", unique: true
315 304 end
316 305
317 - add_index "users", ["login"], name: "index_users_on_login", unique: true, using: :btree
318 -
319 306 add_foreign_key "problems_tags", "problems"
320 307 add_foreign_key "problems_tags", "tags"
321 308 end
You need to be logged in to leave comments. Login now