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

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

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