Description:
Merge pull request #21 from nattee/master block report/current_score from normal user
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r721:93040f45d604 - - 1 file changed: 1 inserted, 1 deleted

@@ -1,391 +1,391
1 1 require 'csv'
2 2
3 3 class ReportController < ApplicationController
4 4
5 5 before_filter :authenticate
6 6
7 - before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score]
7 + before_filter :admin_authorization, only: [:login_stat,:submission_stat, :stuck, :cheat_report, :cheat_scruntinize, :show_max_score, :current_score]
8 8
9 9 before_filter(only: [:problem_hof]) { |c|
10 10 return false unless authenticate
11 11
12 12 admin_authorization unless GraderConfiguration["right.user_view_submission"]
13 13 }
14 14
15 15 def max_score
16 16 end
17 17
18 18 def current_score
19 19 @problems = Problem.available_problems
20 20 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
21 21 @scorearray = calculate_max_score(@problems, @users,0,0,true)
22 22
23 23 #rencer accordingly
24 24 if params[:button] == 'download' then
25 25 csv = gen_csv_from_scorearray(@scorearray,@problems)
26 26 send_data csv, filename: 'max_score.csv'
27 27 else
28 28 #render template: 'user_admin/user_stat'
29 29 render 'current_score'
30 30 end
31 31 end
32 32
33 33 def show_max_score
34 34 #process parameters
35 35 #problems
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 @since_id = nil if @since_id == 0
56 56 @until_id = nil if @until_id == 0
57 57
58 58 #calculate the routine
59 59 @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
60 60
61 61 #rencer accordingly
62 62 if params[:button] == 'download' then
63 63 csv = gen_csv_from_scorearray(@scorearray,@problems)
64 64 send_data csv, filename: 'max_score.csv'
65 65 else
66 66 #render template: 'user_admin/user_stat'
67 67 render 'max_score'
68 68 end
69 69
70 70 end
71 71
72 72 def score
73 73 if params[:commit] == 'download csv'
74 74 @problems = Problem.all
75 75 else
76 76 @problems = Problem.available_problems
77 77 end
78 78 @users = User.includes(:contests, :contest_stat).where(enabled: true)
79 79 @scorearray = Array.new
80 80 @users.each do |u|
81 81 ustat = Array.new
82 82 ustat[0] = u
83 83 @problems.each do |p|
84 84 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
85 85 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
86 86 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
87 87 else
88 88 ustat << [0,false]
89 89 end
90 90 end
91 91 @scorearray << ustat
92 92 end
93 93 if params[:commit] == 'download csv' then
94 94 csv = gen_csv_from_scorearray(@scorearray,@problems)
95 95 send_data csv, filename: 'last_score.csv'
96 96 else
97 97 render template: 'user_admin/user_stat'
98 98 end
99 99
100 100 end
101 101
102 102 def login_stat
103 103 @logins = Array.new
104 104
105 105 date_and_time = '%Y-%m-%d %H:%M'
106 106 begin
107 107 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
108 108 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
109 109 rescue
110 110 @since_time = DateTime.new(1000,1,1)
111 111 end
112 112 begin
113 113 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
114 114 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
115 115 rescue
116 116 @until_time = DateTime.new(3000,1,1)
117 117 end
118 118
119 119 User.all.each do |user|
120 120 @logins << { id: user.id,
121 121 login: user.login,
122 122 full_name: user.full_name,
123 123 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
124 124 user.id,@since_time,@until_time)
125 125 .count(:id),
126 126 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
127 127 user.id,@since_time,@until_time)
128 128 .minimum(:created_at),
129 129 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
130 130 user.id,@since_time,@until_time)
131 131 .maximum(:created_at),
132 132 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
133 133 user.id,@since_time,@until_time)
134 134 .select(:ip_address).uniq
135 135
136 136 }
137 137 end
138 138 end
139 139
140 140 def submission_stat
141 141
142 142 date_and_time = '%Y-%m-%d %H:%M'
143 143 begin
144 144 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
145 145 rescue
146 146 @since_time = DateTime.new(1000,1,1)
147 147 end
148 148 begin
149 149 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
150 150 rescue
151 151 @until_time = DateTime.new(3000,1,1)
152 152 end
153 153
154 154 @submissions = {}
155 155
156 156 User.find_each do |user|
157 157 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
158 158 end
159 159
160 160 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
161 161 if @submissions[s.user_id]
162 162 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
163 163 a = Problem.find_by_id(s.problem_id)
164 164 @submissions[s.user_id][:sub][s.problem_id] =
165 165 { prob_name: (a ? a.full_name : '(NULL)'),
166 166 sub_ids: [s.id] }
167 167 else
168 168 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
169 169 end
170 170 @submissions[s.user_id][:count] += 1
171 171 end
172 172 end
173 173 end
174 174
175 175 def problem_hof
176 176 # gen problem list
177 177 @user = User.find(session[:user_id])
178 178 @problems = @user.available_problems
179 179
180 180 # get selected problems or the default
181 181 if params[:id]
182 182 begin
183 183 @problem = Problem.available.find(params[:id])
184 184 rescue
185 185 redirect_to action: :problem_hof
186 186 flash[:notice] = 'Error: submissions for that problem are not viewable.'
187 187 return
188 188 end
189 189 end
190 190
191 191 return unless @problem
192 192
193 193 @by_lang = {} #aggregrate by language
194 194
195 195 range =65
196 196 @histogram = { data: Array.new(range,0), summary: {} }
197 197 @summary = {count: 0, solve: 0, attempt: 0}
198 198 user = Hash.new(0)
199 199 Submission.where(problem_id: @problem.id).find_each do |sub|
200 200 #histogram
201 201 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
202 202 @histogram[:data][d.to_i] += 1 if d < range
203 203
204 204 next unless sub.points
205 205 @summary[:count] += 1
206 206 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
207 207
208 208 lang = Language.find_by_id(sub.language_id)
209 209 next unless lang
210 210 next unless sub.points >= @problem.full_score
211 211
212 212 #initialize
213 213 unless @by_lang.has_key?(lang.pretty_name)
214 214 @by_lang[lang.pretty_name] = {
215 215 runtime: { avail: false, value: 2**30-1 },
216 216 memory: { avail: false, value: 2**30-1 },
217 217 length: { avail: false, value: 2**30-1 },
218 218 first: { avail: false, value: DateTime.new(3000,1,1) }
219 219 }
220 220 end
221 221
222 222 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
223 223 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
224 224 end
225 225
226 226 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
227 227 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
228 228 end
229 229
230 230 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and sub.user and
231 231 !sub.user.admin?
232 232 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
233 233 end
234 234
235 235 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
236 236 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
237 237 end
238 238 end
239 239
240 240 #process user_id
241 241 @by_lang.each do |lang,prop|
242 242 prop.each do |k,v|
243 243 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
244 244 end
245 245 end
246 246
247 247 #sum into best
248 248 if @by_lang and @by_lang.first
249 249 @best = @by_lang.first[1].clone
250 250 @by_lang.each do |lang,prop|
251 251 if @best[:runtime][:value] >= prop[:runtime][:value]
252 252 @best[:runtime] = prop[:runtime]
253 253 @best[:runtime][:lang] = lang
254 254 end
255 255 if @best[:memory][:value] >= prop[:memory][:value]
256 256 @best[:memory] = prop[:memory]
257 257 @best[:memory][:lang] = lang
258 258 end
259 259 if @best[:length][:value] >= prop[:length][:value]
260 260 @best[:length] = prop[:length]
261 261 @best[:length][:lang] = lang
262 262 end
263 263 if @best[:first][:value] >= prop[:first][:value]
264 264 @best[:first] = prop[:first]
265 265 @best[:first][:lang] = lang
266 266 end
267 267 end
268 268 end
269 269
270 270 @histogram[:summary][:max] = [@histogram[:data].max,1].max
271 271 @summary[:attempt] = user.count
272 272 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
273 273 end
274 274
275 275 def stuck #report struggling user,problem
276 276 # init
277 277 user,problem = nil
278 278 solve = true
279 279 tries = 0
280 280 @struggle = Array.new
281 281 record = {}
282 282 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
283 283 next unless sub.problem and sub.user
284 284 if user != sub.user_id or problem != sub.problem_id
285 285 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
286 286 record = {user: sub.user, problem: sub.problem}
287 287 user,problem = sub.user_id, sub.problem_id
288 288 solve = false
289 289 tries = 0
290 290 end
291 291 if sub.points >= sub.problem.full_score
292 292 solve = true
293 293 else
294 294 tries += 1
295 295 end
296 296 end
297 297 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
298 298 @struggle = @struggle[0..50]
299 299 end
300 300
301 301
302 302 def multiple_login
303 303 #user with multiple IP
304 304 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
305 305 last,count = 0,0
306 306 first = 0
307 307 @users = []
308 308 raw.each do |r|
309 309 if last != r.user.login
310 310 count = 1
311 311 last = r.user.login
312 312 first = r
313 313 else
314 314 @users << first if count == 1
315 315 @users << r
316 316 count += 1
317 317 end
318 318 end
319 319
320 320 #IP with multiple user
321 321 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
322 322 last,count = 0,0
323 323 first = 0
324 324 @ip = []
325 325 raw.each do |r|
326 326 if last != r.ip_address
327 327 count = 1
328 328 last = r.ip_address
329 329 first = r
330 330 else
331 331 @ip << first if count == 1
332 332 @ip << r
333 333 count += 1
334 334 end
335 335 end
336 336 end
337 337
338 338 def cheat_report
339 339 date_and_time = '%Y-%m-%d %H:%M'
340 340 begin
341 341 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
342 342 @since_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
343 343 rescue
344 344 @since_time = Time.zone.now.ago( 90.minutes)
345 345 end
346 346 begin
347 347 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
348 348 @until_time = Time.zone.local(md[1].to_i,md[2].to_i,md[3].to_i,md[4].to_i,md[5].to_i)
349 349 rescue
350 350 @until_time = Time.zone.now
351 351 end
352 352
353 353 #multi login
354 354 @ml = Login.joins(:user).where("logins.created_at >= ? and logins.created_at <= ?",@since_time,@until_time).select('users.login,count(distinct ip_address) as count,users.full_name').group("users.id").having("count > 1")
355 355
356 356 st = <<-SQL
357 357 SELECT l2.*
358 358 FROM logins l2 INNER JOIN
359 359 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
360 360 FROM logins l
361 361 INNER JOIN users u ON l.user_id = u.id
362 362 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
363 363 GROUP BY u.id
364 364 HAVING count > 1
365 365 ) ml ON l2.user_id = ml.id
366 366 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
367 367 UNION
368 368 SELECT l2.*
369 369 FROM logins l2 INNER JOIN
370 370 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
371 371 FROM logins l
372 372 INNER JOIN users u ON l.user_id = u.id
373 373 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
374 374 GROUP BY l.ip_address
375 375 HAVING count > 1
376 376 ) ml on ml.ip_address = l2.ip_address
377 377 INNER JOIN users u ON l2.user_id = u.id
378 378 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
379 379 ORDER BY ip_address,created_at
380 380 SQL
381 381 @mld = Login.find_by_sql(st)
382 382
383 383 st = <<-SQL
384 384 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
385 385 FROM submissions s INNER JOIN
386 386 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
387 387 FROM logins l
388 388 INNER JOIN users u ON l.user_id = u.id
389 389 WHERE l.created_at >= ? and l.created_at <= ?
390 390 GROUP BY u.id
391 391 HAVING count > 1
You need to be logged in to leave comments. Login now