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

r793:8cbec24011e9 - - 4 files changed: 112 inserted, 87 deleted

@@ -0,0 +1,107
1 + - content_for :header do
2 + = stylesheet_link_tag 'tablesorter-theme.cafe'
3 + = javascript_include_tag 'local_jquery'
4 +
5 + %script{:type=>"text/javascript"}
6 + $(function () {
7 + $('#since_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
8 + $('#until_datetime').datetimepicker({ showButtonPanel: true, dateFormat: "yy-mm-dd", controlType: "slider"} );
9 + $('#my_table').tablesorter({widthFixed: true, widgets: ['zebra']});
10 + $('#my_table2').tablesorter({widthFixed: true, widgets: ['zebra']});
11 + $('#sub_table').tablesorter({widthFixed: true, widgets: ['zebra']});
12 + });
13 +
14 + %h1 Login status
15 +
16 + .panel.panel-primary
17 + .panel-heading
18 + User ID and time rnage
19 + .panel-body
20 + = form_tag({session: :url }) do
21 + .row.form-group
22 + .col-md-2
23 + From Date:
24 + .col-md-4
25 + .input-group.date
26 + = text_field_tag :since_datetime, @since_time, class: 'form-control'
27 + %span.input-group-addon
28 + %span.glyphicon.glyphicon-calendar
29 + .row.form-group
30 + .col-md-2
31 + Until Date:
32 + .col-md-4
33 + .input-group.date
34 + = text_field_tag :until_datetime, @until_time, class: 'form-control'
35 + %span.input-group-addon
36 + %span.glyphicon.glyphicon-calendar
37 + .row.form-group
38 + .col-md-2
39 + IDs (separated by space):
40 + .col-md-10
41 + = text_field_tag 'SID', @sid, size: 40, class: 'form-control'
42 + .row.form-group
43 + .col-md-2
44 + .col-md-10
45 + =submit_tag 'query', class: 'btn btn-primary'
46 +
47 + -#
48 + %table
49 + %tr
50 + %td{style: 'width: 120px; font-weight: bold'}= 'Login date range'
51 + %td{align: 'right'} since:
52 + %td= text_field_tag 'since_datetime', @since_time, class: 'form-control datetimepicker'
53 + %tr
54 + %td
55 + %td{align: 'right'} until:
56 + %td= text_field_tag 'until_datetime', @until_time
57 + %tr
58 + %td
59 + %td
60 + %td Blank mean no condition
61 + %tr
62 + %td{style: 'width: 120px; font-weight: bold'}= "ID"
63 + %td{colspan: 2}= text_field_tag 'SID', @sid, size: 40
64 + %tr
65 + %td
66 + %td
67 + %td= submit_tag 'query'
68 +
69 +
70 + %h2 Logs
71 +
72 + - if @sid
73 + %table
74 + %tbody
75 + - @sid.each do |id|
76 + - user = User.where("login = ?",id).first
77 + %tr
78 + //%td= link_to id, controller: :user, action: :profile, id: id
79 + %td= link_to id, "https://www.nattee.net/java/users/profile/#{user.id}"
80 + %td= user.full_name
81 +
82 + //%table.tablesorter-cafe#my_table
83 + %table.info
84 + %thead
85 + %tr.info-head
86 + %th Time
87 + %th Action
88 + %th IP
89 + %th login
90 + %th name
91 + %th problem
92 + %th score
93 + %tbody
94 + - if @logs
95 + - @logs.each do |l|
96 + %tr{class: cycle('info-even','info-odd')}
97 + %td= l[:submitted_at].strftime "%Y.%b.%d %H:%M:%S"
98 + //%td= l[:id] == -1 ? "LOGIN" : link_to("submit #{l[:id]}", controller: 'graders' , action: 'submission', id: l[:id])
99 + %td= l[:id] == -1 ? "LOGIN" : link_to( "submit #{l[:id]}", "https://www.nattee.net/java/graders/submission/#{l[:id]}")
100 + %td= l[:ip_address]
101 + //%td= link_to l[:login], controller: 'users', action: 'profile', id: l[:id]
102 + %td= link_to( l[:login], "https://www.nattee.net/java/users/profile/#{l.user_id}")
103 + %td= l[:full_name]
104 + %td= l[:id] == -1 ? "" : l.problem.name
105 + %td= l[:id] == -1 ? "" : l.points * 100/ l.problem.full_score
106 +
107 +
@@ -1,105 +1,105
1 1 require 'csv'
2 2
3 3 class ReportController < ApplicationController
4 4
5 5 before_action :check_valid_login
6 6
7 7 before_action :admin_authorization, only: [:login_stat,:submission, :submission_query,
8 8 :login, :login_detail_query, :login_summary_query,
9 - :stuck, :cheat_report, :cheat_scruntinize, :show_max_score, :current_score]
9 + :stuck, :cheat_report, :cheat_scrutinize, :show_max_score, :current_score]
10 10
11 11 before_action(only: [:problem_hof]) { |c|
12 12 return false unless check_valid_login
13 13
14 14 admin_authorization unless GraderConfiguration["right.user_view_submission"]
15 15 }
16 16
17 17 def max_score
18 18 end
19 19
20 20 def current_score
21 21 @problems = Problem.available_problems
22 22 if params[:group_id]
23 23 @group = Group.find(params[:group_id])
24 24 @users = @group.users.where(enabled: true)
25 25 else
26 26 @users = User.includes(:contests).includes(:contest_stat).where(enabled: true)
27 27 end
28 28 @scorearray = calculate_max_score(@problems, @users,0,0,true)
29 29
30 30 #rencer accordingly
31 31 if params[:button] == 'download' then
32 32 csv = gen_csv_from_scorearray(@scorearray,@problems)
33 33 send_data csv, filename: 'max_score.csv'
34 34 else
35 35 #render template: 'user_admin/user_stat'
36 36 render 'current_score'
37 37 end
38 38 end
39 39
40 40 def show_max_score
41 41 #process parameters
42 42 #problems
43 43 @problems = []
44 44 if params[:problem_id]
45 45 params[:problem_id].each do |id|
46 46 next unless id.strip != ""
47 47 pid = Problem.find_by_id(id.to_i)
48 48 @problems << pid if pid
49 49 end
50 50 end
51 51
52 52 #users
53 53 @users = if params[:users] == "all" then
54 54 User.includes(:contests).includes(:contest_stat)
55 55 else
56 56 User.includes(:contests).includes(:contest_stat).where(enabled: true)
57 57 end
58 58
59 59 #set up range from param
60 60 @since_id = params.fetch(:from_id, 0).to_i
61 61 @until_id = params.fetch(:to_id, 0).to_i
62 62 @since_id = nil if @since_id == 0
63 63 @until_id = nil if @until_id == 0
64 64
65 65 #calculate the routine
66 66 @scorearray = calculate_max_score(@problems, @users, @since_id, @until_id)
67 67
68 68 #rencer accordingly
69 69 if params[:button] == 'download' then
70 70 csv = gen_csv_from_scorearray(@scorearray,@problems)
71 71 send_data csv, filename: 'max_score.csv'
72 72 else
73 73 #render template: 'user_admin/user_stat'
74 74 render 'max_score'
75 75 end
76 76
77 77 end
78 78
79 79 def score
80 80 if params[:commit] == 'download csv'
81 81 @problems = Problem.all
82 82 else
83 83 @problems = Problem.available_problems
84 84 end
85 85 @users = User.includes(:contests, :contest_stat).where(enabled: true)
86 86 @scorearray = Array.new
87 87 @users.each do |u|
88 88 ustat = Array.new
89 89 ustat[0] = u
90 90 @problems.each do |p|
91 91 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
92 92 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
93 93 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
94 94 else
95 95 ustat << [0,false]
96 96 end
97 97 end
98 98 @scorearray << ustat
99 99 end
100 100 if params[:commit] == 'download csv' then
101 101 csv = gen_csv_from_scorearray(@scorearray,@problems)
102 102 send_data csv, filename: 'last_score.csv'
103 103 else
104 104 render template: 'user_admin/user_stat'
105 105 end
@@ -360,205 +360,205
360 360 end
361 361
362 362 #IP with multiple user
363 363 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
364 364 last,count = 0,0
365 365 first = 0
366 366 @ip = []
367 367 raw.each do |r|
368 368 if last != r.ip_address
369 369 count = 1
370 370 last = r.ip_address
371 371 first = r
372 372 else
373 373 @ip << first if count == 1
374 374 @ip << r
375 375 count += 1
376 376 end
377 377 end
378 378 end
379 379
380 380 def cheat_report
381 381 date_and_time = '%Y-%m-%d %H:%M'
382 382 begin
383 383 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
384 384 @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)
385 385 rescue
386 386 @since_time = Time.zone.now.ago( 90.minutes)
387 387 end
388 388 begin
389 389 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
390 390 @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)
391 391 rescue
392 392 @until_time = Time.zone.now
393 393 end
394 394
395 395 #multi login
396 396 @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")
397 397
398 398 st = <<-SQL
399 399 SELECT l2.*
400 400 FROM logins l2 INNER JOIN
401 401 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
402 402 FROM logins l
403 403 INNER JOIN users u ON l.user_id = u.id
404 404 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
405 405 GROUP BY u.id
406 406 HAVING count > 1
407 407 ) ml ON l2.user_id = ml.id
408 408 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
409 409 UNION
410 410 SELECT l2.*
411 411 FROM logins l2 INNER JOIN
412 412 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
413 413 FROM logins l
414 414 INNER JOIN users u ON l.user_id = u.id
415 415 WHERE l.created_at >= '#{@since_time.in_time_zone("UTC")}' and l.created_at <= '#{@until_time.in_time_zone("UTC")}'
416 416 GROUP BY l.ip_address
417 417 HAVING count > 1
418 418 ) ml on ml.ip_address = l2.ip_address
419 419 INNER JOIN users u ON l2.user_id = u.id
420 420 WHERE l2.created_at >= '#{@since_time.in_time_zone("UTC")}' and l2.created_at <= '#{@until_time.in_time_zone("UTC")}'
421 421 ORDER BY ip_address,created_at
422 422 SQL
423 423 @mld = Login.find_by_sql(st)
424 424
425 425 st = <<-SQL
426 426 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
427 427 FROM submissions s INNER JOIN
428 428 (SELECT u.id,COUNT(DISTINCT ip_address) as count,u.login,u.full_name
429 429 FROM logins l
430 430 INNER JOIN users u ON l.user_id = u.id
431 431 WHERE l.created_at >= ? and l.created_at <= ?
432 432 GROUP BY u.id
433 433 HAVING count > 1
434 434 ) ml ON s.user_id = ml.id
435 435 WHERE s.submitted_at >= ? and s.submitted_at <= ?
436 436 UNION
437 437 SELECT s.id,s.user_id,s.ip_address,s.submitted_at,s.problem_id
438 438 FROM submissions s INNER JOIN
439 439 (SELECT l.ip_address,COUNT(DISTINCT u.id) as count
440 440 FROM logins l
441 441 INNER JOIN users u ON l.user_id = u.id
442 442 WHERE l.created_at >= ? and l.created_at <= ?
443 443 GROUP BY l.ip_address
444 444 HAVING count > 1
445 445 ) ml on ml.ip_address = s.ip_address
446 446 WHERE s.submitted_at >= ? and s.submitted_at <= ?
447 447 ORDER BY ip_address,submitted_at
448 448 SQL
449 449 @subs = Submission.joins(:problem).find_by_sql([st,@since_time,@until_time,
450 450 @since_time,@until_time,
451 451 @since_time,@until_time,
452 452 @since_time,@until_time])
453 453
454 454 end
455 455
456 - def cheat_scruntinize
456 + def cheat_scrutinize
457 457 #convert date & time
458 458 date_and_time = '%Y-%m-%d %H:%M'
459 459 begin
460 460 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
461 461 @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)
462 462 rescue
463 463 @since_time = Time.zone.now.ago( 90.minutes)
464 464 end
465 465 begin
466 466 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
467 467 @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)
468 468 rescue
469 469 @until_time = Time.zone.now
470 470 end
471 471
472 472 #convert sid
473 473 @sid = params[:SID].split(/[,\s]/) if params[:SID]
474 474 unless @sid and @sid.size > 0
475 475 return
476 - redirect_to actoin: :cheat_scruntinize
476 + redirect_to actoin: :cheat_scrutinize
477 477 flash[:notice] = 'Please enter at least 1 student id'
478 478 end
479 479 mark = Array.new(@sid.size,'?')
480 480 condition = "(u.login = " + mark.join(' OR u.login = ') + ')'
481 481
482 482 @st = <<-SQL
483 483 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
484 484 FROM logins l INNER JOIN users u on l.user_id = u.id
485 485 WHERE l.created_at >= ? AND l.created_at <= ? AND #{condition}
486 486 UNION
487 487 SELECT s.submitted_at,s.id,u.login,u.full_name,s.ip_address,s.problem_id,s.points,s.user_id
488 488 FROM submissions s INNER JOIN users u ON s.user_id = u.id
489 489 WHERE s.submitted_at >= ? AND s.submitted_at <= ? AND #{condition}
490 490 ORDER BY submitted_at
491 491 SQL
492 492
493 493 p = [@st,@since_time,@until_time] + @sid + [@since_time,@until_time] + @sid
494 494 @logs = Submission.joins(:problem).find_by_sql(p)
495 495
496 496
497 497
498 498
499 499
500 500 end
501 501
502 502 protected
503 503
504 504 def calculate_max_score(problems, users,since_id,until_id, get_last_score = false)
505 505 #scorearray[i] = user #i's user stat where i is the index (not id)
506 506 scorearray = Array.new
507 507 users.each do |u|
508 508 ustat = Array.new
509 509 ustat[0] = u
510 510 problems.each do |p|
511 511 unless get_last_score
512 512 #get max score
513 513 max_points = 0
514 514 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
515 515 max_points = sub.points if sub and sub.points and (sub.points > max_points)
516 516 end
517 517 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
518 518 else
519 519 #get latest score
520 520 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
521 521 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
522 522 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
523 523 else
524 524 ustat << [0,false]
525 525 end
526 526 end
527 527 end
528 528 scorearray << ustat
529 529 end
530 530 return scorearray
531 531 end
532 532
533 533 def gen_csv_from_scorearray(scorearray,problem)
534 534 CSV.generate do |csv|
535 535 #add header
536 536 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
537 537 problem.each { |p| header << p.name }
538 538 header += ['Total','Passed']
539 539 csv << header
540 540 #add data
541 541 scorearray.each do |sc|
542 542 total = num_passed = 0
543 543 row = Array.new
544 544 sc.each_index do |i|
545 545 if i == 0
546 546 row << sc[i].login
547 547 row << sc[i].full_name
548 548 row << sc[i].activated
549 549 row << (sc[i].try(:contest_stat).try(:started_at)!=nil ? 'yes' : 'no')
550 550 row << sc[i].contests.collect {|c| c.name}.join(', ')
551 551 else
552 552 row << sc[i][0]
553 553 total += sc[i][0]
554 554 num_passed += 1 if sc[i][1]
555 555 end
556 556 end
557 557 row << total
558 558 row << num_passed
559 559 csv << row
560 560 end
561 561 end
562 562 end
563 563
564 564 end
@@ -57,155 +57,155
57 57 end
58 58 collection do
59 59
60 60 end
61 61 end
62 62
63 63 resources :testcases, only: [] do
64 64 member do
65 65 get 'download_input'
66 66 get 'download_sol'
67 67 end
68 68 collection do
69 69 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
70 70 end
71 71 end
72 72
73 73 resources :grader_configuration, controller: 'configurations' do
74 74 collection do
75 75 get 'set_exam_right(/:value)', action: 'set_exam_right', as: 'set_exam_right'
76 76 end
77 77 end
78 78
79 79 resources :users do
80 80 member do
81 81 get 'toggle_activate', 'toggle_enable'
82 82 get 'stat'
83 83 end
84 84 collection do
85 85 get 'profile'
86 86 post 'chg_passwd'
87 87 end
88 88 end
89 89
90 90 resources :submissions do
91 91 member do
92 92 get 'download'
93 93 get 'compiler_msg'
94 94 get 'rejudge'
95 95 get 'source'
96 96 end
97 97 collection do
98 98 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
99 99 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
100 100 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
101 101 end
102 102 end
103 103
104 104
105 105 #user admin
106 106 resources :user_admin do
107 107 collection do
108 108 match 'bulk_manage', via: [:get, :post]
109 109 get 'bulk_mail'
110 110 get 'user_stat'
111 111 get 'import'
112 112 get 'new_list'
113 113 get 'admin'
114 114 get 'active'
115 115 get 'mass_mailing'
116 116 get 'revoke_admin'
117 117 post 'grant_admin'
118 118 match 'create_from_list', via: [:get, :post]
119 119 match 'random_all_passwords', via: [:get, :post]
120 120 end
121 121 member do
122 122 get 'clear_last_ip'
123 123 end
124 124 end
125 125
126 126 resources :contest_management, only: [:index] do
127 127 collection do
128 128 get 'user_stat'
129 129 get 'clear_stat'
130 130 get 'clear_all_stat'
131 131 get 'change_contest_mode'
132 132 end
133 133 end
134 134
135 135 #get 'user_admin', to: 'user_admin#index'
136 136 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
137 137 #post 'user_admin', to: 'user_admin#create'
138 138 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
139 139
140 140 #singular resource
141 141 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
142 142 #report
143 143 resource :report, only: [], controller: 'report' do
144 144 get 'login'
145 145 get 'multiple_login'
146 146 get 'problem_hof(/:id)', action: 'problem_hof', as: 'problem_hof'
147 147 get 'current_score(/:group_id)', action: 'current_score', as: 'current_score'
148 148 get 'max_score'
149 149 post 'show_max_score'
150 150 get 'stuck'
151 151 get 'cheat_report'
152 152 post 'cheat_report'
153 - get 'cheat_scruntinize'
154 - post 'cheat_scruntinize'
153 + get 'cheat_scrutinize'
154 + post 'cheat_scrutinize'
155 155 get 'submission'
156 156 post 'submission_query'
157 157 get 'login_stat'
158 158 post 'login_stat'
159 159 get 'login'
160 160 post 'login_summary_query'
161 161 post 'login_detail_query'
162 162 end
163 163 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
164 164 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
165 165 #get "report/login"
166 166 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
167 167 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
168 168
169 169 resource :main, only: [], controller: 'main' do
170 170 get 'login'
171 171 get 'logout'
172 172 get 'list'
173 173 get 'submission(/:id)', action: 'submission', as: 'main_submission'
174 174 get 'announcements'
175 175 get 'help'
176 176 post 'submit'
177 177 end
178 178 #main
179 179 #get "main/list"
180 180 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
181 181 #post 'main/submit', to: 'main#submit'
182 182 #get 'main/announcements', to: 'main#announcements'
183 183
184 184
185 185 #
186 186 get 'tasks/view/:file.:ext' => 'tasks#view'
187 187 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
188 188 get 'heartbeat/:id/edit' => 'heartbeat#edit'
189 189
190 190 #grader
191 191 get 'graders/list', to: 'graders#list', as: 'grader_list'
192 192 namespace :graders do
193 193 get 'task/:id/:type', action: 'task', as: 'task'
194 194 get 'view/:id/:type', action: 'view', as: 'view'
195 195 get 'clear/:id', action: 'clear', as: 'clear'
196 196 get 'stop'
197 197 get 'stop_all'
198 198 get 'clear_all'
199 199 get 'clear_terminated'
200 200 get 'start_grading'
201 201 get 'start_exam'
202 202
203 203 end
204 204
205 205
206 206 # See how all your routes lay out with "rake routes"
207 207
208 208 # This is a legacy wild controller route that's not recommended for RESTful applications.
209 209 # Note: This route will make all actions in every controller accessible via GET requests.
210 210 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
211 211 end
deleted file
You need to be logged in to leave comments. Login now