Description:
finalize multiple_login, change default menu of report to multiple login
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r521:5a22064770a9 - - 7 files changed: 64 inserted, 17 deleted

@@ -1,277 +1,277
1 class ProblemsController < ApplicationController
1 class ProblemsController < ApplicationController
2
2
3 before_filter :authenticate, :authorization
3 before_filter :authenticate, :authorization
4
4
5 in_place_edit_for :problem, :name
5 in_place_edit_for :problem, :name
6 in_place_edit_for :problem, :full_name
6 in_place_edit_for :problem, :full_name
7 in_place_edit_for :problem, :full_score
7 in_place_edit_for :problem, :full_score
8
8
9 def index
9 def index
10 list
10 list
11 render :action => 'list'
11 render :action => 'list'
12 end
12 end
13
13
14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
15 verify :method => :post, :only => [ :destroy,
15 verify :method => :post, :only => [ :destroy,
16 :create, :quick_create,
16 :create, :quick_create,
17 :do_manage,
17 :do_manage,
18 :do_import,
18 :do_import,
19 :update ],
19 :update ],
20 :redirect_to => { :action => :list }
20 :redirect_to => { :action => :list }
21
21
22 def list
22 def list
23 @problems = Problem.find(:all, :order => 'date_added DESC')
23 @problems = Problem.find(:all, :order => 'date_added DESC')
24 end
24 end
25
25
26 def show
26 def show
27 @problem = Problem.find(params[:id])
27 @problem = Problem.find(params[:id])
28 end
28 end
29
29
30 def new
30 def new
31 @problem = Problem.new
31 @problem = Problem.new
32 @description = nil
32 @description = nil
33 end
33 end
34
34
35 def create
35 def create
36 @problem = Problem.new(params[:problem])
36 @problem = Problem.new(params[:problem])
37 @description = Description.new(params[:description])
37 @description = Description.new(params[:description])
38 if @description.body!=''
38 if @description.body!=''
39 if !@description.save
39 if !@description.save
40 render :action => new and return
40 render :action => new and return
41 end
41 end
42 else
42 else
43 @description = nil
43 @description = nil
44 end
44 end
45 @problem.description = @description
45 @problem.description = @description
46 if @problem.save
46 if @problem.save
47 flash[:notice] = 'Problem was successfully created.'
47 flash[:notice] = 'Problem was successfully created.'
48 redirect_to :action => 'list'
48 redirect_to :action => 'list'
49 else
49 else
50 render :action => 'new'
50 render :action => 'new'
51 end
51 end
52 end
52 end
53
53
54 def quick_create
54 def quick_create
55 @problem = Problem.new(params[:problem])
55 @problem = Problem.new(params[:problem])
56 @problem.full_name = @problem.name if @problem.full_name == ''
56 @problem.full_name = @problem.name if @problem.full_name == ''
57 @problem.full_score = 100
57 @problem.full_score = 100
58 @problem.available = false
58 @problem.available = false
59 @problem.test_allowed = true
59 @problem.test_allowed = true
60 @problem.output_only = false
60 @problem.output_only = false
61 @problem.date_added = Time.new
61 @problem.date_added = Time.new
62 if @problem.save
62 if @problem.save
63 flash[:notice] = 'Problem was successfully created.'
63 flash[:notice] = 'Problem was successfully created.'
64 redirect_to :action => 'list'
64 redirect_to :action => 'list'
65 else
65 else
66 flash[:notice] = 'Error saving problem'
66 flash[:notice] = 'Error saving problem'
67 redirect_to :action => 'list'
67 redirect_to :action => 'list'
68 end
68 end
69 end
69 end
70
70
71 def edit
71 def edit
72 @problem = Problem.find(params[:id])
72 @problem = Problem.find(params[:id])
73 @description = @problem.description
73 @description = @problem.description
74 end
74 end
75
75
76 def update
76 def update
77 @problem = Problem.find(params[:id])
77 @problem = Problem.find(params[:id])
78 @description = @problem.description
78 @description = @problem.description
79 if @description == nil and params[:description][:body]!=''
79 if @description == nil and params[:description][:body]!=''
80 @description = Description.new(params[:description])
80 @description = Description.new(params[:description])
81 if !@description.save
81 if !@description.save
82 flash[:notice] = 'Error saving description'
82 flash[:notice] = 'Error saving description'
83 render :action => 'edit' and return
83 render :action => 'edit' and return
84 end
84 end
85 @problem.description = @description
85 @problem.description = @description
86 elsif @description!=nil
86 elsif @description!=nil
87 if !@description.update_attributes(params[:description])
87 if !@description.update_attributes(params[:description])
88 flash[:notice] = 'Error saving description'
88 flash[:notice] = 'Error saving description'
89 render :action => 'edit' and return
89 render :action => 'edit' and return
90 end
90 end
91 end
91 end
92 if params[:file] and params[:file].content_type != 'application/pdf'
92 if params[:file] and params[:file].content_type != 'application/pdf'
93 flash[:notice] = 'Error: Uploaded file is not PDF'
93 flash[:notice] = 'Error: Uploaded file is not PDF'
94 render :action => 'edit' and return
94 render :action => 'edit' and return
95 end
95 end
96 if @problem.update_attributes(params[:problem])
96 if @problem.update_attributes(params[:problem])
97 flash[:notice] = 'Problem was successfully updated.'
97 flash[:notice] = 'Problem was successfully updated.'
98 unless params[:file] == nil or params[:file] == ''
98 unless params[:file] == nil or params[:file] == ''
99 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
99 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
100 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
100 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
101 if not FileTest.exists? out_dirname
101 if not FileTest.exists? out_dirname
102 Dir.mkdir out_dirname
102 Dir.mkdir out_dirname
103 end
103 end
104
104
105 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
105 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
106 if FileTest.exists? out_filename
106 if FileTest.exists? out_filename
107 File.delete out_filename
107 File.delete out_filename
108 end
108 end
109
109
110 File.open(out_filename,"wb") do |file|
110 File.open(out_filename,"wb") do |file|
111 file.write(params[:file].read)
111 file.write(params[:file].read)
112 end
112 end
113 @problem.description_filename = "#{@problem.name}.pdf"
113 @problem.description_filename = "#{@problem.name}.pdf"
114 @problem.save
114 @problem.save
115 end
115 end
116 redirect_to :action => 'show', :id => @problem
116 redirect_to :action => 'show', :id => @problem
117 else
117 else
118 render :action => 'edit'
118 render :action => 'edit'
119 end
119 end
120 end
120 end
121
121
122 def destroy
122 def destroy
123 Problem.find(params[:id]).destroy
123 Problem.find(params[:id]).destroy
124 redirect_to :action => 'list'
124 redirect_to :action => 'list'
125 end
125 end
126
126
127 def toggle
127 def toggle
128 @problem = Problem.find(params[:id])
128 @problem = Problem.find(params[:id])
129 @problem.available = !(@problem.available)
129 @problem.available = !(@problem.available)
130 @problem.save
130 @problem.save
131 end
131 end
132
132
133 def turn_all_off
133 def turn_all_off
134 Problem.find(:all,
134 Problem.find(:all,
135 :conditions => "available = 1").each do |problem|
135 :conditions => "available = 1").each do |problem|
136 problem.available = false
136 problem.available = false
137 problem.save
137 problem.save
138 end
138 end
139 redirect_to :action => 'list'
139 redirect_to :action => 'list'
140 end
140 end
141
141
142 def turn_all_on
142 def turn_all_on
143 Problem.find(:all,
143 Problem.find(:all,
144 :conditions => "available = 0").each do |problem|
144 :conditions => "available = 0").each do |problem|
145 problem.available = true
145 problem.available = true
146 problem.save
146 problem.save
147 end
147 end
148 redirect_to :action => 'list'
148 redirect_to :action => 'list'
149 end
149 end
150
150
151 def stat
151 def stat
152 @problem = Problem.find(params[:id])
152 @problem = Problem.find(params[:id])
153 unless @problem.available or session[:admin]
153 unless @problem.available or session[:admin]
154 redirect_to :controller => 'main', :action => 'list'
154 redirect_to :controller => 'main', :action => 'list'
155 return
155 return
156 end
156 end
157 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
157 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
158
158
159 #stat summary
159 #stat summary
160 range =65
160 range =65
161 @histogram = { data: Array.new(range,0), summary: {} }
161 @histogram = { data: Array.new(range,0), summary: {} }
162 user = Hash.new(0)
162 user = Hash.new(0)
163 @submissions.find_each do |sub|
163 @submissions.find_each do |sub|
164 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
164 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
165 @histogram[:data][d.to_i] += 1 if d < range
165 @histogram[:data][d.to_i] += 1 if d < range
166 - user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
166 + user[sub.user_id] = [user[sub.user_id], (sub.try(:points) >= @problem.full_score) ? 1 : 0].max
167 end
167 end
168 @histogram[:summary][:max] = [@histogram[:data].max,1].max
168 @histogram[:summary][:max] = [@histogram[:data].max,1].max
169
169
170 @summary = { attempt: user.count, solve: 0 }
170 @summary = { attempt: user.count, solve: 0 }
171 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
171 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
172 end
172 end
173
173
174 def manage
174 def manage
175 @problems = Problem.find(:all, :order => 'date_added DESC')
175 @problems = Problem.find(:all, :order => 'date_added DESC')
176 end
176 end
177
177
178 def do_manage
178 def do_manage
179 if params.has_key? 'change_date_added'
179 if params.has_key? 'change_date_added'
180 change_date_added
180 change_date_added
181 elsif params.has_key? 'add_to_contest'
181 elsif params.has_key? 'add_to_contest'
182 add_to_contest
182 add_to_contest
183 elsif params.has_key? 'enable_problem'
183 elsif params.has_key? 'enable_problem'
184 set_available(true)
184 set_available(true)
185 elsif params.has_key? 'disable_problem'
185 elsif params.has_key? 'disable_problem'
186 set_available(false)
186 set_available(false)
187 end
187 end
188 redirect_to :action => 'manage'
188 redirect_to :action => 'manage'
189 end
189 end
190
190
191 def import
191 def import
192 @allow_test_pair_import = allow_test_pair_import?
192 @allow_test_pair_import = allow_test_pair_import?
193 end
193 end
194
194
195 def do_import
195 def do_import
196 old_problem = Problem.find_by_name(params[:name])
196 old_problem = Problem.find_by_name(params[:name])
197 if !allow_test_pair_import? and params.has_key? :import_to_db
197 if !allow_test_pair_import? and params.has_key? :import_to_db
198 params.delete :import_to_db
198 params.delete :import_to_db
199 end
199 end
200 @problem, import_log = Problem.create_from_import_form_params(params,
200 @problem, import_log = Problem.create_from_import_form_params(params,
201 old_problem)
201 old_problem)
202
202
203 if !@problem.errors.empty?
203 if !@problem.errors.empty?
204 render :action => 'import' and return
204 render :action => 'import' and return
205 end
205 end
206
206
207 if old_problem!=nil
207 if old_problem!=nil
208 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
208 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
209 end
209 end
210 @log = import_log
210 @log = import_log
211 end
211 end
212
212
213 def remove_contest
213 def remove_contest
214 problem = Problem.find(params[:id])
214 problem = Problem.find(params[:id])
215 contest = Contest.find(params[:contest_id])
215 contest = Contest.find(params[:contest_id])
216 if problem!=nil and contest!=nil
216 if problem!=nil and contest!=nil
217 problem.contests.delete(contest)
217 problem.contests.delete(contest)
218 end
218 end
219 redirect_to :action => 'manage'
219 redirect_to :action => 'manage'
220 end
220 end
221
221
222 ##################################
222 ##################################
223 protected
223 protected
224
224
225 def allow_test_pair_import?
225 def allow_test_pair_import?
226 if defined? ALLOW_TEST_PAIR_IMPORT
226 if defined? ALLOW_TEST_PAIR_IMPORT
227 return ALLOW_TEST_PAIR_IMPORT
227 return ALLOW_TEST_PAIR_IMPORT
228 else
228 else
229 return false
229 return false
230 end
230 end
231 end
231 end
232
232
233 def change_date_added
233 def change_date_added
234 problems = get_problems_from_params
234 problems = get_problems_from_params
235 year = params[:date_added][:year].to_i
235 year = params[:date_added][:year].to_i
236 month = params[:date_added][:month].to_i
236 month = params[:date_added][:month].to_i
237 day = params[:date_added][:day].to_i
237 day = params[:date_added][:day].to_i
238 date = Date.new(year,month,day)
238 date = Date.new(year,month,day)
239 problems.each do |p|
239 problems.each do |p|
240 p.date_added = date
240 p.date_added = date
241 p.save
241 p.save
242 end
242 end
243 end
243 end
244
244
245 def add_to_contest
245 def add_to_contest
246 problems = get_problems_from_params
246 problems = get_problems_from_params
247 contest = Contest.find(params[:contest][:id])
247 contest = Contest.find(params[:contest][:id])
248 if contest!=nil and contest.enabled
248 if contest!=nil and contest.enabled
249 problems.each do |p|
249 problems.each do |p|
250 p.contests << contest
250 p.contests << contest
251 end
251 end
252 end
252 end
253 end
253 end
254
254
255 def set_available(avail)
255 def set_available(avail)
256 problems = get_problems_from_params
256 problems = get_problems_from_params
257 problems.each do |p|
257 problems.each do |p|
258 p.available = avail
258 p.available = avail
259 p.save
259 p.save
260 end
260 end
261 end
261 end
262
262
263 def get_problems_from_params
263 def get_problems_from_params
264 problems = []
264 problems = []
265 params.keys.each do |k|
265 params.keys.each do |k|
266 if k.index('prob-')==0
266 if k.index('prob-')==0
267 name, id, order = k.split('-')
267 name, id, order = k.split('-')
268 problems << Problem.find(id)
268 problems << Problem.find(id)
269 end
269 end
270 end
270 end
271 problems
271 problems
272 end
272 end
273
273
274 def get_problems_stat
274 def get_problems_stat
275 end
275 end
276
276
277 end
277 end
@@ -27,205 +27,228
27 rescue
27 rescue
28 @until_time = DateTime.new(3000,1,1)
28 @until_time = DateTime.new(3000,1,1)
29 end
29 end
30
30
31 User.all.each do |user|
31 User.all.each do |user|
32 @logins << { id: user.id,
32 @logins << { id: user.id,
33 login: user.login,
33 login: user.login,
34 full_name: user.full_name,
34 full_name: user.full_name,
35 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
35 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
36 user.id,@since_time,@until_time)
36 user.id,@since_time,@until_time)
37 .count(:id),
37 .count(:id),
38 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
38 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
39 user.id,@since_time,@until_time)
39 user.id,@since_time,@until_time)
40 .minimum(:created_at),
40 .minimum(:created_at),
41 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
41 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
42 user.id,@since_time,@until_time)
42 user.id,@since_time,@until_time)
43 .maximum(:created_at),
43 .maximum(:created_at),
44 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
44 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
45 user.id,@since_time,@until_time)
45 user.id,@since_time,@until_time)
46 .select(:ip_address).uniq
46 .select(:ip_address).uniq
47
47
48 }
48 }
49 end
49 end
50 end
50 end
51
51
52 def submission_stat
52 def submission_stat
53
53
54 date_and_time = '%Y-%m-%d %H:%M'
54 date_and_time = '%Y-%m-%d %H:%M'
55 begin
55 begin
56 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
56 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
57 rescue
57 rescue
58 @since_time = DateTime.new(1000,1,1)
58 @since_time = DateTime.new(1000,1,1)
59 end
59 end
60 begin
60 begin
61 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
61 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
62 rescue
62 rescue
63 @until_time = DateTime.new(3000,1,1)
63 @until_time = DateTime.new(3000,1,1)
64 end
64 end
65
65
66 @submissions = {}
66 @submissions = {}
67
67
68 User.find_each do |user|
68 User.find_each do |user|
69 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
69 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
70 end
70 end
71
71
72 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
72 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
73 if @submissions[s.user_id]
73 if @submissions[s.user_id]
74 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
74 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
75 a = nil
75 a = nil
76 begin
76 begin
77 a = Problem.find(s.problem_id)
77 a = Problem.find(s.problem_id)
78 rescue
78 rescue
79 a = nil
79 a = nil
80 end
80 end
81 @submissions[s.user_id][:sub][s.problem_id] =
81 @submissions[s.user_id][:sub][s.problem_id] =
82 { prob_name: (a ? a.full_name : '(NULL)'),
82 { prob_name: (a ? a.full_name : '(NULL)'),
83 sub_ids: [s.id] }
83 sub_ids: [s.id] }
84 else
84 else
85 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
85 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
86 end
86 end
87 @submissions[s.user_id][:count] += 1
87 @submissions[s.user_id][:count] += 1
88 end
88 end
89 end
89 end
90 end
90 end
91
91
92 def problem_hof
92 def problem_hof
93 # gen problem list
93 # gen problem list
94 @user = User.find(session[:user_id])
94 @user = User.find(session[:user_id])
95 @problems = @user.available_problems
95 @problems = @user.available_problems
96
96
97 # get selected problems or the default
97 # get selected problems or the default
98 if params[:id]
98 if params[:id]
99 begin
99 begin
100 @problem = Problem.available.find(params[:id])
100 @problem = Problem.available.find(params[:id])
101 rescue
101 rescue
102 redirect_to action: :problem_hof
102 redirect_to action: :problem_hof
103 flash[:notice] = 'Error: submissions for that problem are not viewable.'
103 flash[:notice] = 'Error: submissions for that problem are not viewable.'
104 return
104 return
105 end
105 end
106 end
106 end
107
107
108 return unless @problem
108 return unless @problem
109
109
110 @by_lang = {} #aggregrate by language
110 @by_lang = {} #aggregrate by language
111
111
112 range =65
112 range =65
113 @histogram = { data: Array.new(range,0), summary: {} }
113 @histogram = { data: Array.new(range,0), summary: {} }
114 @summary = {count: 0, solve: 0, attempt: 0}
114 @summary = {count: 0, solve: 0, attempt: 0}
115 user = Hash.new(0)
115 user = Hash.new(0)
116 Submission.where(problem_id: @problem.id).find_each do |sub|
116 Submission.where(problem_id: @problem.id).find_each do |sub|
117 #histogram
117 #histogram
118 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
118 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
119 @histogram[:data][d.to_i] += 1 if d < range
119 @histogram[:data][d.to_i] += 1 if d < range
120
120
121 @summary[:count] += 1
121 @summary[:count] += 1
122 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
122 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
123
123
124 lang = Language.find_by_id(sub.language_id)
124 lang = Language.find_by_id(sub.language_id)
125 next unless lang
125 next unless lang
126 next unless sub.points >= @problem.full_score
126 next unless sub.points >= @problem.full_score
127
127
128 #initialize
128 #initialize
129 unless @by_lang.has_key?(lang.pretty_name)
129 unless @by_lang.has_key?(lang.pretty_name)
130 @by_lang[lang.pretty_name] = {
130 @by_lang[lang.pretty_name] = {
131 runtime: { avail: false, value: 2**30-1 },
131 runtime: { avail: false, value: 2**30-1 },
132 memory: { avail: false, value: 2**30-1 },
132 memory: { avail: false, value: 2**30-1 },
133 length: { avail: false, value: 2**30-1 },
133 length: { avail: false, value: 2**30-1 },
134 first: { avail: false, value: DateTime.new(3000,1,1) }
134 first: { avail: false, value: DateTime.new(3000,1,1) }
135 }
135 }
136 end
136 end
137
137
138 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
138 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
139 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
139 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
140 end
140 end
141
141
142 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
142 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
143 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
143 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
144 end
144 end
145
145
146 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and
146 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and
147 !sub.user.admin?
147 !sub.user.admin?
148 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
148 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
149 end
149 end
150
150
151 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
151 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
152 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
152 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
153 end
153 end
154 end
154 end
155
155
156 #process user_id
156 #process user_id
157 @by_lang.each do |lang,prop|
157 @by_lang.each do |lang,prop|
158 prop.each do |k,v|
158 prop.each do |k,v|
159 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
159 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
160 end
160 end
161 end
161 end
162
162
163 #sum into best
163 #sum into best
164 if @by_lang and @by_lang.first
164 if @by_lang and @by_lang.first
165 @best = @by_lang.first[1].clone
165 @best = @by_lang.first[1].clone
166 @by_lang.each do |lang,prop|
166 @by_lang.each do |lang,prop|
167 if @best[:runtime][:value] >= prop[:runtime][:value]
167 if @best[:runtime][:value] >= prop[:runtime][:value]
168 @best[:runtime] = prop[:runtime]
168 @best[:runtime] = prop[:runtime]
169 @best[:runtime][:lang] = lang
169 @best[:runtime][:lang] = lang
170 end
170 end
171 if @best[:memory][:value] >= prop[:memory][:value]
171 if @best[:memory][:value] >= prop[:memory][:value]
172 @best[:memory] = prop[:memory]
172 @best[:memory] = prop[:memory]
173 @best[:memory][:lang] = lang
173 @best[:memory][:lang] = lang
174 end
174 end
175 if @best[:length][:value] >= prop[:length][:value]
175 if @best[:length][:value] >= prop[:length][:value]
176 @best[:length] = prop[:length]
176 @best[:length] = prop[:length]
177 @best[:length][:lang] = lang
177 @best[:length][:lang] = lang
178 end
178 end
179 if @best[:first][:value] >= prop[:first][:value]
179 if @best[:first][:value] >= prop[:first][:value]
180 @best[:first] = prop[:first]
180 @best[:first] = prop[:first]
181 @best[:first][:lang] = lang
181 @best[:first][:lang] = lang
182 end
182 end
183 end
183 end
184 end
184 end
185
185
186 @histogram[:summary][:max] = [@histogram[:data].max,1].max
186 @histogram[:summary][:max] = [@histogram[:data].max,1].max
187 @summary[:attempt] = user.count
187 @summary[:attempt] = user.count
188 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
188 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
189 end
189 end
190
190
191 def stuck #report struggling user,problem
191 def stuck #report struggling user,problem
192 # init
192 # init
193 user,problem = nil
193 user,problem = nil
194 solve = true
194 solve = true
195 tries = 0
195 tries = 0
196 @struggle = Array.new
196 @struggle = Array.new
197 record = {}
197 record = {}
198 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
198 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
199 next unless sub.user and sub.problem
199 next unless sub.user and sub.problem
200 if user != sub.user_id or problem != sub.problem_id
200 if user != sub.user_id or problem != sub.problem_id
201 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
201 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
202 record = {user: sub.user, problem: sub.problem}
202 record = {user: sub.user, problem: sub.problem}
203 user,problem = sub.user_id, sub.problem_id
203 user,problem = sub.user_id, sub.problem_id
204 solve = false
204 solve = false
205 tries = 0
205 tries = 0
206 end
206 end
207 if sub.points >= sub.problem.full_score
207 if sub.points >= sub.problem.full_score
208 solve = true
208 solve = true
209 else
209 else
210 tries += 1
210 tries += 1
211 end
211 end
212 end
212 end
213 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
213 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
214 @struggle = @struggle[0..50]
214 @struggle = @struggle[0..50]
215 end
215 end
216
216
217
217
218 def multiple_login
218 def multiple_login
219 + #user with multiple IP
219 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
220 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
220 last,count = 0,0
221 last,count = 0,0
221 - @multiple = []
222 + first = 0
223 + @users = []
222 raw.each do |r|
224 raw.each do |r|
223 if last != r.user.login
225 if last != r.user.login
224 count = 1
226 count = 1
227 + last = r.user.login
228 + first = r
225 else
229 else
226 - @multiple << r
230 + @users << first if count == 1
231 + @users << r
232 + count += 1
233 + end
234 + end
235 +
236 + #IP with multiple user
237 + raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:ip_address)
238 + last,count = 0,0
239 + first = 0
240 + @ip = []
241 + raw.each do |r|
242 + if last != r.ip_address
243 + count = 1
244 + last = r.ip_address
245 + first = r
246 + else
247 + @ip << first if count == 1
248 + @ip << r
249 + count += 1
227 end
250 end
228 end
251 end
229 end
252 end
230
253
231 end
254 end
@@ -1,283 +1,288
1 require 'csv'
1 require 'csv'
2
2
3 class UserAdminController < ApplicationController
3 class UserAdminController < ApplicationController
4
4
5
5
6 include MailHelperMethods
6 include MailHelperMethods
7
7
8 before_filter :admin_authorization
8 before_filter :admin_authorization
9
9
10 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
10 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
11 verify :method => :post, :only => [ :destroy,
11 verify :method => :post, :only => [ :destroy,
12 :create, :create_from_list,
12 :create, :create_from_list,
13 :update,
13 :update,
14 :manage_contest,
14 :manage_contest,
15 :bulk_mail
15 :bulk_mail
16 ],
16 ],
17 :redirect_to => { :action => :list }
17 :redirect_to => { :action => :list }
18
18
19 def index
19 def index
20 list
20 list
21 render :action => 'list'
21 render :action => 'list'
22 end
22 end
23
23
24 def list
24 def list
25 @user_count = User.count
25 @user_count = User.count
26 if params[:page] == 'all'
26 if params[:page] == 'all'
27 @users = User.all
27 @users = User.all
28 @paginated = false
28 @paginated = false
29 else
29 else
30 @users = User.paginate :page => params[:page]
30 @users = User.paginate :page => params[:page]
31 @paginated = true
31 @paginated = true
32 end
32 end
33 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
33 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
34 @contests = Contest.enabled
34 @contests = Contest.enabled
35 end
35 end
36
36
37 def active
37 def active
38 sessions = ActiveRecord::SessionStore::Session.find(:all, :conditions => ["updated_at >= ?", 60.minutes.ago])
38 sessions = ActiveRecord::SessionStore::Session.find(:all, :conditions => ["updated_at >= ?", 60.minutes.ago])
39 @users = []
39 @users = []
40 sessions.each do |session|
40 sessions.each do |session|
41 if session.data[:user_id]
41 if session.data[:user_id]
42 @users << User.find(session.data[:user_id])
42 @users << User.find(session.data[:user_id])
43 end
43 end
44 end
44 end
45 end
45 end
46
46
47 def show
47 def show
48 @user = User.find(params[:id])
48 @user = User.find(params[:id])
49 end
49 end
50
50
51 def new
51 def new
52 @user = User.new
52 @user = User.new
53 end
53 end
54
54
55 def create
55 def create
56 @user = User.new(params[:user])
56 @user = User.new(params[:user])
57 @user.activated = true
57 @user.activated = true
58 if @user.save
58 if @user.save
59 flash[:notice] = 'User was successfully created.'
59 flash[:notice] = 'User was successfully created.'
60 redirect_to :action => 'list'
60 redirect_to :action => 'list'
61 else
61 else
62 render :action => 'new'
62 render :action => 'new'
63 end
63 end
64 end
64 end
65
65
66 def create_from_list
66 def create_from_list
67 lines = params[:user_list]
67 lines = params[:user_list]
68
68
69 note = []
69 note = []
70
70
71 lines.split("\n").each do |line|
71 lines.split("\n").each do |line|
72 items = line.chomp.split(',')
72 items = line.chomp.split(',')
73 if items.length>=2
73 if items.length>=2
74 login = items[0]
74 login = items[0]
75 full_name = items[1]
75 full_name = items[1]
76
76
77 added_random_password = false
77 added_random_password = false
78 if items.length>=3
78 if items.length>=3
79 password = items[2].chomp(" ")
79 password = items[2].chomp(" ")
80 user_alias = (items.length>=4) ? items[3] : login
80 user_alias = (items.length>=4) ? items[3] : login
81 else
81 else
82 password = random_password
82 password = random_password
83 user_alias = (items.length>=4) ? items[3] : login
83 user_alias = (items.length>=4) ? items[3] : login
84 added_random_password = true
84 added_random_password = true
85 end
85 end
86
86
87 - user = User.new({:login => login,
87 + user = User.find_by_login(login)
88 - :full_name => full_name,
88 + if user
89 - :password => password,
89 + user.password = password
90 - :password_confirmation => password,
90 + else
91 - :alias => user_alias})
91 + user = User.new({:login => login,
92 + :full_name => full_name,
93 + :password => password,
94 + :password_confirmation => password,
95 + :alias => user_alias})
96 + end
92 user.activated = true
97 user.activated = true
93 user.save
98 user.save
94
99
95 if added_random_password
100 if added_random_password
96 note << "'#{login}' (+)"
101 note << "'#{login}' (+)"
97 else
102 else
98 note << login
103 note << login
99 end
104 end
100 end
105 end
101 end
106 end
102 flash[:notice] = 'User(s) ' + note.join(', ') +
107 flash[:notice] = 'User(s) ' + note.join(', ') +
103 ' were successfully created. ' +
108 ' were successfully created. ' +
104 '( (+) - created with random passwords.)'
109 '( (+) - created with random passwords.)'
105 redirect_to :action => 'list'
110 redirect_to :action => 'list'
106 end
111 end
107
112
108 def edit
113 def edit
109 @user = User.find(params[:id])
114 @user = User.find(params[:id])
110 end
115 end
111
116
112 def update
117 def update
113 @user = User.find(params[:id])
118 @user = User.find(params[:id])
114 if @user.update_attributes(params[:user])
119 if @user.update_attributes(params[:user])
115 flash[:notice] = 'User was successfully updated.'
120 flash[:notice] = 'User was successfully updated.'
116 redirect_to :action => 'show', :id => @user
121 redirect_to :action => 'show', :id => @user
117 else
122 else
118 render :action => 'edit'
123 render :action => 'edit'
119 end
124 end
120 end
125 end
121
126
122 def destroy
127 def destroy
123 User.find(params[:id]).destroy
128 User.find(params[:id]).destroy
124 redirect_to :action => 'list'
129 redirect_to :action => 'list'
125 end
130 end
126
131
127 def user_stat
132 def user_stat
128 if params[:commit] == 'download csv'
133 if params[:commit] == 'download csv'
129 @problems = Problem.all
134 @problems = Problem.all
130 else
135 else
131 @problems = Problem.find_available_problems
136 @problems = Problem.find_available_problems
132 end
137 end
133 @users = User.find(:all, :include => [:contests, :contest_stat])
138 @users = User.find(:all, :include => [:contests, :contest_stat])
134 @scorearray = Array.new
139 @scorearray = Array.new
135 @users.each do |u|
140 @users.each do |u|
136 ustat = Array.new
141 ustat = Array.new
137 ustat[0] = u
142 ustat[0] = u
138 @problems.each do |p|
143 @problems.each do |p|
139 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
144 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
140 if (sub!=nil) and (sub.points!=nil)
145 if (sub!=nil) and (sub.points!=nil)
141 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
146 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
142 else
147 else
143 ustat << [0,false]
148 ustat << [0,false]
144 end
149 end
145 end
150 end
146 @scorearray << ustat
151 @scorearray << ustat
147 end
152 end
148
153
149 if params[:commit] == 'download csv' then
154 if params[:commit] == 'download csv' then
150 csv = gen_csv_from_scorearray(@scorearray,@problems)
155 csv = gen_csv_from_scorearray(@scorearray,@problems)
151 send_data csv, filename: 'last_score.csv'
156 send_data csv, filename: 'last_score.csv'
152 else
157 else
153 render template: 'user_admin/user_stat'
158 render template: 'user_admin/user_stat'
154 end
159 end
155 end
160 end
156
161
157 def user_stat_max
162 def user_stat_max
158 if params[:commit] == 'download csv'
163 if params[:commit] == 'download csv'
159 @problems = Problem.all
164 @problems = Problem.all
160 else
165 else
161 @problems = Problem.find_available_problems
166 @problems = Problem.find_available_problems
162 end
167 end
163 @users = User.find(:all, :include => [:contests, :contest_stat])
168 @users = User.find(:all, :include => [:contests, :contest_stat])
164 @scorearray = Array.new
169 @scorearray = Array.new
165 #set up range from param
170 #set up range from param
166 since_id = params.fetch(:since_id, 0).to_i
171 since_id = params.fetch(:since_id, 0).to_i
167 until_id = params.fetch(:until_id, 0).to_i
172 until_id = params.fetch(:until_id, 0).to_i
168 @users.each do |u|
173 @users.each do |u|
169 ustat = Array.new
174 ustat = Array.new
170 ustat[0] = u
175 ustat[0] = u
171 @problems.each do |p|
176 @problems.each do |p|
172 max_points = 0
177 max_points = 0
173 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
178 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
174 max_points = sub.points if sub and sub.points and (sub.points > max_points)
179 max_points = sub.points if sub and sub.points and (sub.points > max_points)
175 end
180 end
176 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
181 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
177 end
182 end
178 @scorearray << ustat
183 @scorearray << ustat
179 end
184 end
180
185
181 if params[:commit] == 'download csv' then
186 if params[:commit] == 'download csv' then
182 csv = gen_csv_from_scorearray(@scorearray,@problems)
187 csv = gen_csv_from_scorearray(@scorearray,@problems)
183 send_data csv, filename: 'max_score.csv'
188 send_data csv, filename: 'max_score.csv'
184 else
189 else
185 render template: 'user_admin/user_stat'
190 render template: 'user_admin/user_stat'
186 end
191 end
187 end
192 end
188
193
189 def import
194 def import
190 if params[:file]==''
195 if params[:file]==''
191 flash[:notice] = 'Error importing no file'
196 flash[:notice] = 'Error importing no file'
192 redirect_to :action => 'list' and return
197 redirect_to :action => 'list' and return
193 end
198 end
194 import_from_file(params[:file])
199 import_from_file(params[:file])
195 end
200 end
196
201
197 def random_all_passwords
202 def random_all_passwords
198 users = User.find(:all)
203 users = User.find(:all)
199 @prefix = params[:prefix] || ''
204 @prefix = params[:prefix] || ''
200 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
205 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
201 @changed = false
206 @changed = false
202 if request.request_method == 'POST'
207 if request.request_method == 'POST'
203 @non_admin_users.each do |user|
208 @non_admin_users.each do |user|
204 password = random_password
209 password = random_password
205 user.password = password
210 user.password = password
206 user.password_confirmation = password
211 user.password_confirmation = password
207 user.save
212 user.save
208 end
213 end
209 @changed = true
214 @changed = true
210 end
215 end
211 end
216 end
212
217
213 # contest management
218 # contest management
214
219
215 def contests
220 def contests
216 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
221 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
217 @contests = Contest.enabled
222 @contests = Contest.enabled
218 end
223 end
219
224
220 def assign_from_list
225 def assign_from_list
221 contest_id = params[:users_contest_id]
226 contest_id = params[:users_contest_id]
222 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
227 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
223 contest = Contest.find(params[:new_contest][:id])
228 contest = Contest.find(params[:new_contest][:id])
224 if !contest
229 if !contest
225 flash[:notice] = 'Error: no contest'
230 flash[:notice] = 'Error: no contest'
226 redirect_to :action => 'contests', :id =>contest_id
231 redirect_to :action => 'contests', :id =>contest_id
227 end
232 end
228
233
229 note = []
234 note = []
230 users.each do |u|
235 users.each do |u|
231 u.contests = [contest]
236 u.contests = [contest]
232 note << u.login
237 note << u.login
233 end
238 end
234 flash[:notice] = 'User(s) ' + note.join(', ') +
239 flash[:notice] = 'User(s) ' + note.join(', ') +
235 " were successfully reassigned to #{contest.title}."
240 " were successfully reassigned to #{contest.title}."
236 redirect_to :action => 'contests', :id =>contest.id
241 redirect_to :action => 'contests', :id =>contest.id
237 end
242 end
238
243
239 def add_to_contest
244 def add_to_contest
240 user = User.find(params[:id])
245 user = User.find(params[:id])
241 contest = Contest.find(params[:contest_id])
246 contest = Contest.find(params[:contest_id])
242 if user and contest
247 if user and contest
243 user.contests << contest
248 user.contests << contest
244 end
249 end
245 redirect_to :action => 'list'
250 redirect_to :action => 'list'
246 end
251 end
247
252
248 def remove_from_contest
253 def remove_from_contest
249 user = User.find(params[:id])
254 user = User.find(params[:id])
250 contest = Contest.find(params[:contest_id])
255 contest = Contest.find(params[:contest_id])
251 if user and contest
256 if user and contest
252 user.contests.delete(contest)
257 user.contests.delete(contest)
253 end
258 end
254 redirect_to :action => 'list'
259 redirect_to :action => 'list'
255 end
260 end
256
261
257 def contest_management
262 def contest_management
258 end
263 end
259
264
260 def manage_contest
265 def manage_contest
261 contest = Contest.find(params[:contest][:id])
266 contest = Contest.find(params[:contest][:id])
262 if !contest
267 if !contest
263 flash[:notice] = 'You did not choose the contest.'
268 flash[:notice] = 'You did not choose the contest.'
264 redirect_to :action => 'contest_management' and return
269 redirect_to :action => 'contest_management' and return
265 end
270 end
266
271
267 operation = params[:operation]
272 operation = params[:operation]
268
273
269 if not ['add','remove','assign'].include? operation
274 if not ['add','remove','assign'].include? operation
270 flash[:notice] = 'You did not choose the operation to perform.'
275 flash[:notice] = 'You did not choose the operation to perform.'
271 redirect_to :action => 'contest_management' and return
276 redirect_to :action => 'contest_management' and return
272 end
277 end
273
278
274 lines = params[:login_list]
279 lines = params[:login_list]
275 if !lines or lines.blank?
280 if !lines or lines.blank?
276 flash[:notice] = 'You entered an empty list.'
281 flash[:notice] = 'You entered an empty list.'
277 redirect_to :action => 'contest_management' and return
282 redirect_to :action => 'contest_management' and return
278 end
283 end
279
284
280 note = []
285 note = []
281 users = []
286 users = []
282 lines.split("\n").each do |line|
287 lines.split("\n").each do |line|
283 user = User.find_by_login(line.chomp)
288 user = User.find_by_login(line.chomp)
@@ -1,138 +1,138
1 # Methods added to this helper will be available to all templates in the application.
1 # Methods added to this helper will be available to all templates in the application.
2 module ApplicationHelper
2 module ApplicationHelper
3
3
4 def user_header
4 def user_header
5 menu_items = ''
5 menu_items = ''
6 user = User.find(session[:user_id])
6 user = User.find(session[:user_id])
7
7
8 if (user!=nil) and (session[:admin])
8 if (user!=nil) and (session[:admin])
9 # admin menu
9 # admin menu
10 menu_items << "<b>Administrative task:</b> "
10 menu_items << "<b>Administrative task:</b> "
11 append_to menu_items, '[Announcements]', 'announcements', 'index'
11 append_to menu_items, '[Announcements]', 'announcements', 'index'
12 append_to menu_items, '[Msg console]', 'messages', 'console'
12 append_to menu_items, '[Msg console]', 'messages', 'console'
13 append_to menu_items, '[Problems]', 'problems', 'index'
13 append_to menu_items, '[Problems]', 'problems', 'index'
14 append_to menu_items, '[Users]', 'user_admin', 'index'
14 append_to menu_items, '[Users]', 'user_admin', 'index'
15 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
15 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
16 - append_to menu_items, '[Report]', 'report', 'login_stat'
16 + append_to menu_items, '[Report]', 'report', 'multiple_login'
17 append_to menu_items, '[Graders]', 'graders', 'list'
17 append_to menu_items, '[Graders]', 'graders', 'list'
18 append_to menu_items, '[Contests]', 'contest_management', 'index'
18 append_to menu_items, '[Contests]', 'contest_management', 'index'
19 append_to menu_items, '[Sites]', 'sites', 'index'
19 append_to menu_items, '[Sites]', 'sites', 'index'
20 append_to menu_items, '[System config]', 'configurations', 'index'
20 append_to menu_items, '[System config]', 'configurations', 'index'
21 menu_items << "<br/>"
21 menu_items << "<br/>"
22 end
22 end
23
23
24 # main page
24 # main page
25 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
25 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
26 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
26 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
27
27
28 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
28 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
29 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
29 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
30 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
30 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
31 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
31 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
32 end
32 end
33
33
34 if GraderConfiguration['right.user_hall_of_fame']
34 if GraderConfiguration['right.user_hall_of_fame']
35 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
35 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
36 end
36 end
37 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
37 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
38
38
39 if GraderConfiguration['system.user_setting_enabled']
39 if GraderConfiguration['system.user_setting_enabled']
40 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
40 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
41 end
41 end
42 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
42 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
43
43
44 menu_items.html_safe
44 menu_items.html_safe
45 end
45 end
46
46
47 def append_to(option,label, controller, action)
47 def append_to(option,label, controller, action)
48 option << ' ' if option!=''
48 option << ' ' if option!=''
49 option << link_to_unless_current(label,
49 option << link_to_unless_current(label,
50 :controller => controller,
50 :controller => controller,
51 :action => action)
51 :action => action)
52 end
52 end
53
53
54 def format_short_time(time)
54 def format_short_time(time)
55 now = Time.now.gmtime
55 now = Time.now.gmtime
56 st = ''
56 st = ''
57 if (time.yday != now.yday) or
57 if (time.yday != now.yday) or
58 (time.year != now.year)
58 (time.year != now.year)
59 st = time.strftime("%x ")
59 st = time.strftime("%x ")
60 end
60 end
61 st + time.strftime("%X")
61 st + time.strftime("%X")
62 end
62 end
63
63
64 def format_short_duration(duration)
64 def format_short_duration(duration)
65 return '' if duration==nil
65 return '' if duration==nil
66 d = duration.to_f
66 d = duration.to_f
67 return Time.at(d).gmtime.strftime("%X")
67 return Time.at(d).gmtime.strftime("%X")
68 end
68 end
69
69
70 def read_textfile(fname,max_size=2048)
70 def read_textfile(fname,max_size=2048)
71 begin
71 begin
72 File.open(fname).read(max_size)
72 File.open(fname).read(max_size)
73 rescue
73 rescue
74 nil
74 nil
75 end
75 end
76 end
76 end
77
77
78 def user_title_bar(user)
78 def user_title_bar(user)
79 header = ''
79 header = ''
80 time_left = ''
80 time_left = ''
81
81
82 #
82 #
83 # if the contest is over
83 # if the contest is over
84 if GraderConfiguration.time_limit_mode?
84 if GraderConfiguration.time_limit_mode?
85 if user.contest_finished?
85 if user.contest_finished?
86 header = <<CONTEST_OVER
86 header = <<CONTEST_OVER
87 <tr><td colspan="2" align="center">
87 <tr><td colspan="2" align="center">
88 <span class="contest-over-msg">THE CONTEST IS OVER</span>
88 <span class="contest-over-msg">THE CONTEST IS OVER</span>
89 </td></tr>
89 </td></tr>
90 CONTEST_OVER
90 CONTEST_OVER
91 end
91 end
92 if !user.contest_started?
92 if !user.contest_started?
93 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
93 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
94 else
94 else
95 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
95 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
96 " #{format_short_duration(user.contest_time_left)}"
96 " #{format_short_duration(user.contest_time_left)}"
97 end
97 end
98 end
98 end
99
99
100 #
100 #
101 # if the contest is in the anaysis mode
101 # if the contest is in the anaysis mode
102 if GraderConfiguration.analysis_mode?
102 if GraderConfiguration.analysis_mode?
103 header = <<ANALYSISMODE
103 header = <<ANALYSISMODE
104 <tr><td colspan="2" align="center">
104 <tr><td colspan="2" align="center">
105 <span class="contest-over-msg">ANALYSIS MODE</span>
105 <span class="contest-over-msg">ANALYSIS MODE</span>
106 </td></tr>
106 </td></tr>
107 ANALYSISMODE
107 ANALYSISMODE
108 end
108 end
109
109
110 contest_name = GraderConfiguration['contest.name']
110 contest_name = GraderConfiguration['contest.name']
111
111
112 #
112 #
113 # build real title bar
113 # build real title bar
114 result = <<TITLEBAR
114 result = <<TITLEBAR
115 <div class="title">
115 <div class="title">
116 <table>
116 <table>
117 #{header}
117 #{header}
118 <tr>
118 <tr>
119 <td class="left-col">
119 <td class="left-col">
120 #{user.full_name}<br/>
120 #{user.full_name}<br/>
121 #{t 'title_bar.current_time'} #{format_short_time(Time.new)}
121 #{t 'title_bar.current_time'} #{format_short_time(Time.new)}
122 #{time_left}
122 #{time_left}
123 <br/>
123 <br/>
124 </td>
124 </td>
125 <td class="right-col">#{contest_name}</td>
125 <td class="right-col">#{contest_name}</td>
126 </tr>
126 </tr>
127 </table>
127 </table>
128 </div>
128 </div>
129 TITLEBAR
129 TITLEBAR
130 result.html_safe
130 result.html_safe
131 end
131 end
132
132
133 def markdown(text)
133 def markdown(text)
134 markdown = RDiscount.new(text)
134 markdown = RDiscount.new(text)
135 markdown.to_html.html_safe
135 markdown.to_html.html_safe
136 end
136 end
137
137
138 end
138 end
@@ -1,266 +1,266
1 require 'digest/sha1'
1 require 'digest/sha1'
2 require 'net/pop'
2 require 'net/pop'
3 require 'net/https'
3 require 'net/https'
4 require 'net/http'
4 require 'net/http'
5 require 'json'
5 require 'json'
6
6
7 class User < ActiveRecord::Base
7 class User < ActiveRecord::Base
8
8
9 has_and_belongs_to_many :roles
9 has_and_belongs_to_many :roles
10
10
11 has_many :test_requests, :order => "submitted_at DESC"
11 has_many :test_requests, :order => "submitted_at DESC"
12
12
13 has_many :messages,
13 has_many :messages,
14 :class_name => "Message",
14 :class_name => "Message",
15 :foreign_key => "sender_id",
15 :foreign_key => "sender_id",
16 :order => 'created_at DESC'
16 :order => 'created_at DESC'
17
17
18 has_many :replied_messages,
18 has_many :replied_messages,
19 :class_name => "Message",
19 :class_name => "Message",
20 :foreign_key => "receiver_id",
20 :foreign_key => "receiver_id",
21 :order => 'created_at DESC'
21 :order => 'created_at DESC'
22
22
23 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
23 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
24
24
25 belongs_to :site
25 belongs_to :site
26 belongs_to :country
26 belongs_to :country
27
27
28 has_and_belongs_to_many :contests, :uniq => true, :order => 'name'
28 has_and_belongs_to_many :contests, :uniq => true, :order => 'name'
29
29
30 scope :activated_users, :conditions => {:activated => true}
30 scope :activated_users, :conditions => {:activated => true}
31
31
32 validates_presence_of :login
32 validates_presence_of :login
33 validates_uniqueness_of :login
33 validates_uniqueness_of :login
34 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
34 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
35 validates_length_of :login, :within => 3..30
35 validates_length_of :login, :within => 3..30
36
36
37 validates_presence_of :full_name
37 validates_presence_of :full_name
38 validates_length_of :full_name, :minimum => 1
38 validates_length_of :full_name, :minimum => 1
39
39
40 validates_presence_of :password, :if => :password_required?
40 validates_presence_of :password, :if => :password_required?
41 validates_length_of :password, :within => 4..20, :if => :password_required?
41 validates_length_of :password, :within => 4..20, :if => :password_required?
42 validates_confirmation_of :password, :if => :password_required?
42 validates_confirmation_of :password, :if => :password_required?
43
43
44 validates_format_of :email,
44 validates_format_of :email,
45 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
45 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
46 :if => :email_validation?
46 :if => :email_validation?
47 validate :uniqueness_of_email_from_activated_users,
47 validate :uniqueness_of_email_from_activated_users,
48 :if => :email_validation?
48 :if => :email_validation?
49 validate :enough_time_interval_between_same_email_registrations,
49 validate :enough_time_interval_between_same_email_registrations,
50 :if => :email_validation?
50 :if => :email_validation?
51
51
52 # these are for ytopc
52 # these are for ytopc
53 # disable for now
53 # disable for now
54 #validates_presence_of :province
54 #validates_presence_of :province
55
55
56 attr_accessor :password
56 attr_accessor :password
57
57
58 before_save :encrypt_new_password
58 before_save :encrypt_new_password
59 before_save :assign_default_site
59 before_save :assign_default_site
60 before_save :assign_default_contest
60 before_save :assign_default_contest
61
61
62 # this is for will_paginate
62 # this is for will_paginate
63 cattr_reader :per_page
63 cattr_reader :per_page
64 @@per_page = 50
64 @@per_page = 50
65
65
66 def self.authenticate(login, password)
66 def self.authenticate(login, password)
67 user = find_by_login(login)
67 user = find_by_login(login)
68 if user
68 if user
69 return user if user.authenticated?(password)
69 return user if user.authenticated?(password)
70 - if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
70 + # if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
71 - user.password = password
71 + # user.password = password
72 - user.save
72 + # user.save
73 - return user
73 + # return user
74 - end
74 + # end
75 end
75 end
76 end
76 end
77
77
78 def authenticated?(password)
78 def authenticated?(password)
79 if self.activated
79 if self.activated
80 hashed_password == User.encrypt(password,self.salt)
80 hashed_password == User.encrypt(password,self.salt)
81 else
81 else
82 false
82 false
83 end
83 end
84 end
84 end
85
85
86 def authenticated_by_pop3?(password)
86 def authenticated_by_pop3?(password)
87 Net::POP3.enable_ssl
87 Net::POP3.enable_ssl
88 pop = Net::POP3.new('pops.it.chula.ac.th')
88 pop = Net::POP3.new('pops.it.chula.ac.th')
89 authen = true
89 authen = true
90 begin
90 begin
91 pop.start(login, password)
91 pop.start(login, password)
92 pop.finish
92 pop.finish
93 return true
93 return true
94 rescue
94 rescue
95 return false
95 return false
96 end
96 end
97 end
97 end
98
98
99 def authenticated_by_cucas?(password)
99 def authenticated_by_cucas?(password)
100 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
100 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
101 appid = '41508763e340d5858c00f8c1a0f5a2bb'
101 appid = '41508763e340d5858c00f8c1a0f5a2bb'
102 appsecret ='d9cbb5863091dbe186fded85722a1e31'
102 appsecret ='d9cbb5863091dbe186fded85722a1e31'
103 post_args = {
103 post_args = {
104 'appid' => appid,
104 'appid' => appid,
105 'appsecret' => appsecret,
105 'appsecret' => appsecret,
106 'username' => login,
106 'username' => login,
107 'password' => password
107 'password' => password
108 }
108 }
109
109
110 #simple call
110 #simple call
111 begin
111 begin
112 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
112 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
113 http.use_ssl = true
113 http.use_ssl = true
114 result = [ ]
114 result = [ ]
115 http.start do |http|
115 http.start do |http|
116 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
116 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
117 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
117 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
118 resp = http.request(req,param)
118 resp = http.request(req,param)
119 result = JSON.parse resp.body
119 result = JSON.parse resp.body
120 end
120 end
121 return true if result["type"] == "beanStudent"
121 return true if result["type"] == "beanStudent"
122 rescue
122 rescue
123 return false
123 return false
124 end
124 end
125 return false
125 return false
126 end
126 end
127
127
128 def admin?
128 def admin?
129 self.roles.detect {|r| r.name == 'admin' }
129 self.roles.detect {|r| r.name == 'admin' }
130 end
130 end
131
131
132 def email_for_editing
132 def email_for_editing
133 if self.email==nil
133 if self.email==nil
134 "(unknown)"
134 "(unknown)"
135 elsif self.email==''
135 elsif self.email==''
136 "(blank)"
136 "(blank)"
137 else
137 else
138 self.email
138 self.email
139 end
139 end
140 end
140 end
141
141
142 def email_for_editing=(e)
142 def email_for_editing=(e)
143 self.email=e
143 self.email=e
144 end
144 end
145
145
146 def alias_for_editing
146 def alias_for_editing
147 if self.alias==nil
147 if self.alias==nil
148 "(unknown)"
148 "(unknown)"
149 elsif self.alias==''
149 elsif self.alias==''
150 "(blank)"
150 "(blank)"
151 else
151 else
152 self.alias
152 self.alias
153 end
153 end
154 end
154 end
155
155
156 def alias_for_editing=(e)
156 def alias_for_editing=(e)
157 self.alias=e
157 self.alias=e
158 end
158 end
159
159
160 def activation_key
160 def activation_key
161 if self.hashed_password==nil
161 if self.hashed_password==nil
162 encrypt_new_password
162 encrypt_new_password
163 end
163 end
164 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
164 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
165 end
165 end
166
166
167 def verify_activation_key(key)
167 def verify_activation_key(key)
168 key == activation_key
168 key == activation_key
169 end
169 end
170
170
171 def self.random_password(length=5)
171 def self.random_password(length=5)
172 chars = 'abcdefghjkmnopqrstuvwxyz'
172 chars = 'abcdefghjkmnopqrstuvwxyz'
173 password = ''
173 password = ''
174 length.times { password << chars[rand(chars.length - 1)] }
174 length.times { password << chars[rand(chars.length - 1)] }
175 password
175 password
176 end
176 end
177
177
178 def self.find_non_admin_with_prefix(prefix='')
178 def self.find_non_admin_with_prefix(prefix='')
179 users = User.find(:all)
179 users = User.find(:all)
180 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
180 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
181 end
181 end
182
182
183 # Contest information
183 # Contest information
184
184
185 def self.find_users_with_no_contest()
185 def self.find_users_with_no_contest()
186 users = User.find(:all)
186 users = User.find(:all)
187 return users.find_all { |u| u.contests.length == 0 }
187 return users.find_all { |u| u.contests.length == 0 }
188 end
188 end
189
189
190
190
191 def contest_time_left
191 def contest_time_left
192 if GraderConfiguration.contest_mode?
192 if GraderConfiguration.contest_mode?
193 return nil if site==nil
193 return nil if site==nil
194 return site.time_left
194 return site.time_left
195 elsif GraderConfiguration.indv_contest_mode?
195 elsif GraderConfiguration.indv_contest_mode?
196 time_limit = GraderConfiguration.contest_time_limit
196 time_limit = GraderConfiguration.contest_time_limit
197 if time_limit == nil
197 if time_limit == nil
198 return nil
198 return nil
199 end
199 end
200 if contest_stat==nil or contest_stat.started_at==nil
200 if contest_stat==nil or contest_stat.started_at==nil
201 return (Time.now.gmtime + time_limit) - Time.now.gmtime
201 return (Time.now.gmtime + time_limit) - Time.now.gmtime
202 else
202 else
203 finish_time = contest_stat.started_at + time_limit
203 finish_time = contest_stat.started_at + time_limit
204 current_time = Time.now.gmtime
204 current_time = Time.now.gmtime
205 if current_time > finish_time
205 if current_time > finish_time
206 return 0
206 return 0
207 else
207 else
208 return finish_time - current_time
208 return finish_time - current_time
209 end
209 end
210 end
210 end
211 else
211 else
212 return nil
212 return nil
213 end
213 end
214 end
214 end
215
215
216 def contest_finished?
216 def contest_finished?
217 if GraderConfiguration.contest_mode?
217 if GraderConfiguration.contest_mode?
218 return false if site==nil
218 return false if site==nil
219 return site.finished?
219 return site.finished?
220 elsif GraderConfiguration.indv_contest_mode?
220 elsif GraderConfiguration.indv_contest_mode?
221 return false if self.contest_stat(true)==nil
221 return false if self.contest_stat(true)==nil
222 return contest_time_left == 0
222 return contest_time_left == 0
223 else
223 else
224 return false
224 return false
225 end
225 end
226 end
226 end
227
227
228 def contest_started?
228 def contest_started?
229 if GraderConfiguration.indv_contest_mode?
229 if GraderConfiguration.indv_contest_mode?
230 stat = self.contest_stat
230 stat = self.contest_stat
231 return ((stat != nil) and (stat.started_at != nil))
231 return ((stat != nil) and (stat.started_at != nil))
232 elsif GraderConfiguration.contest_mode?
232 elsif GraderConfiguration.contest_mode?
233 return true if site==nil
233 return true if site==nil
234 return site.started
234 return site.started
235 else
235 else
236 return true
236 return true
237 end
237 end
238 end
238 end
239
239
240 def update_start_time
240 def update_start_time
241 stat = self.contest_stat
241 stat = self.contest_stat
242 if stat == nil or stat.started_at == nil
242 if stat == nil or stat.started_at == nil
243 stat ||= UserContestStat.new(:user => self)
243 stat ||= UserContestStat.new(:user => self)
244 stat.started_at = Time.now.gmtime
244 stat.started_at = Time.now.gmtime
245 stat.save
245 stat.save
246 end
246 end
247 end
247 end
248
248
249 def problem_in_user_contests?(problem)
249 def problem_in_user_contests?(problem)
250 problem_contests = problem.contests.all
250 problem_contests = problem.contests.all
251
251
252 if problem_contests.length == 0 # this is public contest
252 if problem_contests.length == 0 # this is public contest
253 return true
253 return true
254 end
254 end
255
255
256 contests.each do |contest|
256 contests.each do |contest|
257 if problem_contests.find {|c| c.id == contest.id }
257 if problem_contests.find {|c| c.id == contest.id }
258 return true
258 return true
259 end
259 end
260 end
260 end
261 return false
261 return false
262 end
262 end
263
263
264 def available_problems_group_by_contests
264 def available_problems_group_by_contests
265 contest_problems = []
265 contest_problems = []
266 pin = {}
266 pin = {}
@@ -1,51 +1,53
1 :css
1 :css
2 .fix-width {
2 .fix-width {
3 font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier"
3 font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier"
4 }
4 }
5
5
6 %h1 Problem stat: #{@problem.name}
6 %h1 Problem stat: #{@problem.name}
7 %h2 Overview
7 %h2 Overview
8
8
9
9
10 %table.info
10 %table.info
11 %thead
11 %thead
12 %tr.info-head
12 %tr.info-head
13 %th Stat
13 %th Stat
14 %th Value
14 %th Value
15 %tbody
15 %tbody
16 %tr{class: cycle('info-even','info-odd')}
16 %tr{class: cycle('info-even','info-odd')}
17 %td Submissions
17 %td Submissions
18 %td= @submissions.count
18 %td= @submissions.count
19 %tr{class: cycle('info-even','info-odd')}
19 %tr{class: cycle('info-even','info-odd')}
20 %td Solved/Attempted User
20 %td Solved/Attempted User
21 %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
21 %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
22
22
23 %h2 Submissions Count
23 %h2 Submissions Count
24 = render partial: 'application/bar_graph', locals: { histogram: @histogram }
24 = render partial: 'application/bar_graph', locals: { histogram: @histogram }
25
25
26 %h2 Submissions
26 %h2 Submissions
27 - if @submissions and @submissions.count > 0
27 - if @submissions and @submissions.count > 0
28 %table.info#main_table
28 %table.info#main_table
29 %thead
29 %thead
30 %tr.info-head
30 %tr.info-head
31 %th ID
31 %th ID
32 %th Login
32 %th Login
33 %th Name
33 %th Name
34 %th Submitted_at
34 %th Submitted_at
35 %th Points
35 %th Points
36 %th comment
36 %th comment
37 + %th IP
37 %tbody
38 %tbody
38 - row_odd,curr = true,''
39 - row_odd,curr = true,''
39 - @submissions.each do |sub|
40 - @submissions.each do |sub|
40 - next unless sub.user
41 - next unless sub.user
41 - row_odd,curr = !row_odd, sub.user if curr != sub.user
42 - row_odd,curr = !row_odd, sub.user if curr != sub.user
42 %tr{class: row_odd ? "info-odd" : "info-even"}
43 %tr{class: row_odd ? "info-odd" : "info-even"}
43 %td= link_to sub.id, controller: 'graders', action: 'submission', id: sub.id
44 %td= link_to sub.id, controller: 'graders', action: 'submission', id: sub.id
44 %td= link_to sub.user.login, controller: :users, action: :profile, id: sub.user.id
45 %td= link_to sub.user.login, controller: :users, action: :profile, id: sub.user.id
45 %td= sub.user.full_name
46 %td= sub.user.full_name
46 %td= time_ago_in_words(sub.submitted_at) + " ago"
47 %td= time_ago_in_words(sub.submitted_at) + " ago"
47 %td= sub.points
48 %td= sub.points
48 %td.fix-width= sub.grader_comment
49 %td.fix-width= sub.grader_comment
50 + %td= sub.ip_address
49 - else
51 - else
50 No submission
52 No submission
51
53
@@ -1,20 +1,37
1 - content_for :header do
1 - content_for :header do
2 = stylesheet_link_tag 'tablesorter-theme.cafe'
2 = stylesheet_link_tag 'tablesorter-theme.cafe'
3 = javascript_include_tag 'local_jquery'
3 = javascript_include_tag 'local_jquery'
4
4
5 %h1 Login status
5 %h1 Login status
6
6
7 =render partial: 'report_menu'
7 =render partial: 'report_menu'
8
8
9 + Checking for all submissions with the currently available problem
10 +
11 + %h2 Users with Multiple IP
9 %table.tablesorter-cafe#my_table
12 %table.tablesorter-cafe#my_table
10 %thead
13 %thead
11 %tr
14 %tr
12 %th login
15 %th login
13 %th full name
16 %th full name
14 %th IP
17 %th IP
15 %tbody
18 %tbody
16 - - @multiple.each do |l|
19 + - @users.each do |l|
17 %tr{class: cycle('info-even','info-odd')}
20 %tr{class: cycle('info-even','info-odd')}
18 - %td= link_to l[:login], controller: 'users', action: 'profile', id: l[:id]
21 + %td= link_to l.user.login, controller: 'users', action: 'profile', id: l[:id]
19 - %td= l[:full_name]
22 + %td= l.user.full_name
20 %td= l[:ip_address]
23 %td= l[:ip_address]
24 +
25 + %h2 IP with multiple users
26 + %table.tablesorter-cafe#my_table
27 + %thead
28 + %tr
29 + %th IP
30 + %th login
31 + %th full name
32 + %tbody
33 + - @ip.each do |l|
34 + %tr{class: cycle('info-even','info-odd')}
35 + %td= l[:ip_address]
36 + %td= link_to l.user.login, controller: 'users', action: 'profile', id: l[:id]
37 + %td= l.user.full_name
You need to be logged in to leave comments. Login now