Description:
merge with master
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
r665:ecbd1a5dbc27 - - 7 files changed: 108 inserted, 8 deleted
@@ -0,0 +1,34 | |||
|
1 | + .container-fluid | |
|
2 | + %h1 Editing announcement | |
|
3 | + = error_messages_for :announcement | |
|
4 | + .row | |
|
5 | + .col-md-6 | |
|
6 | + = form_for(@announcement) do |f| | |
|
7 | + .form-group | |
|
8 | + %label Title | |
|
9 | + = f.text_field :title, class: 'form-control' | |
|
10 | + .form-group | |
|
11 | + %label Notes | |
|
12 | + (shown internally, used to organize announcements) | |
|
13 | + = f.text_field :notes, class: 'form-control' | |
|
14 | + .form-group | |
|
15 | + %label Body | |
|
16 | + = f.text_area :body, class: 'form-control', style: 'height: 200px;' | |
|
17 | + .form-group | |
|
18 | + %label Author | |
|
19 | + = f.text_field :author, class: 'form-control' | |
|
20 | + .checkbox | |
|
21 | + %label | |
|
22 | + = f.check_box :published | |
|
23 | + Published | |
|
24 | + .checkbox | |
|
25 | + %label | |
|
26 | + = f.check_box :frontpage | |
|
27 | + Show on front page? | |
|
28 | + .checkbox | |
|
29 | + %label | |
|
30 | + = f.check_box :contest_only | |
|
31 | + Show only in contest? | |
|
32 | + = f.submit "Update", class: 'btn btn-primary' | |
|
33 | + = link_to 'Show', @announcement, class: 'btn btn-default' | |
|
34 | + = link_to 'Back', announcements_path, class: 'btn btn-default' |
@@ -0,0 +1,52 | |||
|
1 | + = error_messages_for 'problem' | |
|
2 | + / [form:problem] | |
|
3 | + .form-group | |
|
4 | + %label{:for => "problem_name"} Name | |
|
5 | + = text_field 'problem', 'name', class: 'form-control' | |
|
6 | + %small | |
|
7 | + Do not directly edit the problem name, unless you know what you are doing. If you want to change the name, use the name change button in the problem management menu instead. | |
|
8 | + .form-group | |
|
9 | + %label{:for => "problem_full_name"} Full name | |
|
10 | + = text_field 'problem', 'full_name', class: 'form-control' | |
|
11 | + .form-group | |
|
12 | + %label{:for => "problem_full_score"} Full score | |
|
13 | + = text_field 'problem', 'full_score', class: 'form-control' | |
|
14 | + .form-group | |
|
15 | + %label{:for => "problem_date_added"} Date added | |
|
16 | + = date_select 'problem', 'date_added', class: 'form-control' | |
|
17 | + - # TODO: these should be put in model Problem, but I can't think of | |
|
18 | + - # nice default values for them. These values look fine only | |
|
19 | + - # in this case (of lazily adding new problems). | |
|
20 | + - @problem.available = true if @problem!=nil and @problem.available==nil | |
|
21 | + - @problem.test_allowed = true if @problem!=nil and @problem.test_allowed==nil | |
|
22 | + - @problem.output_only = false if @problem!=nil and @problem.output_only==nil | |
|
23 | + .checkbox | |
|
24 | + %label{:for => "problem_available"} | |
|
25 | + = check_box :problem, :available | |
|
26 | + Available? | |
|
27 | + .checkbox | |
|
28 | + %label{:for => "problem_test_allowed"} | |
|
29 | + = check_box :problem, :test_allowed | |
|
30 | + Test allowed? | |
|
31 | + .checkbox | |
|
32 | + %label{:for => "problem_output_only"} | |
|
33 | + = check_box :problem, :output_only | |
|
34 | + Output only? | |
|
35 | + = error_messages_for 'description' | |
|
36 | + .form-group | |
|
37 | + %label{:for => "description_body"} Description | |
|
38 | + %br/ | |
|
39 | + = text_area :description, :body, :rows => 10, :cols => 80,class: 'form-control' | |
|
40 | + .form-group | |
|
41 | + %label{:for => "description_markdowned"} Markdowned? | |
|
42 | + = select "description", | | |
|
43 | + "markdowned", | | |
|
44 | + [['True',true],['False',false]], | | |
|
45 | + {:selected => (@description) ? @description.markdowned : false } | | |
|
46 | + .form-group | |
|
47 | + %label{:for => "problem_url"} URL | |
|
48 | + %br/ | |
|
49 | + = text_field 'problem', 'url',class: 'form-control' | |
|
50 | + %p | |
|
51 | + Task PDF #{file_field_tag 'file'} | |
|
52 | + / [eoform:problem] |
@@ -0,0 +1,14 | |||
|
1 | + .container-fluid | |
|
2 | + = form_for @problem,url:{action: 'update'},html: {multipart: true} do | |
|
3 | + .row | |
|
4 | + .col-md-6 | |
|
5 | + %h1 Editing problem | |
|
6 | + = render :partial => 'form' | |
|
7 | + .row | |
|
8 | + .col-md-4 | |
|
9 | + = submit_tag 'Edit', class: 'btn btn-primary btn-block' | |
|
10 | + .col-md-4 | |
|
11 | + = link_to 'Show', {:action => 'show', :id => @problem}, class: 'btn btn-default btn-block' | |
|
12 | + .col-md-4 | |
|
13 | + = link_to 'Back', problems_path, class: 'btn btn-default btn-block' | |
|
14 | + .div{style: 'height: 5em'} |
@@ -0,0 +1,5 | |||
|
1 | + require 'spec_helper' | |
|
2 | + | |
|
3 | + describe Testcases do | |
|
4 | + pending "add some examples to (or delete) #{__FILE__}" | |
|
5 | + end |
@@ -36,385 +36,385 | |||
|
36 | 36 | @problems = [] |
|
37 | 37 | if params[:problem_id] |
|
38 | 38 | params[:problem_id].each do |id| |
|
39 | 39 | next unless id.strip != "" |
|
40 | 40 | pid = Problem.find_by_id(id.to_i) |
|
41 | 41 | @problems << pid if pid |
|
42 | 42 | end |
|
43 | 43 | end |
|
44 | 44 | |
|
45 | 45 | #users |
|
46 | 46 | @users = if params[:user] == "all" then |
|
47 | 47 | User.includes(:contests).includes(:contest_stat) |
|
48 | 48 | else |
|
49 | 49 | User.includes(:contests).includes(:contest_stat).where(enabled: true) |
|
50 | 50 | end |
|
51 | 51 | |
|
52 | 52 | #set up range from param |
|
53 | 53 | @since_id = params.fetch(:from_id, 0).to_i |
|
54 | 54 | @until_id = params.fetch(:to_id, 0).to_i |
|
55 | 55 | |
|
56 | 56 | #calculate the routine |
|
57 | 57 | @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id) |
|
58 | 58 | |
|
59 | 59 | #rencer accordingly |
|
60 | 60 | if params[:button] == 'download' then |
|
61 | 61 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
62 | 62 | send_data csv, filename: 'max_score.csv' |
|
63 | 63 | else |
|
64 | 64 | #render template: 'user_admin/user_stat' |
|
65 | 65 | render 'max_score' |
|
66 | 66 | end |
|
67 | 67 | |
|
68 | 68 | end |
|
69 | 69 | |
|
70 | 70 | def score |
|
71 | 71 | if params[:commit] == 'download csv' |
|
72 | 72 | @problems = Problem.all |
|
73 | 73 | else |
|
74 | 74 | @problems = Problem.available_problems |
|
75 | 75 | end |
|
76 | 76 | @users = User.includes(:contests, :contest_stat).where(enabled: true) |
|
77 | 77 | @scorearray = Array.new |
|
78 | 78 | @users.each do |u| |
|
79 | 79 | ustat = Array.new |
|
80 | 80 | ustat[0] = u |
|
81 | 81 | @problems.each do |p| |
|
82 | 82 | sub = Submission.find_last_by_user_and_problem(u.id,p.id) |
|
83 | 83 | if (sub!=nil) and (sub.points!=nil) and p and p.full_score |
|
84 | 84 | ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)] |
|
85 | 85 | else |
|
86 | 86 | ustat << [0,false] |
|
87 | 87 | end |
|
88 | 88 | end |
|
89 | 89 | @scorearray << ustat |
|
90 | 90 | end |
|
91 | 91 | if params[:commit] == 'download csv' then |
|
92 | 92 | csv = gen_csv_from_scorearray(@scorearray,@problems) |
|
93 | 93 | send_data csv, filename: 'last_score.csv' |
|
94 | 94 | else |
|
95 | 95 | render template: 'user_admin/user_stat' |
|
96 | 96 | end |
|
97 | 97 | |
|
98 | 98 | end |
|
99 | 99 | |
|
100 | 100 | def login_stat |
|
101 | 101 | @logins = Array.new |
|
102 | 102 | |
|
103 | 103 | date_and_time = '%Y-%m-%d %H:%M' |
|
104 | 104 | begin |
|
105 | 105 | md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/) |
|
106 | 106 | @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) |
|
107 | 107 | rescue |
|
108 | 108 | @since_time = DateTime.new(1000,1,1) |
|
109 | 109 | end |
|
110 | 110 | begin |
|
111 | 111 | md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/) |
|
112 | 112 | @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) |
|
113 | 113 | rescue |
|
114 | 114 | @until_time = DateTime.new(3000,1,1) |
|
115 | 115 | end |
|
116 | 116 | |
|
117 | 117 | User.all.each do |user| |
|
118 | 118 | @logins << { id: user.id, |
|
119 | 119 | login: user.login, |
|
120 | 120 | full_name: user.full_name, |
|
121 | 121 | count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?", |
|
122 | 122 | user.id,@since_time,@until_time) |
|
123 | 123 | .count(:id), |
|
124 | 124 | min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?", |
|
125 | 125 | user.id,@since_time,@until_time) |
|
126 | 126 | .minimum(:created_at), |
|
127 | 127 | max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?", |
|
128 | 128 | user.id,@since_time,@until_time) |
|
129 | 129 | .maximum(:created_at), |
|
130 | 130 | ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?", |
|
131 | 131 | user.id,@since_time,@until_time) |
|
132 | 132 | .select(:ip_address).uniq |
|
133 | 133 | |
|
134 | 134 | } |
|
135 | 135 | end |
|
136 | 136 | end |
|
137 | 137 | |
|
138 | 138 | def submission_stat |
|
139 | 139 | |
|
140 | 140 | date_and_time = '%Y-%m-%d %H:%M' |
|
141 | 141 | begin |
|
142 | 142 | @since_time = DateTime.strptime(params[:since_datetime],date_and_time) |
|
143 | 143 | rescue |
|
144 | 144 | @since_time = DateTime.new(1000,1,1) |
|
145 | 145 | end |
|
146 | 146 | begin |
|
147 | 147 | @until_time = DateTime.strptime(params[:until_datetime],date_and_time) |
|
148 | 148 | rescue |
|
149 | 149 | @until_time = DateTime.new(3000,1,1) |
|
150 | 150 | end |
|
151 | 151 | |
|
152 | 152 | @submissions = {} |
|
153 | 153 | |
|
154 | 154 | User.find_each do |user| |
|
155 | 155 | @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } } |
|
156 | 156 | end |
|
157 | 157 | |
|
158 | 158 | Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s| |
|
159 | 159 | if @submissions[s.user_id] |
|
160 | 160 | if not @submissions[s.user_id][:sub].has_key?(s.problem_id) |
|
161 | 161 | a = Problem.find_by_id(s.problem_id) |
|
162 | 162 | @submissions[s.user_id][:sub][s.problem_id] = |
|
163 | 163 | { prob_name: (a ? a.full_name : '(NULL)'), |
|
164 | 164 | sub_ids: [s.id] } |
|
165 | 165 | else |
|
166 | 166 | @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id |
|
167 | 167 | end |
|
168 | 168 | @submissions[s.user_id][:count] += 1 |
|
169 | 169 | end |
|
170 | 170 | end |
|
171 | 171 | end |
|
172 | 172 | |
|
173 | 173 | def problem_hof |
|
174 | 174 | # gen problem list |
|
175 | 175 | @user = User.find(session[:user_id]) |
|
176 | 176 | @problems = @user.available_problems |
|
177 | 177 | |
|
178 | 178 | # get selected problems or the default |
|
179 | 179 | if params[:id] |
|
180 | 180 | begin |
|
181 | 181 | @problem = Problem.available.find(params[:id]) |
|
182 | 182 | rescue |
|
183 | 183 | redirect_to action: :problem_hof |
|
184 | 184 | flash[:notice] = 'Error: submissions for that problem are not viewable.' |
|
185 | 185 | return |
|
186 | 186 | end |
|
187 | 187 | end |
|
188 | 188 | |
|
189 | 189 | return unless @problem |
|
190 | 190 | |
|
191 | 191 | @by_lang = {} #aggregrate by language |
|
192 | 192 | |
|
193 | 193 | range =65 |
|
194 | 194 | @histogram = { data: Array.new(range,0), summary: {} } |
|
195 | 195 | @summary = {count: 0, solve: 0, attempt: 0} |
|
196 | 196 | user = Hash.new(0) |
|
197 | 197 | Submission.where(problem_id: @problem.id).find_each do |sub| |
|
198 | 198 | #histogram |
|
199 | 199 | d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60 |
|
200 | 200 | @histogram[:data][d.to_i] += 1 if d < range |
|
201 | 201 | |
|
202 | 202 | next unless sub.points |
|
203 | 203 | @summary[:count] += 1 |
|
204 | 204 | user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max |
|
205 | 205 | |
|
206 | 206 | lang = Language.find_by_id(sub.language_id) |
|
207 | 207 | next unless lang |
|
208 | 208 | next unless sub.points >= @problem.full_score |
|
209 | 209 | |
|
210 | 210 | #initialize |
|
211 | 211 | unless @by_lang.has_key?(lang.pretty_name) |
|
212 | 212 | @by_lang[lang.pretty_name] = { |
|
213 | 213 | runtime: { avail: false, value: 2**30-1 }, |
|
214 | 214 | memory: { avail: false, value: 2**30-1 }, |
|
215 | 215 | length: { avail: false, value: 2**30-1 }, |
|
216 | 216 | first: { avail: false, value: DateTime.new(3000,1,1) } |
|
217 | 217 | } |
|
218 | 218 | end |
|
219 | 219 | |
|
220 | 220 | if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value] |
|
221 | 221 | @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id } |
|
222 | 222 | end |
|
223 | 223 | |
|
224 | 224 | if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value] |
|
225 | 225 | @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id } |
|
226 | 226 | end |
|
227 | 227 | |
|
228 | - if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and | |
|
228 | + if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and sub.user and | |
|
229 | 229 | !sub.user.admin? |
|
230 | 230 | @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id } |
|
231 | 231 | end |
|
232 | 232 | |
|
233 | 233 | if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length |
|
234 | 234 | @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id } |
|
235 | 235 | end |
|
236 | 236 | end |
|
237 | 237 | |
|
238 | 238 | #process user_id |
|
239 | 239 | @by_lang.each do |lang,prop| |
|
240 | 240 | prop.each do |k,v| |
|
241 | 241 | v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)" |
|
242 | 242 | end |
|
243 | 243 | end |
|
244 | 244 | |
|
245 | 245 | #sum into best |
|
246 | 246 | if @by_lang and @by_lang.first |
|
247 | 247 | @best = @by_lang.first[1].clone |
|
248 | 248 | @by_lang.each do |lang,prop| |
|
249 | 249 | if @best[:runtime][:value] >= prop[:runtime][:value] |
|
250 | 250 | @best[:runtime] = prop[:runtime] |
|
251 | 251 | @best[:runtime][:lang] = lang |
|
252 | 252 | end |
|
253 | 253 | if @best[:memory][:value] >= prop[:memory][:value] |
|
254 | 254 | @best[:memory] = prop[:memory] |
|
255 | 255 | @best[:memory][:lang] = lang |
|
256 | 256 | end |
|
257 | 257 | if @best[:length][:value] >= prop[:length][:value] |
|
258 | 258 | @best[:length] = prop[:length] |
|
259 | 259 | @best[:length][:lang] = lang |
|
260 | 260 | end |
|
261 | 261 | if @best[:first][:value] >= prop[:first][:value] |
|
262 | 262 | @best[:first] = prop[:first] |
|
263 | 263 | @best[:first][:lang] = lang |
|
264 | 264 | end |
|
265 | 265 | end |
|
266 | 266 | end |
|
267 | 267 | |
|
268 | 268 | @histogram[:summary][:max] = [@histogram[:data].max,1].max |
|
269 | 269 | @summary[:attempt] = user.count |
|
270 | 270 | user.each_value { |v| @summary[:solve] += 1 if v == 1 } |
|
271 | 271 | end |
|
272 | 272 | |
|
273 | 273 | def stuck #report struggling user,problem |
|
274 | 274 | # init |
|
275 | 275 | user,problem = nil |
|
276 | 276 | solve = true |
|
277 | 277 | tries = 0 |
|
278 | 278 | @struggle = Array.new |
|
279 | 279 | record = {} |
|
280 | 280 | Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub| |
|
281 | 281 | next unless sub.problem and sub.user |
|
282 | 282 | if user != sub.user_id or problem != sub.problem_id |
|
283 | 283 | @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve |
|
284 | 284 | record = {user: sub.user, problem: sub.problem} |
|
285 | 285 | user,problem = sub.user_id, sub.problem_id |
|
286 | 286 | solve = false |
|
287 | 287 | tries = 0 |
|
288 | 288 | end |
|
289 | 289 | if sub.points >= sub.problem.full_score |
|
290 | 290 | solve = true |
|
291 | 291 | else |
|
292 | 292 | tries += 1 |
|
293 | 293 | end |
|
294 | 294 | end |
|
295 | 295 | @struggle.sort!{|a,b| b[:tries] <=> a[:tries] } |
|
296 | 296 | @struggle = @struggle[0..50] |
|
297 | 297 | end |
|
298 | 298 | |
|
299 | 299 | |
|
300 | 300 | def multiple_login |
|
301 | 301 | #user with multiple IP |
|
302 | 302 | raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login) |
|
303 | 303 | last,count = 0,0 |
|
304 | 304 | first = 0 |
|
305 | 305 | @users = [] |
|
306 | 306 | raw.each do |r| |
|
307 | 307 | if last != r.user.login |
|
308 | 308 | count = 1 |
|
309 | 309 | last = r.user.login |
|
310 | 310 | first = r |
|
311 | 311 | else |
|
312 | 312 | @users << first if count == 1 |
|
313 | 313 | @users << r |
|
314 | 314 | count += 1 |
|
315 | 315 | end |
|
316 | 316 | end |
|
317 | 317 | |
|
318 | 318 | #IP with multiple user |
|
319 | 319 | raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address) |
|
320 | 320 | last,count = 0,0 |
|
321 | 321 | first = 0 |
|
322 | 322 | @ip = [] |
|
323 | 323 | raw.each do |r| |
|
324 | 324 | if last != r.ip_address |
|
325 | 325 | count = 1 |
|
326 | 326 | last = r.ip_address |
|
327 | 327 | first = r |
|
328 | 328 | else |
|
329 | 329 | @ip << first if count == 1 |
|
330 | 330 | @ip << r |
|
331 | 331 | count += 1 |
|
332 | 332 | end |
|
333 | 333 | end |
|
334 | 334 | end |
|
335 | 335 | |
|
336 | 336 | def cheat_report |
|
337 | 337 | date_and_time = '%Y-%m-%d %H:%M' |
|
338 | 338 | begin |
|
339 | 339 | md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/) |
|
340 | 340 | @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) |
|
341 | 341 | rescue |
|
342 | 342 | @since_time = Time.zone.now.ago( 90.minutes) |
|
343 | 343 | end |
|
344 | 344 | begin |
|
345 | 345 | md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/) |
|
346 | 346 | @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) |
|
347 | 347 | rescue |
|
348 | 348 | @until_time = Time.zone.now |
|
349 | 349 | end |
|
350 | 350 | |
|
351 | 351 | #multi login |
|
352 | 352 | @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") |
|
353 | 353 | |
|
354 | 354 | st = <<-SQL |
|
355 | 355 | SELECT l2.* |
|
356 | 356 | FROM logins l2 INNER JOIN |
|
357 | 357 | (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name |
|
358 | 358 | FROM logins l |
|
359 | 359 | INNER JOIN users u ON l.user_id = u.id |
|
360 | 360 | WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}' |
|
361 | 361 | GROUP BY u.id |
|
362 | 362 | HAVING count > 1 |
|
363 | 363 | ) ml ON l2.user_id = ml.id |
|
364 | 364 | WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}' |
|
365 | 365 | UNION |
|
366 | 366 | SELECT l2.* |
|
367 | 367 | FROM logins l2 INNER JOIN |
|
368 | 368 | (SELECT l.ip_address,COUNT(DISTINCT u.id) as count |
|
369 | 369 | FROM logins l |
|
370 | 370 | INNER JOIN users u ON l.user_id = u.id |
|
371 | 371 | WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}' |
|
372 | 372 | GROUP BY l.ip_address |
|
373 | 373 | HAVING count > 1 |
|
374 | 374 | ) ml on ml.ip_address = l2.ip_address |
|
375 | 375 | INNER JOIN users u ON l2.user_id = u.id |
|
376 | 376 | WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}' |
|
377 | 377 | ORDER BY ip_address,created_at |
|
378 | 378 | SQL |
|
379 | 379 | @mld = Login.find_by_sql(st) |
|
380 | 380 | |
|
381 | 381 | st = <<-SQL |
|
382 | 382 | SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id |
|
383 | 383 | FROM submissions s INNER JOIN |
|
384 | 384 | (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name |
|
385 | 385 | FROM logins l |
|
386 | 386 | INNER JOIN users u ON l.user_id = u.id |
|
387 | 387 | WHERE l.created_at >= ? and l.created_at <= ? |
|
388 | 388 | GROUP BY u.id |
|
389 | 389 | HAVING count > 1 |
|
390 | 390 | ) ml ON s.user_id = ml.id |
|
391 | 391 | WHERE s.submitted_at >= ? and s.submitted_at <= ? |
|
392 | 392 | UNION |
|
393 | 393 | SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id |
|
394 | 394 | FROM submissions s INNER JOIN |
|
395 | 395 | (SELECT l.ip_address,COUNT(DISTINCT u.id) as count |
|
396 | 396 | FROM logins l |
|
397 | 397 | INNER JOIN users u ON l.user_id = u.id |
|
398 | 398 | WHERE l.created_at >= ? and l.created_at <= ? |
|
399 | 399 | GROUP BY l.ip_address |
|
400 | 400 | HAVING count > 1 |
|
401 | 401 | ) ml on ml.ip_address = s.ip_address |
|
402 | 402 | WHERE s.submitted_at >= ? and s.submitted_at <= ? |
|
403 | 403 | ORDER BY ip_address,submitted_at |
|
404 | 404 | SQL |
|
405 | 405 | @subs = Submission.joins(:problem).find_by_sql([st,@since_time,@until_time, |
|
406 | 406 | @since_time,@until_time, |
|
407 | 407 | @since_time,@until_time, |
|
408 | 408 | @since_time,@until_time]) |
|
409 | 409 | |
|
410 | 410 | end |
|
411 | 411 | |
|
412 | 412 | def cheat_scruntinize |
|
413 | 413 | #convert date & time |
|
414 | 414 | date_and_time = '%Y-%m-%d %H:%M' |
|
415 | 415 | begin |
|
416 | 416 | md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/) |
|
417 | 417 | @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) |
|
418 | 418 | rescue |
|
419 | 419 | @since_time = Time.zone.now.ago( 90.minutes) |
|
420 | 420 | end |
@@ -1,218 +1,219 | |||
|
1 | 1 | require 'net/smtp' |
|
2 | 2 | |
|
3 | 3 | class UsersController < ApplicationController |
|
4 | 4 | |
|
5 | 5 | include MailHelperMethods |
|
6 | 6 | |
|
7 | 7 | before_filter :authenticate, :except => [:new, |
|
8 | 8 | :register, |
|
9 | 9 | :confirm, |
|
10 | 10 | :forget, |
|
11 | 11 | :retrieve_password] |
|
12 | 12 | |
|
13 | 13 | before_filter :verify_online_registration, :only => [:new, |
|
14 | 14 | :register, |
|
15 | 15 | :forget, |
|
16 | 16 | :retrieve_password] |
|
17 | 17 | before_filter :authenticate, :profile_authorization, only: [:profile] |
|
18 | 18 | |
|
19 | 19 | verify :method => :post, :only => [:chg_passwd], |
|
20 | 20 | :redirect_to => { :action => :index } |
|
21 | 21 | |
|
22 | 22 | #in_place_edit_for :user, :alias_for_editing |
|
23 | 23 | #in_place_edit_for :user, :email_for_editing |
|
24 | 24 | |
|
25 | 25 | def index |
|
26 | 26 | if !GraderConfiguration['system.user_setting_enabled'] |
|
27 | 27 | redirect_to :controller => 'main', :action => 'list' |
|
28 | 28 | else |
|
29 | 29 | @user = User.find(session[:user_id]) |
|
30 | 30 | end |
|
31 | 31 | end |
|
32 | 32 | |
|
33 | 33 | def chg_passwd |
|
34 | 34 | user = User.find(session[:user_id]) |
|
35 | 35 | user.password = params[:passwd] |
|
36 | 36 | user.password_confirmation = params[:passwd_verify] |
|
37 | 37 | if user.save |
|
38 | 38 | flash[:notice] = 'password changed' |
|
39 | 39 | else |
|
40 | 40 | flash[:notice] = 'Error: password changing failed' |
|
41 | 41 | end |
|
42 | 42 | redirect_to :action => 'index' |
|
43 | 43 | end |
|
44 | 44 | |
|
45 | 45 | def new |
|
46 | 46 | @user = User.new |
|
47 | 47 | render :action => 'new', :layout => 'empty' |
|
48 | 48 | end |
|
49 | 49 | |
|
50 | 50 | def register |
|
51 | 51 | if(params[:cancel]) |
|
52 | 52 | redirect_to :controller => 'main', :action => 'login' |
|
53 | 53 | return |
|
54 | 54 | end |
|
55 | 55 | @user = User.new(user_params) |
|
56 | 56 | @user.password_confirmation = @user.password = User.random_password |
|
57 | 57 | @user.activated = false |
|
58 | 58 | if (@user.valid?) and (@user.save) |
|
59 | 59 | if send_confirmation_email(@user) |
|
60 | 60 | render :action => 'new_splash', :layout => 'empty' |
|
61 | 61 | else |
|
62 | 62 | @admin_email = GraderConfiguration['system.admin_email'] |
|
63 | 63 | render :action => 'email_error', :layout => 'empty' |
|
64 | 64 | end |
|
65 | 65 | else |
|
66 | 66 | @user.errors.add(:base,"Email cannot be blank") if @user.email=='' |
|
67 | 67 | render :action => 'new', :layout => 'empty' |
|
68 | 68 | end |
|
69 | 69 | end |
|
70 | 70 | |
|
71 | 71 | def confirm |
|
72 | 72 | login = params[:login] |
|
73 | 73 | key = params[:activation] |
|
74 | 74 | @user = User.find_by_login(login) |
|
75 | 75 | if (@user) and (@user.verify_activation_key(key)) |
|
76 | 76 | if @user.valid? # check uniquenss of email |
|
77 | 77 | @user.activated = true |
|
78 | 78 | @user.save |
|
79 | 79 | @result = :successful |
|
80 | 80 | else |
|
81 | 81 | @result = :email_used |
|
82 | 82 | end |
|
83 | 83 | else |
|
84 | 84 | @result = :failed |
|
85 | 85 | end |
|
86 | 86 | render :action => 'confirm', :layout => 'empty' |
|
87 | 87 | end |
|
88 | 88 | |
|
89 | 89 | def forget |
|
90 | 90 | render :action => 'forget', :layout => 'empty' |
|
91 | 91 | end |
|
92 | 92 | |
|
93 | 93 | def retrieve_password |
|
94 | 94 | email = params[:email] |
|
95 | 95 | user = User.find_by_email(email) |
|
96 | 96 | if user |
|
97 | 97 | last_updated_time = user.updated_at || user.created_at || (Time.now.gmtime - 1.hour) |
|
98 | 98 | if last_updated_time > Time.now.gmtime - 5.minutes |
|
99 | 99 | flash[:notice] = 'The account has recently created or new password has recently been requested. Please wait for 5 minutes' |
|
100 | 100 | else |
|
101 | 101 | user.password = user.password_confirmation = User.random_password |
|
102 | 102 | user.save |
|
103 | 103 | send_new_password_email(user) |
|
104 | 104 | flash[:notice] = 'New password has been mailed to you.' |
|
105 | 105 | end |
|
106 | 106 | else |
|
107 | 107 | flash[:notice] = I18n.t 'registration.password_retrieval.no_email' |
|
108 | 108 | end |
|
109 | 109 | redirect_to :action => 'forget' |
|
110 | 110 | end |
|
111 | 111 | |
|
112 | 112 | def stat |
|
113 | 113 | @user = User.find(params[:id]) |
|
114 |
- @submission = Submission.in |
|
|
114 | + @submission = Submission.joins(:problem).where(user_id: params[:id]) | |
|
115 | + @submission = @submission.where('problems.available = true') unless current_user.admin? | |
|
115 | 116 | |
|
116 | 117 | range = 120 |
|
117 | 118 | @histogram = { data: Array.new(range,0), summary: {} } |
|
118 | 119 | @summary = {count: 0, solve: 0, attempt: 0} |
|
119 | 120 | problem = Hash.new(0) |
|
120 | 121 | |
|
121 | 122 | @submission.find_each do |sub| |
|
122 | 123 | #histogram |
|
123 | 124 | d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60 |
|
124 | 125 | @histogram[:data][d.to_i] += 1 if d < range |
|
125 | 126 | |
|
126 | 127 | @summary[:count] += 1 |
|
127 | 128 | next unless sub.problem |
|
128 | 129 | problem[sub.problem] = [problem[sub.problem], ( (sub.try(:points) || 0) >= sub.problem.full_score) ? 1 : 0].max |
|
129 | 130 | end |
|
130 | 131 | |
|
131 | 132 | @histogram[:summary][:max] = [@histogram[:data].max,1].max |
|
132 | 133 | @summary[:attempt] = problem.count |
|
133 | 134 | problem.each_value { |v| @summary[:solve] += 1 if v == 1 } |
|
134 | 135 | end |
|
135 | 136 | |
|
136 | 137 | def toggle_activate |
|
137 | 138 | @user = User.find(params[:id]) |
|
138 | 139 | @user.update_attributes( activated: !@user.activated? ) |
|
139 | 140 | respond_to do |format| |
|
140 | 141 | format.js { render partial: 'toggle_button', |
|
141 | 142 | locals: {button_id: "#toggle_activate_user_#{@user.id}",button_on: @user.activated? } } |
|
142 | 143 | end |
|
143 | 144 | end |
|
144 | 145 | |
|
145 | 146 | def toggle_enable |
|
146 | 147 | @user = User.find(params[:id]) |
|
147 | 148 | @user.update_attributes( enabled: !@user.enabled? ) |
|
148 | 149 | respond_to do |format| |
|
149 | 150 | format.js { render partial: 'toggle_button', |
|
150 | 151 | locals: {button_id: "#toggle_enable_user_#{@user.id}",button_on: @user.enabled? } } |
|
151 | 152 | end |
|
152 | 153 | end |
|
153 | 154 | |
|
154 | 155 | protected |
|
155 | 156 | |
|
156 | 157 | def verify_online_registration |
|
157 | 158 | if !GraderConfiguration['system.online_registration'] |
|
158 | 159 | redirect_to :controller => 'main', :action => 'login' |
|
159 | 160 | end |
|
160 | 161 | end |
|
161 | 162 | |
|
162 | 163 | def send_confirmation_email(user) |
|
163 | 164 | contest_name = GraderConfiguration['contest.name'] |
|
164 | 165 | activation_url = url_for(:action => 'confirm', |
|
165 | 166 | :login => user.login, |
|
166 | 167 | :activation => user.activation_key) |
|
167 | 168 | home_url = url_for(:controller => 'main', :action => 'index') |
|
168 | 169 | mail_subject = "[#{contest_name}] Confirmation" |
|
169 | 170 | mail_body = t('registration.email_body', { |
|
170 | 171 | :full_name => user.full_name, |
|
171 | 172 | :contest_name => contest_name, |
|
172 | 173 | :login => user.login, |
|
173 | 174 | :password => user.password, |
|
174 | 175 | :activation_url => activation_url, |
|
175 | 176 | :admin_email => GraderConfiguration['system.admin_email'] |
|
176 | 177 | }) |
|
177 | 178 | |
|
178 | 179 | logger.info mail_body |
|
179 | 180 | |
|
180 | 181 | send_mail(user.email, mail_subject, mail_body) |
|
181 | 182 | end |
|
182 | 183 | |
|
183 | 184 | def send_new_password_email(user) |
|
184 | 185 | contest_name = GraderConfiguration['contest.name'] |
|
185 | 186 | mail_subject = "[#{contest_name}] Password recovery" |
|
186 | 187 | mail_body = t('registration.password_retrieval.email_body', { |
|
187 | 188 | :full_name => user.full_name, |
|
188 | 189 | :contest_name => contest_name, |
|
189 | 190 | :login => user.login, |
|
190 | 191 | :password => user.password, |
|
191 | 192 | :admin_email => GraderConfiguration['system.admin_email'] |
|
192 | 193 | }) |
|
193 | 194 | |
|
194 | 195 | logger.info mail_body |
|
195 | 196 | |
|
196 | 197 | send_mail(user.email, mail_subject, mail_body) |
|
197 | 198 | end |
|
198 | 199 | |
|
199 | 200 | # allow viewing of regular user profile only when options allow so |
|
200 | 201 | # only admins can view admins profile |
|
201 | 202 | def profile_authorization |
|
202 | 203 | #if view admins' profile, allow only admin |
|
203 | 204 | return false unless(params[:id]) |
|
204 | 205 | user = User.find(params[:id]) |
|
205 | 206 | return false unless user |
|
206 | 207 | return admin_authorization if user.admin? |
|
207 | 208 | return true if GraderConfiguration["right.user_view_submission"] |
|
208 | 209 | |
|
209 | 210 | #finally, we allow only admin |
|
210 | 211 | admin_authorization |
|
211 | 212 | end |
|
212 | 213 | |
|
213 | 214 | private |
|
214 | 215 | def user_params |
|
215 | 216 | params.require(:user).permit(:login, :full_name, :email) |
|
216 | 217 | end |
|
217 | 218 | |
|
218 | 219 | end |
@@ -1,78 +1,72 | |||
|
1 | 1 | require File.expand_path('../boot', __FILE__) |
|
2 | 2 | |
|
3 | 3 | require 'rails/all' |
|
4 | 4 | |
|
5 | 5 | if defined?(Bundler) |
|
6 | 6 | # If you precompile assets before deploying to production, use this line |
|
7 | 7 | Bundler.require(*Rails.groups(:assets => %w(development test))) |
|
8 | 8 | # If you want your assets lazily compiled in production, use this line |
|
9 | 9 | # Bundler.require(:default, :assets, Rails.env) |
|
10 | 10 | end |
|
11 | 11 | |
|
12 | 12 | module CafeGrader |
|
13 | 13 | class Application < Rails::Application |
|
14 | 14 | # Settings in config/environments/* take precedence over those specified here. |
|
15 | 15 | # Application configuration should go into files in config/initializers |
|
16 | 16 | # -- all .rb files in that directory are automatically loaded. |
|
17 | 17 | |
|
18 | 18 | # Custom directories with classes and modules you want to be autoloadable. |
|
19 | 19 | config.autoload_paths += %W(#{config.root}/lib) |
|
20 | 20 | |
|
21 | 21 | # Only load the plugins named here, in the order given (default is alphabetical). |
|
22 | 22 | # :all can be used as a placeholder for all plugins not explicitly named. |
|
23 | 23 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] |
|
24 | 24 | |
|
25 | 25 | # Activate observers that should always be running. |
|
26 | 26 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer |
|
27 | 27 | |
|
28 | 28 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. |
|
29 | 29 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. |
|
30 | 30 | config.time_zone = 'UTC' |
|
31 | 31 | |
|
32 | 32 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. |
|
33 | 33 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] |
|
34 | 34 | config.i18n.default_locale = :en |
|
35 | 35 | |
|
36 | 36 | # Configure the default encoding used in templates for Ruby 1.9. |
|
37 | 37 | config.encoding = "utf-8" |
|
38 | 38 | |
|
39 | 39 | # Configure sensitive parameters which will be filtered from the log file. |
|
40 | 40 | config.filter_parameters += [:password] |
|
41 | 41 | |
|
42 | 42 | # Enable escaping HTML in JSON. |
|
43 | 43 | config.active_support.escape_html_entities_in_json = true |
|
44 | 44 | |
|
45 | 45 | # Use SQL instead of Active Record's schema dumper when creating the database. |
|
46 | 46 | # This is necessary if your schema can't be completely dumped by the schema dumper, |
|
47 | 47 | # like if you have constraints or database-specific column types |
|
48 | 48 | # config.active_record.schema_format = :sql |
|
49 | 49 | |
|
50 | - # Enforce whitelist mode for mass assignment. | |
|
51 | - # This will create an empty whitelist of attributes available for mass-assignment for all models | |
|
52 | - # in your app. As such, your models will need to explicitly whitelist or blacklist accessible | |
|
53 | - # parameters by using an attr_accessible or attr_protected declaration. | |
|
54 | - config.active_record.whitelist_attributes = false | |
|
55 | - | |
|
56 | 50 | # Enable the asset pipeline |
|
57 | 51 | config.assets.enabled = true |
|
58 | 52 | |
|
59 | 53 | # Version of your assets, change this if you want to expire all your assets |
|
60 | 54 | config.assets.version = '1.0' |
|
61 | 55 | |
|
62 | 56 | # ---------------- IMPORTANT ---------------------- |
|
63 | 57 | # If we deploy the app into a subdir name "grader", be sure to do "rake assets:precompile RAILS_RELATIVE_URL_ROOT=/grader" |
|
64 | 58 | # moreover, using the following line instead also known to works |
|
65 | 59 | #config.action_controller.relative_url_root = '/grader' |
|
66 | 60 | |
|
67 | 61 | #font path |
|
68 | 62 | config.assets.paths << "#{Rails}/vendor/assets/fonts" |
|
69 | 63 | |
|
70 | 64 | config.assets.precompile += ['announcement_refresh.js','effects.js','site_update.js'] |
|
71 | 65 | config.assets.precompile += ['local_jquery.js','tablesorter-theme.cafe.css'] |
|
72 | 66 | %w( announcements submissions configurations contests contest_management graders heartbeat |
|
73 | 67 | login main messages problems report site sites sources tasks |
|
74 | 68 | test user_admin users ).each do |controller| |
|
75 | 69 | config.assets.precompile += ["#{controller}.js", "#{controller}.css"] |
|
76 | 70 | end |
|
77 | 71 | end |
|
78 | 72 | end |
You need to be logged in to leave comments.
Login now