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 1 class ProblemsController < ApplicationController
2 2
3 3 before_filter :authenticate, :authorization
4 4
5 5 in_place_edit_for :problem, :name
6 6 in_place_edit_for :problem, :full_name
7 7 in_place_edit_for :problem, :full_score
8 8
9 9 def index
10 10 list
11 11 render :action => 'list'
12 12 end
13 13
14 14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
15 15 verify :method => :post, :only => [ :destroy,
16 16 :create, :quick_create,
17 17 :do_manage,
18 18 :do_import,
19 19 :update ],
20 20 :redirect_to => { :action => :list }
21 21
22 22 def list
23 23 @problems = Problem.find(:all, :order => 'date_added DESC')
24 24 end
25 25
26 26 def show
27 27 @problem = Problem.find(params[:id])
28 28 end
29 29
30 30 def new
31 31 @problem = Problem.new
32 32 @description = nil
33 33 end
34 34
35 35 def create
36 36 @problem = Problem.new(params[:problem])
37 37 @description = Description.new(params[:description])
38 38 if @description.body!=''
39 39 if !@description.save
40 40 render :action => new and return
41 41 end
42 42 else
43 43 @description = nil
44 44 end
45 45 @problem.description = @description
46 46 if @problem.save
47 47 flash[:notice] = 'Problem was successfully created.'
48 48 redirect_to :action => 'list'
49 49 else
50 50 render :action => 'new'
51 51 end
52 52 end
53 53
54 54 def quick_create
55 55 @problem = Problem.new(params[:problem])
56 56 @problem.full_name = @problem.name if @problem.full_name == ''
57 57 @problem.full_score = 100
58 58 @problem.available = false
59 59 @problem.test_allowed = true
60 60 @problem.output_only = false
61 61 @problem.date_added = Time.new
62 62 if @problem.save
63 63 flash[:notice] = 'Problem was successfully created.'
64 64 redirect_to :action => 'list'
65 65 else
66 66 flash[:notice] = 'Error saving problem'
67 67 redirect_to :action => 'list'
68 68 end
69 69 end
70 70
71 71 def edit
72 72 @problem = Problem.find(params[:id])
73 73 @description = @problem.description
74 74 end
75 75
76 76 def update
77 77 @problem = Problem.find(params[:id])
78 78 @description = @problem.description
79 79 if @description == nil and params[:description][:body]!=''
80 80 @description = Description.new(params[:description])
81 81 if !@description.save
82 82 flash[:notice] = 'Error saving description'
83 83 render :action => 'edit' and return
84 84 end
85 85 @problem.description = @description
86 86 elsif @description!=nil
87 87 if !@description.update_attributes(params[:description])
88 88 flash[:notice] = 'Error saving description'
89 89 render :action => 'edit' and return
90 90 end
91 91 end
92 92 if params[:file] and params[:file].content_type != 'application/pdf'
93 93 flash[:notice] = 'Error: Uploaded file is not PDF'
94 94 render :action => 'edit' and return
95 95 end
96 96 if @problem.update_attributes(params[:problem])
97 97 flash[:notice] = 'Problem was successfully updated.'
98 98 unless params[:file] == nil or params[:file] == ''
99 99 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
100 100 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
101 101 if not FileTest.exists? out_dirname
102 102 Dir.mkdir out_dirname
103 103 end
104 104
105 105 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
106 106 if FileTest.exists? out_filename
107 107 File.delete out_filename
108 108 end
109 109
110 110 File.open(out_filename,"wb") do |file|
111 111 file.write(params[:file].read)
112 112 end
113 113 @problem.description_filename = "#{@problem.name}.pdf"
114 114 @problem.save
115 115 end
116 116 redirect_to :action => 'show', :id => @problem
117 117 else
118 118 render :action => 'edit'
119 119 end
120 120 end
121 121
122 122 def destroy
123 123 Problem.find(params[:id]).destroy
124 124 redirect_to :action => 'list'
125 125 end
126 126
127 127 def toggle
128 128 @problem = Problem.find(params[:id])
129 129 @problem.available = !(@problem.available)
130 130 @problem.save
131 131 end
132 132
133 133 def turn_all_off
134 134 Problem.find(:all,
135 135 :conditions => "available = 1").each do |problem|
136 136 problem.available = false
137 137 problem.save
138 138 end
139 139 redirect_to :action => 'list'
140 140 end
141 141
142 142 def turn_all_on
143 143 Problem.find(:all,
144 144 :conditions => "available = 0").each do |problem|
145 145 problem.available = true
146 146 problem.save
147 147 end
148 148 redirect_to :action => 'list'
149 149 end
150 150
151 151 def stat
152 152 @problem = Problem.find(params[:id])
153 153 unless @problem.available or session[:admin]
154 154 redirect_to :controller => 'main', :action => 'list'
155 155 return
156 156 end
157 157 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
158 158
159 159 #stat summary
160 160 range =65
161 161 @histogram = { data: Array.new(range,0), summary: {} }
162 162 user = Hash.new(0)
163 163 @submissions.find_each do |sub|
164 164 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
165 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 167 end
168 168 @histogram[:summary][:max] = [@histogram[:data].max,1].max
169 169
170 170 @summary = { attempt: user.count, solve: 0 }
171 171 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
172 172 end
173 173
174 174 def manage
175 175 @problems = Problem.find(:all, :order => 'date_added DESC')
176 176 end
177 177
178 178 def do_manage
179 179 if params.has_key? 'change_date_added'
180 180 change_date_added
181 181 elsif params.has_key? 'add_to_contest'
182 182 add_to_contest
183 183 elsif params.has_key? 'enable_problem'
184 184 set_available(true)
185 185 elsif params.has_key? 'disable_problem'
186 186 set_available(false)
187 187 end
188 188 redirect_to :action => 'manage'
189 189 end
190 190
191 191 def import
192 192 @allow_test_pair_import = allow_test_pair_import?
193 193 end
194 194
195 195 def do_import
196 196 old_problem = Problem.find_by_name(params[:name])
197 197 if !allow_test_pair_import? and params.has_key? :import_to_db
198 198 params.delete :import_to_db
199 199 end
200 200 @problem, import_log = Problem.create_from_import_form_params(params,
201 201 old_problem)
202 202
203 203 if !@problem.errors.empty?
204 204 render :action => 'import' and return
205 205 end
206 206
207 207 if old_problem!=nil
208 208 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
209 209 end
210 210 @log = import_log
211 211 end
212 212
213 213 def remove_contest
214 214 problem = Problem.find(params[:id])
215 215 contest = Contest.find(params[:contest_id])
216 216 if problem!=nil and contest!=nil
217 217 problem.contests.delete(contest)
218 218 end
219 219 redirect_to :action => 'manage'
220 220 end
221 221
222 222 ##################################
223 223 protected
224 224
225 225 def allow_test_pair_import?
226 226 if defined? ALLOW_TEST_PAIR_IMPORT
227 227 return ALLOW_TEST_PAIR_IMPORT
228 228 else
229 229 return false
230 230 end
231 231 end
232 232
233 233 def change_date_added
234 234 problems = get_problems_from_params
235 235 year = params[:date_added][:year].to_i
236 236 month = params[:date_added][:month].to_i
237 237 day = params[:date_added][:day].to_i
238 238 date = Date.new(year,month,day)
239 239 problems.each do |p|
240 240 p.date_added = date
241 241 p.save
242 242 end
243 243 end
244 244
245 245 def add_to_contest
246 246 problems = get_problems_from_params
247 247 contest = Contest.find(params[:contest][:id])
248 248 if contest!=nil and contest.enabled
249 249 problems.each do |p|
250 250 p.contests << contest
251 251 end
252 252 end
253 253 end
254 254
255 255 def set_available(avail)
256 256 problems = get_problems_from_params
257 257 problems.each do |p|
258 258 p.available = avail
259 259 p.save
260 260 end
261 261 end
262 262
263 263 def get_problems_from_params
264 264 problems = []
265 265 params.keys.each do |k|
266 266 if k.index('prob-')==0
267 267 name, id, order = k.split('-')
268 268 problems << Problem.find(id)
269 269 end
270 270 end
271 271 problems
272 272 end
273 273
274 274 def get_problems_stat
275 275 end
276 276
277 277 end
@@ -1,231 +1,254
1 1 class ReportController < ApplicationController
2 2
3 3 before_filter :admin_authorization, only: [:login_stat,:submission_stat]
4 4 before_filter(only: [:problem_hof]) { |c|
5 5 return false unless authenticate
6 6
7 7 if GraderConfiguration["right.user_view_submission"]
8 8 return true;
9 9 end
10 10
11 11 admin_authorization
12 12 }
13 13
14 14 def login_stat
15 15 @logins = Array.new
16 16
17 17 date_and_time = '%Y-%m-%d %H:%M'
18 18 begin
19 19 md = params[:since_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
20 20 @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)
21 21 rescue
22 22 @since_time = DateTime.new(1000,1,1)
23 23 end
24 24 begin
25 25 md = params[:until_datetime].match(/(\d+)-(\d+)-(\d+) (\d+):(\d+)/)
26 26 @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)
27 27 rescue
28 28 @until_time = DateTime.new(3000,1,1)
29 29 end
30 30
31 31 User.all.each do |user|
32 32 @logins << { id: user.id,
33 33 login: user.login,
34 34 full_name: user.full_name,
35 35 count: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
36 36 user.id,@since_time,@until_time)
37 37 .count(:id),
38 38 min: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
39 39 user.id,@since_time,@until_time)
40 40 .minimum(:created_at),
41 41 max: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
42 42 user.id,@since_time,@until_time)
43 43 .maximum(:created_at),
44 44 ip: Login.where("user_id = ? AND created_at >= ? AND created_at <= ?",
45 45 user.id,@since_time,@until_time)
46 46 .select(:ip_address).uniq
47 47
48 48 }
49 49 end
50 50 end
51 51
52 52 def submission_stat
53 53
54 54 date_and_time = '%Y-%m-%d %H:%M'
55 55 begin
56 56 @since_time = DateTime.strptime(params[:since_datetime],date_and_time)
57 57 rescue
58 58 @since_time = DateTime.new(1000,1,1)
59 59 end
60 60 begin
61 61 @until_time = DateTime.strptime(params[:until_datetime],date_and_time)
62 62 rescue
63 63 @until_time = DateTime.new(3000,1,1)
64 64 end
65 65
66 66 @submissions = {}
67 67
68 68 User.find_each do |user|
69 69 @submissions[user.id] = { login: user.login, full_name: user.full_name, count: 0, sub: { } }
70 70 end
71 71
72 72 Submission.where("submitted_at >= ? AND submitted_at <= ?",@since_time,@until_time).find_each do |s|
73 73 if @submissions[s.user_id]
74 74 if not @submissions[s.user_id][:sub].has_key?(s.problem_id)
75 75 a = nil
76 76 begin
77 77 a = Problem.find(s.problem_id)
78 78 rescue
79 79 a = nil
80 80 end
81 81 @submissions[s.user_id][:sub][s.problem_id] =
82 82 { prob_name: (a ? a.full_name : '(NULL)'),
83 83 sub_ids: [s.id] }
84 84 else
85 85 @submissions[s.user_id][:sub][s.problem_id][:sub_ids] << s.id
86 86 end
87 87 @submissions[s.user_id][:count] += 1
88 88 end
89 89 end
90 90 end
91 91
92 92 def problem_hof
93 93 # gen problem list
94 94 @user = User.find(session[:user_id])
95 95 @problems = @user.available_problems
96 96
97 97 # get selected problems or the default
98 98 if params[:id]
99 99 begin
100 100 @problem = Problem.available.find(params[:id])
101 101 rescue
102 102 redirect_to action: :problem_hof
103 103 flash[:notice] = 'Error: submissions for that problem are not viewable.'
104 104 return
105 105 end
106 106 end
107 107
108 108 return unless @problem
109 109
110 110 @by_lang = {} #aggregrate by language
111 111
112 112 range =65
113 113 @histogram = { data: Array.new(range,0), summary: {} }
114 114 @summary = {count: 0, solve: 0, attempt: 0}
115 115 user = Hash.new(0)
116 116 Submission.where(problem_id: @problem.id).find_each do |sub|
117 117 #histogram
118 118 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
119 119 @histogram[:data][d.to_i] += 1 if d < range
120 120
121 121 @summary[:count] += 1
122 122 user[sub.user_id] = [user[sub.user_id], (sub.points >= @problem.full_score) ? 1 : 0].max
123 123
124 124 lang = Language.find_by_id(sub.language_id)
125 125 next unless lang
126 126 next unless sub.points >= @problem.full_score
127 127
128 128 #initialize
129 129 unless @by_lang.has_key?(lang.pretty_name)
130 130 @by_lang[lang.pretty_name] = {
131 131 runtime: { avail: false, value: 2**30-1 },
132 132 memory: { avail: false, value: 2**30-1 },
133 133 length: { avail: false, value: 2**30-1 },
134 134 first: { avail: false, value: DateTime.new(3000,1,1) }
135 135 }
136 136 end
137 137
138 138 if sub.max_runtime and sub.max_runtime < @by_lang[lang.pretty_name][:runtime][:value]
139 139 @by_lang[lang.pretty_name][:runtime] = { avail: true, user_id: sub.user_id, value: sub.max_runtime, sub_id: sub.id }
140 140 end
141 141
142 142 if sub.peak_memory and sub.peak_memory < @by_lang[lang.pretty_name][:memory][:value]
143 143 @by_lang[lang.pretty_name][:memory] = { avail: true, user_id: sub.user_id, value: sub.peak_memory, sub_id: sub.id }
144 144 end
145 145
146 146 if sub.submitted_at and sub.submitted_at < @by_lang[lang.pretty_name][:first][:value] and
147 147 !sub.user.admin?
148 148 @by_lang[lang.pretty_name][:first] = { avail: true, user_id: sub.user_id, value: sub.submitted_at, sub_id: sub.id }
149 149 end
150 150
151 151 if @by_lang[lang.pretty_name][:length][:value] > sub.effective_code_length
152 152 @by_lang[lang.pretty_name][:length] = { avail: true, user_id: sub.user_id, value: sub.effective_code_length, sub_id: sub.id }
153 153 end
154 154 end
155 155
156 156 #process user_id
157 157 @by_lang.each do |lang,prop|
158 158 prop.each do |k,v|
159 159 v[:user] = User.exists?(v[:user_id]) ? User.find(v[:user_id]).full_name : "(NULL)"
160 160 end
161 161 end
162 162
163 163 #sum into best
164 164 if @by_lang and @by_lang.first
165 165 @best = @by_lang.first[1].clone
166 166 @by_lang.each do |lang,prop|
167 167 if @best[:runtime][:value] >= prop[:runtime][:value]
168 168 @best[:runtime] = prop[:runtime]
169 169 @best[:runtime][:lang] = lang
170 170 end
171 171 if @best[:memory][:value] >= prop[:memory][:value]
172 172 @best[:memory] = prop[:memory]
173 173 @best[:memory][:lang] = lang
174 174 end
175 175 if @best[:length][:value] >= prop[:length][:value]
176 176 @best[:length] = prop[:length]
177 177 @best[:length][:lang] = lang
178 178 end
179 179 if @best[:first][:value] >= prop[:first][:value]
180 180 @best[:first] = prop[:first]
181 181 @best[:first][:lang] = lang
182 182 end
183 183 end
184 184 end
185 185
186 186 @histogram[:summary][:max] = [@histogram[:data].max,1].max
187 187 @summary[:attempt] = user.count
188 188 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
189 189 end
190 190
191 191 def stuck #report struggling user,problem
192 192 # init
193 193 user,problem = nil
194 194 solve = true
195 195 tries = 0
196 196 @struggle = Array.new
197 197 record = {}
198 198 Submission.includes(:problem,:user).order(:problem_id,:user_id).find_each do |sub|
199 199 next unless sub.user and sub.problem
200 200 if user != sub.user_id or problem != sub.problem_id
201 201 @struggle << { user: record[:user], problem: record[:problem], tries: tries } unless solve
202 202 record = {user: sub.user, problem: sub.problem}
203 203 user,problem = sub.user_id, sub.problem_id
204 204 solve = false
205 205 tries = 0
206 206 end
207 207 if sub.points >= sub.problem.full_score
208 208 solve = true
209 209 else
210 210 tries += 1
211 211 end
212 212 end
213 213 @struggle.sort!{|a,b| b[:tries] <=> a[:tries] }
214 214 @struggle = @struggle[0..50]
215 215 end
216 216
217 217
218 218 def multiple_login
219 + #user with multiple IP
219 220 raw = Submission.joins(:user).joins(:problem).where("problems.available != 0").group("login,ip_address").order(:login)
220 221 last,count = 0,0
221 - @multiple = []
222 + first = 0
223 + @users = []
222 224 raw.each do |r|
223 225 if last != r.user.login
224 226 count = 1
227 + last = r.user.login
228 + first = r
225 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 250 end
228 251 end
229 252 end
230 253
231 254 end
@@ -1,532 +1,537
1 1 require 'csv'
2 2
3 3 class UserAdminController < ApplicationController
4 4
5 5
6 6 include MailHelperMethods
7 7
8 8 before_filter :admin_authorization
9 9
10 10 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
11 11 verify :method => :post, :only => [ :destroy,
12 12 :create, :create_from_list,
13 13 :update,
14 14 :manage_contest,
15 15 :bulk_mail
16 16 ],
17 17 :redirect_to => { :action => :list }
18 18
19 19 def index
20 20 list
21 21 render :action => 'list'
22 22 end
23 23
24 24 def list
25 25 @user_count = User.count
26 26 if params[:page] == 'all'
27 27 @users = User.all
28 28 @paginated = false
29 29 else
30 30 @users = User.paginate :page => params[:page]
31 31 @paginated = true
32 32 end
33 33 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
34 34 @contests = Contest.enabled
35 35 end
36 36
37 37 def active
38 38 sessions = ActiveRecord::SessionStore::Session.find(:all, :conditions => ["updated_at >= ?", 60.minutes.ago])
39 39 @users = []
40 40 sessions.each do |session|
41 41 if session.data[:user_id]
42 42 @users << User.find(session.data[:user_id])
43 43 end
44 44 end
45 45 end
46 46
47 47 def show
48 48 @user = User.find(params[:id])
49 49 end
50 50
51 51 def new
52 52 @user = User.new
53 53 end
54 54
55 55 def create
56 56 @user = User.new(params[:user])
57 57 @user.activated = true
58 58 if @user.save
59 59 flash[:notice] = 'User was successfully created.'
60 60 redirect_to :action => 'list'
61 61 else
62 62 render :action => 'new'
63 63 end
64 64 end
65 65
66 66 def create_from_list
67 67 lines = params[:user_list]
68 68
69 69 note = []
70 70
71 71 lines.split("\n").each do |line|
72 72 items = line.chomp.split(',')
73 73 if items.length>=2
74 74 login = items[0]
75 75 full_name = items[1]
76 76
77 77 added_random_password = false
78 78 if items.length>=3
79 79 password = items[2].chomp(" ")
80 80 user_alias = (items.length>=4) ? items[3] : login
81 81 else
82 82 password = random_password
83 83 user_alias = (items.length>=4) ? items[3] : login
84 84 added_random_password = true
85 85 end
86 86
87 - user = User.new({:login => login,
88 - :full_name => full_name,
89 - :password => password,
90 - :password_confirmation => password,
91 - :alias => user_alias})
87 + user = User.find_by_login(login)
88 + if user
89 + user.password = password
90 + else
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 97 user.activated = true
93 98 user.save
94 99
95 100 if added_random_password
96 101 note << "'#{login}' (+)"
97 102 else
98 103 note << login
99 104 end
100 105 end
101 106 end
102 107 flash[:notice] = 'User(s) ' + note.join(', ') +
103 108 ' were successfully created. ' +
104 109 '( (+) - created with random passwords.)'
105 110 redirect_to :action => 'list'
106 111 end
107 112
108 113 def edit
109 114 @user = User.find(params[:id])
110 115 end
111 116
112 117 def update
113 118 @user = User.find(params[:id])
114 119 if @user.update_attributes(params[:user])
115 120 flash[:notice] = 'User was successfully updated.'
116 121 redirect_to :action => 'show', :id => @user
117 122 else
118 123 render :action => 'edit'
119 124 end
120 125 end
121 126
122 127 def destroy
123 128 User.find(params[:id]).destroy
124 129 redirect_to :action => 'list'
125 130 end
126 131
127 132 def user_stat
128 133 if params[:commit] == 'download csv'
129 134 @problems = Problem.all
130 135 else
131 136 @problems = Problem.find_available_problems
132 137 end
133 138 @users = User.find(:all, :include => [:contests, :contest_stat])
134 139 @scorearray = Array.new
135 140 @users.each do |u|
136 141 ustat = Array.new
137 142 ustat[0] = u
138 143 @problems.each do |p|
139 144 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
140 145 if (sub!=nil) and (sub.points!=nil)
141 146 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
142 147 else
143 148 ustat << [0,false]
144 149 end
145 150 end
146 151 @scorearray << ustat
147 152 end
148 153
149 154 if params[:commit] == 'download csv' then
150 155 csv = gen_csv_from_scorearray(@scorearray,@problems)
151 156 send_data csv, filename: 'last_score.csv'
152 157 else
153 158 render template: 'user_admin/user_stat'
154 159 end
155 160 end
156 161
157 162 def user_stat_max
158 163 if params[:commit] == 'download csv'
159 164 @problems = Problem.all
160 165 else
161 166 @problems = Problem.find_available_problems
162 167 end
163 168 @users = User.find(:all, :include => [:contests, :contest_stat])
164 169 @scorearray = Array.new
165 170 #set up range from param
166 171 since_id = params.fetch(:since_id, 0).to_i
167 172 until_id = params.fetch(:until_id, 0).to_i
168 173 @users.each do |u|
169 174 ustat = Array.new
170 175 ustat[0] = u
171 176 @problems.each do |p|
172 177 max_points = 0
173 178 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
174 179 max_points = sub.points if sub and sub.points and (sub.points > max_points)
175 180 end
176 181 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
177 182 end
178 183 @scorearray << ustat
179 184 end
180 185
181 186 if params[:commit] == 'download csv' then
182 187 csv = gen_csv_from_scorearray(@scorearray,@problems)
183 188 send_data csv, filename: 'max_score.csv'
184 189 else
185 190 render template: 'user_admin/user_stat'
186 191 end
187 192 end
188 193
189 194 def import
190 195 if params[:file]==''
191 196 flash[:notice] = 'Error importing no file'
192 197 redirect_to :action => 'list' and return
193 198 end
194 199 import_from_file(params[:file])
195 200 end
196 201
197 202 def random_all_passwords
198 203 users = User.find(:all)
199 204 @prefix = params[:prefix] || ''
200 205 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
201 206 @changed = false
202 207 if request.request_method == 'POST'
203 208 @non_admin_users.each do |user|
204 209 password = random_password
205 210 user.password = password
206 211 user.password_confirmation = password
207 212 user.save
208 213 end
209 214 @changed = true
210 215 end
211 216 end
212 217
213 218 # contest management
214 219
215 220 def contests
216 221 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
217 222 @contests = Contest.enabled
218 223 end
219 224
220 225 def assign_from_list
221 226 contest_id = params[:users_contest_id]
222 227 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
223 228 contest = Contest.find(params[:new_contest][:id])
224 229 if !contest
225 230 flash[:notice] = 'Error: no contest'
226 231 redirect_to :action => 'contests', :id =>contest_id
227 232 end
228 233
229 234 note = []
230 235 users.each do |u|
231 236 u.contests = [contest]
232 237 note << u.login
233 238 end
234 239 flash[:notice] = 'User(s) ' + note.join(', ') +
235 240 " were successfully reassigned to #{contest.title}."
236 241 redirect_to :action => 'contests', :id =>contest.id
237 242 end
238 243
239 244 def add_to_contest
240 245 user = User.find(params[:id])
241 246 contest = Contest.find(params[:contest_id])
242 247 if user and contest
243 248 user.contests << contest
244 249 end
245 250 redirect_to :action => 'list'
246 251 end
247 252
248 253 def remove_from_contest
249 254 user = User.find(params[:id])
250 255 contest = Contest.find(params[:contest_id])
251 256 if user and contest
252 257 user.contests.delete(contest)
253 258 end
254 259 redirect_to :action => 'list'
255 260 end
256 261
257 262 def contest_management
258 263 end
259 264
260 265 def manage_contest
261 266 contest = Contest.find(params[:contest][:id])
262 267 if !contest
263 268 flash[:notice] = 'You did not choose the contest.'
264 269 redirect_to :action => 'contest_management' and return
265 270 end
266 271
267 272 operation = params[:operation]
268 273
269 274 if not ['add','remove','assign'].include? operation
270 275 flash[:notice] = 'You did not choose the operation to perform.'
271 276 redirect_to :action => 'contest_management' and return
272 277 end
273 278
274 279 lines = params[:login_list]
275 280 if !lines or lines.blank?
276 281 flash[:notice] = 'You entered an empty list.'
277 282 redirect_to :action => 'contest_management' and return
278 283 end
279 284
280 285 note = []
281 286 users = []
282 287 lines.split("\n").each do |line|
283 288 user = User.find_by_login(line.chomp)
284 289 if user
285 290 if operation=='add'
286 291 if ! user.contests.include? contest
287 292 user.contests << contest
288 293 end
289 294 elsif operation=='remove'
290 295 user.contests.delete(contest)
291 296 else
292 297 user.contests = [contest]
293 298 end
294 299
295 300 if params[:reset_timer]
296 301 user.contest_stat.forced_logout = true
297 302 user.contest_stat.reset_timer_and_save
298 303 end
299 304
300 305 if params[:notification_emails]
301 306 send_contest_update_notification_email(user, contest)
302 307 end
303 308
304 309 note << user.login
305 310 users << user
306 311 end
307 312 end
308 313
309 314 if params[:reset_timer]
310 315 logout_users(users)
311 316 end
312 317
313 318 flash[:notice] = 'User(s) ' + note.join(', ') +
314 319 ' were successfully modified. '
315 320 redirect_to :action => 'contest_management'
316 321 end
317 322
318 323 # admin management
319 324
320 325 def admin
321 326 @admins = User.find(:all).find_all {|user| user.admin? }
322 327 end
323 328
324 329 def grant_admin
325 330 login = params[:login]
326 331 user = User.find_by_login(login)
327 332 if user!=nil
328 333 admin_role = Role.find_by_name('admin')
329 334 user.roles << admin_role
330 335 else
331 336 flash[:notice] = 'Unknown user'
332 337 end
333 338 flash[:notice] = 'User added as admins'
334 339 redirect_to :action => 'admin'
335 340 end
336 341
337 342 def revoke_admin
338 343 user = User.find(params[:id])
339 344 if user==nil
340 345 flash[:notice] = 'Unknown user'
341 346 redirect_to :action => 'admin' and return
342 347 elsif user.login == 'root'
343 348 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
344 349 redirect_to :action => 'admin' and return
345 350 end
346 351
347 352 admin_role = Role.find_by_name('admin')
348 353 user.roles.delete(admin_role)
349 354 flash[:notice] = 'User permission revoked'
350 355 redirect_to :action => 'admin'
351 356 end
352 357
353 358 # mass mailing
354 359
355 360 def mass_mailing
356 361 end
357 362
358 363 def bulk_mail
359 364 lines = params[:login_list]
360 365 if !lines or lines.blank?
361 366 flash[:notice] = 'You entered an empty list.'
362 367 redirect_to :action => 'mass_mailing' and return
363 368 end
364 369
365 370 mail_subject = params[:subject]
366 371 if !mail_subject or mail_subject.blank?
367 372 flash[:notice] = 'You entered an empty mail subject.'
368 373 redirect_to :action => 'mass_mailing' and return
369 374 end
370 375
371 376 mail_body = params[:email_body]
372 377 if !mail_body or mail_body.blank?
373 378 flash[:notice] = 'You entered an empty mail body.'
374 379 redirect_to :action => 'mass_mailing' and return
375 380 end
376 381
377 382 note = []
378 383 users = []
379 384 lines.split("\n").each do |line|
380 385 user = User.find_by_login(line.chomp)
381 386 if user
382 387 send_mail(user.email, mail_subject, mail_body)
383 388 note << user.login
384 389 end
385 390 end
386 391
387 392 flash[:notice] = 'User(s) ' + note.join(', ') +
388 393 ' were successfully modified. '
389 394 redirect_to :action => 'mass_mailing'
390 395 end
391 396
392 397 protected
393 398
394 399 def random_password(length=5)
395 400 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
396 401 newpass = ""
397 402 length.times { newpass << chars[rand(chars.size-1)] }
398 403 return newpass
399 404 end
400 405
401 406 def import_from_file(f)
402 407 data_hash = YAML.load(f)
403 408 @import_log = ""
404 409
405 410 country_data = data_hash[:countries]
406 411 site_data = data_hash[:sites]
407 412 user_data = data_hash[:users]
408 413
409 414 # import country
410 415 countries = {}
411 416 country_data.each_pair do |id,country|
412 417 c = Country.find_by_name(country[:name])
413 418 if c!=nil
414 419 countries[id] = c
415 420 @import_log << "Found #{country[:name]}\n"
416 421 else
417 422 countries[id] = Country.new(:name => country[:name])
418 423 countries[id].save
419 424 @import_log << "Created #{country[:name]}\n"
420 425 end
421 426 end
422 427
423 428 # import sites
424 429 sites = {}
425 430 site_data.each_pair do |id,site|
426 431 s = Site.find_by_name(site[:name])
427 432 if s!=nil
428 433 @import_log << "Found #{site[:name]}\n"
429 434 else
430 435 s = Site.new(:name => site[:name])
431 436 @import_log << "Created #{site[:name]}\n"
432 437 end
433 438 s.password = site[:password]
434 439 s.country = countries[site[:country_id]]
435 440 s.save
436 441 sites[id] = s
437 442 end
438 443
439 444 # import users
440 445 user_data.each_pair do |id,user|
441 446 u = User.find_by_login(user[:login])
442 447 if u!=nil
443 448 @import_log << "Found #{user[:login]}\n"
444 449 else
445 450 u = User.new(:login => user[:login])
446 451 @import_log << "Created #{user[:login]}\n"
447 452 end
448 453 u.full_name = user[:name]
449 454 u.password = user[:password]
450 455 u.country = countries[user[:country_id]]
451 456 u.site = sites[user[:site_id]]
452 457 u.activated = true
453 458 u.email = "empty-#{u.login}@none.com"
454 459 if not u.save
455 460 @import_log << "Errors\n"
456 461 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
457 462 end
458 463 end
459 464
460 465 end
461 466
462 467 def logout_users(users)
463 468 users.each do |user|
464 469 contest_stat = user.contest_stat(true)
465 470 if contest_stat and !contest_stat.forced_logout
466 471 contest_stat.forced_logout = true
467 472 contest_stat.save
468 473 end
469 474 end
470 475 end
471 476
472 477 def send_contest_update_notification_email(user, contest)
473 478 contest_title_name = GraderConfiguration['contest.name']
474 479 contest_name = contest.name
475 480 mail_subject = t('contest.notification.email_subject', {
476 481 :contest_title_name => contest_title_name,
477 482 :contest_name => contest_name })
478 483 mail_body = t('contest.notification.email_body', {
479 484 :full_name => user.full_name,
480 485 :contest_title_name => contest_title_name,
481 486 :contest_name => contest.name,
482 487 })
483 488
484 489 logger.info mail_body
485 490 send_mail(user.email, mail_subject, mail_body)
486 491 end
487 492
488 493 def find_contest_and_user_from_contest_id(id)
489 494 if id!='none'
490 495 @contest = Contest.find(id)
491 496 else
492 497 @contest = nil
493 498 end
494 499 if @contest
495 500 @users = @contest.users
496 501 else
497 502 @users = User.find_users_with_no_contest
498 503 end
499 504 return [@contest, @users]
500 505 end
501 506
502 507 def gen_csv_from_scorearray(scorearray,problem)
503 508 CSV.generate do |csv|
504 509 #add header
505 510 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
506 511 problem.each { |p| header << p.name }
507 512 header += ['Total','Passed']
508 513 csv << header
509 514 #add data
510 515 scorearray.each do |sc|
511 516 total = num_passed = 0
512 517 row = Array.new
513 518 sc.each_index do |i|
514 519 if i == 0
515 520 row << sc[i].login
516 521 row << sc[i].full_name
517 522 row << sc[i].activated
518 523 row << (sc[i].try(:contest_stat).try(:started_at)!=nil ? 'yes' : 'no')
519 524 row << sc[i].contests.collect {|c| c.name}.join(', ')
520 525 else
521 526 row << sc[i][0]
522 527 total += sc[i][0]
523 528 num_passed += 1 if sc[i][1]
524 529 end
525 530 end
526 531 row << total
527 532 row << num_passed
528 533 csv << row
529 534 end
530 535 end
531 536 end
532 537 end
@@ -1,138 +1,138
1 1 # Methods added to this helper will be available to all templates in the application.
2 2 module ApplicationHelper
3 3
4 4 def user_header
5 5 menu_items = ''
6 6 user = User.find(session[:user_id])
7 7
8 8 if (user!=nil) and (session[:admin])
9 9 # admin menu
10 10 menu_items << "<b>Administrative task:</b> "
11 11 append_to menu_items, '[Announcements]', 'announcements', 'index'
12 12 append_to menu_items, '[Msg console]', 'messages', 'console'
13 13 append_to menu_items, '[Problems]', 'problems', 'index'
14 14 append_to menu_items, '[Users]', 'user_admin', 'index'
15 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 17 append_to menu_items, '[Graders]', 'graders', 'list'
18 18 append_to menu_items, '[Contests]', 'contest_management', 'index'
19 19 append_to menu_items, '[Sites]', 'sites', 'index'
20 20 append_to menu_items, '[System config]', 'configurations', 'index'
21 21 menu_items << "<br/>"
22 22 end
23 23
24 24 # main page
25 25 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
26 26 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
27 27
28 28 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
29 29 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
30 30 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
31 31 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
32 32 end
33 33
34 34 if GraderConfiguration['right.user_hall_of_fame']
35 35 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
36 36 end
37 37 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
38 38
39 39 if GraderConfiguration['system.user_setting_enabled']
40 40 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
41 41 end
42 42 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
43 43
44 44 menu_items.html_safe
45 45 end
46 46
47 47 def append_to(option,label, controller, action)
48 48 option << ' ' if option!=''
49 49 option << link_to_unless_current(label,
50 50 :controller => controller,
51 51 :action => action)
52 52 end
53 53
54 54 def format_short_time(time)
55 55 now = Time.now.gmtime
56 56 st = ''
57 57 if (time.yday != now.yday) or
58 58 (time.year != now.year)
59 59 st = time.strftime("%x ")
60 60 end
61 61 st + time.strftime("%X")
62 62 end
63 63
64 64 def format_short_duration(duration)
65 65 return '' if duration==nil
66 66 d = duration.to_f
67 67 return Time.at(d).gmtime.strftime("%X")
68 68 end
69 69
70 70 def read_textfile(fname,max_size=2048)
71 71 begin
72 72 File.open(fname).read(max_size)
73 73 rescue
74 74 nil
75 75 end
76 76 end
77 77
78 78 def user_title_bar(user)
79 79 header = ''
80 80 time_left = ''
81 81
82 82 #
83 83 # if the contest is over
84 84 if GraderConfiguration.time_limit_mode?
85 85 if user.contest_finished?
86 86 header = <<CONTEST_OVER
87 87 <tr><td colspan="2" align="center">
88 88 <span class="contest-over-msg">THE CONTEST IS OVER</span>
89 89 </td></tr>
90 90 CONTEST_OVER
91 91 end
92 92 if !user.contest_started?
93 93 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
94 94 else
95 95 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
96 96 " #{format_short_duration(user.contest_time_left)}"
97 97 end
98 98 end
99 99
100 100 #
101 101 # if the contest is in the anaysis mode
102 102 if GraderConfiguration.analysis_mode?
103 103 header = <<ANALYSISMODE
104 104 <tr><td colspan="2" align="center">
105 105 <span class="contest-over-msg">ANALYSIS MODE</span>
106 106 </td></tr>
107 107 ANALYSISMODE
108 108 end
109 109
110 110 contest_name = GraderConfiguration['contest.name']
111 111
112 112 #
113 113 # build real title bar
114 114 result = <<TITLEBAR
115 115 <div class="title">
116 116 <table>
117 117 #{header}
118 118 <tr>
119 119 <td class="left-col">
120 120 #{user.full_name}<br/>
121 121 #{t 'title_bar.current_time'} #{format_short_time(Time.new)}
122 122 #{time_left}
123 123 <br/>
124 124 </td>
125 125 <td class="right-col">#{contest_name}</td>
126 126 </tr>
127 127 </table>
128 128 </div>
129 129 TITLEBAR
130 130 result.html_safe
131 131 end
132 132
133 133 def markdown(text)
134 134 markdown = RDiscount.new(text)
135 135 markdown.to_html.html_safe
136 136 end
137 137
138 138 end
@@ -1,376 +1,376
1 1 require 'digest/sha1'
2 2 require 'net/pop'
3 3 require 'net/https'
4 4 require 'net/http'
5 5 require 'json'
6 6
7 7 class User < ActiveRecord::Base
8 8
9 9 has_and_belongs_to_many :roles
10 10
11 11 has_many :test_requests, :order => "submitted_at DESC"
12 12
13 13 has_many :messages,
14 14 :class_name => "Message",
15 15 :foreign_key => "sender_id",
16 16 :order => 'created_at DESC'
17 17
18 18 has_many :replied_messages,
19 19 :class_name => "Message",
20 20 :foreign_key => "receiver_id",
21 21 :order => 'created_at DESC'
22 22
23 23 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
24 24
25 25 belongs_to :site
26 26 belongs_to :country
27 27
28 28 has_and_belongs_to_many :contests, :uniq => true, :order => 'name'
29 29
30 30 scope :activated_users, :conditions => {:activated => true}
31 31
32 32 validates_presence_of :login
33 33 validates_uniqueness_of :login
34 34 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
35 35 validates_length_of :login, :within => 3..30
36 36
37 37 validates_presence_of :full_name
38 38 validates_length_of :full_name, :minimum => 1
39 39
40 40 validates_presence_of :password, :if => :password_required?
41 41 validates_length_of :password, :within => 4..20, :if => :password_required?
42 42 validates_confirmation_of :password, :if => :password_required?
43 43
44 44 validates_format_of :email,
45 45 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
46 46 :if => :email_validation?
47 47 validate :uniqueness_of_email_from_activated_users,
48 48 :if => :email_validation?
49 49 validate :enough_time_interval_between_same_email_registrations,
50 50 :if => :email_validation?
51 51
52 52 # these are for ytopc
53 53 # disable for now
54 54 #validates_presence_of :province
55 55
56 56 attr_accessor :password
57 57
58 58 before_save :encrypt_new_password
59 59 before_save :assign_default_site
60 60 before_save :assign_default_contest
61 61
62 62 # this is for will_paginate
63 63 cattr_reader :per_page
64 64 @@per_page = 50
65 65
66 66 def self.authenticate(login, password)
67 67 user = find_by_login(login)
68 68 if user
69 69 return user if user.authenticated?(password)
70 - if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
71 - user.password = password
72 - user.save
73 - return user
74 - end
70 + # if user.authenticated_by_cucas?(password) or user.authenticated_by_pop3?(password)
71 + # user.password = password
72 + # user.save
73 + # return user
74 + # end
75 75 end
76 76 end
77 77
78 78 def authenticated?(password)
79 79 if self.activated
80 80 hashed_password == User.encrypt(password,self.salt)
81 81 else
82 82 false
83 83 end
84 84 end
85 85
86 86 def authenticated_by_pop3?(password)
87 87 Net::POP3.enable_ssl
88 88 pop = Net::POP3.new('pops.it.chula.ac.th')
89 89 authen = true
90 90 begin
91 91 pop.start(login, password)
92 92 pop.finish
93 93 return true
94 94 rescue
95 95 return false
96 96 end
97 97 end
98 98
99 99 def authenticated_by_cucas?(password)
100 100 url = URI.parse('https://www.cas.chula.ac.th/cas/api/?q=studentAuthenticate')
101 101 appid = '41508763e340d5858c00f8c1a0f5a2bb'
102 102 appsecret ='d9cbb5863091dbe186fded85722a1e31'
103 103 post_args = {
104 104 'appid' => appid,
105 105 'appsecret' => appsecret,
106 106 'username' => login,
107 107 'password' => password
108 108 }
109 109
110 110 #simple call
111 111 begin
112 112 http = Net::HTTP.new('www.cas.chula.ac.th', 443)
113 113 http.use_ssl = true
114 114 result = [ ]
115 115 http.start do |http|
116 116 req = Net::HTTP::Post.new('/cas/api/?q=studentAuthenticate')
117 117 param = "appid=#{appid}&appsecret=#{appsecret}&username=#{login}&password=#{password}"
118 118 resp = http.request(req,param)
119 119 result = JSON.parse resp.body
120 120 end
121 121 return true if result["type"] == "beanStudent"
122 122 rescue
123 123 return false
124 124 end
125 125 return false
126 126 end
127 127
128 128 def admin?
129 129 self.roles.detect {|r| r.name == 'admin' }
130 130 end
131 131
132 132 def email_for_editing
133 133 if self.email==nil
134 134 "(unknown)"
135 135 elsif self.email==''
136 136 "(blank)"
137 137 else
138 138 self.email
139 139 end
140 140 end
141 141
142 142 def email_for_editing=(e)
143 143 self.email=e
144 144 end
145 145
146 146 def alias_for_editing
147 147 if self.alias==nil
148 148 "(unknown)"
149 149 elsif self.alias==''
150 150 "(blank)"
151 151 else
152 152 self.alias
153 153 end
154 154 end
155 155
156 156 def alias_for_editing=(e)
157 157 self.alias=e
158 158 end
159 159
160 160 def activation_key
161 161 if self.hashed_password==nil
162 162 encrypt_new_password
163 163 end
164 164 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
165 165 end
166 166
167 167 def verify_activation_key(key)
168 168 key == activation_key
169 169 end
170 170
171 171 def self.random_password(length=5)
172 172 chars = 'abcdefghjkmnopqrstuvwxyz'
173 173 password = ''
174 174 length.times { password << chars[rand(chars.length - 1)] }
175 175 password
176 176 end
177 177
178 178 def self.find_non_admin_with_prefix(prefix='')
179 179 users = User.find(:all)
180 180 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
181 181 end
182 182
183 183 # Contest information
184 184
185 185 def self.find_users_with_no_contest()
186 186 users = User.find(:all)
187 187 return users.find_all { |u| u.contests.length == 0 }
188 188 end
189 189
190 190
191 191 def contest_time_left
192 192 if GraderConfiguration.contest_mode?
193 193 return nil if site==nil
194 194 return site.time_left
195 195 elsif GraderConfiguration.indv_contest_mode?
196 196 time_limit = GraderConfiguration.contest_time_limit
197 197 if time_limit == nil
198 198 return nil
199 199 end
200 200 if contest_stat==nil or contest_stat.started_at==nil
201 201 return (Time.now.gmtime + time_limit) - Time.now.gmtime
202 202 else
203 203 finish_time = contest_stat.started_at + time_limit
204 204 current_time = Time.now.gmtime
205 205 if current_time > finish_time
206 206 return 0
207 207 else
208 208 return finish_time - current_time
209 209 end
210 210 end
211 211 else
212 212 return nil
213 213 end
214 214 end
215 215
216 216 def contest_finished?
217 217 if GraderConfiguration.contest_mode?
218 218 return false if site==nil
219 219 return site.finished?
220 220 elsif GraderConfiguration.indv_contest_mode?
221 221 return false if self.contest_stat(true)==nil
222 222 return contest_time_left == 0
223 223 else
224 224 return false
225 225 end
226 226 end
227 227
228 228 def contest_started?
229 229 if GraderConfiguration.indv_contest_mode?
230 230 stat = self.contest_stat
231 231 return ((stat != nil) and (stat.started_at != nil))
232 232 elsif GraderConfiguration.contest_mode?
233 233 return true if site==nil
234 234 return site.started
235 235 else
236 236 return true
237 237 end
238 238 end
239 239
240 240 def update_start_time
241 241 stat = self.contest_stat
242 242 if stat == nil or stat.started_at == nil
243 243 stat ||= UserContestStat.new(:user => self)
244 244 stat.started_at = Time.now.gmtime
245 245 stat.save
246 246 end
247 247 end
248 248
249 249 def problem_in_user_contests?(problem)
250 250 problem_contests = problem.contests.all
251 251
252 252 if problem_contests.length == 0 # this is public contest
253 253 return true
254 254 end
255 255
256 256 contests.each do |contest|
257 257 if problem_contests.find {|c| c.id == contest.id }
258 258 return true
259 259 end
260 260 end
261 261 return false
262 262 end
263 263
264 264 def available_problems_group_by_contests
265 265 contest_problems = []
266 266 pin = {}
267 267 contests.enabled.each do |contest|
268 268 available_problems = contest.problems.available
269 269 contest_problems << {
270 270 :contest => contest,
271 271 :problems => available_problems
272 272 }
273 273 available_problems.each {|p| pin[p.id] = true}
274 274 end
275 275 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
276 276 contest_problems << {
277 277 :contest => nil,
278 278 :problems => other_avaiable_problems
279 279 }
280 280 return contest_problems
281 281 end
282 282
283 283 def available_problems
284 284 if not GraderConfiguration.multicontests?
285 285 return Problem.find_available_problems
286 286 else
287 287 contest_problems = []
288 288 pin = {}
289 289 contests.enabled.each do |contest|
290 290 contest.problems.available.each do |problem|
291 291 if not pin.has_key? problem.id
292 292 contest_problems << problem
293 293 end
294 294 pin[problem.id] = true
295 295 end
296 296 end
297 297 other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
298 298 return contest_problems + other_avaiable_problems
299 299 end
300 300 end
301 301
302 302 def can_view_problem?(problem)
303 303 if not GraderConfiguration.multicontests?
304 304 return problem.available
305 305 else
306 306 return problem_in_user_contests? problem
307 307 end
308 308 end
309 309
310 310 protected
311 311 def encrypt_new_password
312 312 return if password.blank?
313 313 self.salt = (10+rand(90)).to_s
314 314 self.hashed_password = User.encrypt(self.password,self.salt)
315 315 end
316 316
317 317 def assign_default_site
318 318 # have to catch error when migrating (because self.site is not available).
319 319 begin
320 320 if self.site==nil
321 321 self.site = Site.find_by_name('default')
322 322 if self.site==nil
323 323 self.site = Site.find(1) # when 'default has be renamed'
324 324 end
325 325 end
326 326 rescue
327 327 end
328 328 end
329 329
330 330 def assign_default_contest
331 331 # have to catch error when migrating (because self.site is not available).
332 332 begin
333 333 if self.contests.length == 0
334 334 default_contest = Contest.find_by_name(GraderConfiguration['contest.default_contest_name'])
335 335 if default_contest
336 336 self.contests = [default_contest]
337 337 end
338 338 end
339 339 rescue
340 340 end
341 341 end
342 342
343 343 def password_required?
344 344 self.hashed_password.blank? || !self.password.blank?
345 345 end
346 346
347 347 def self.encrypt(string,salt)
348 348 Digest::SHA1.hexdigest(salt + string)
349 349 end
350 350
351 351 def uniqueness_of_email_from_activated_users
352 352 user = User.activated_users.find_by_email(self.email)
353 353 if user and (user.login != self.login)
354 354 self.errors.add_to_base("Email has already been taken")
355 355 end
356 356 end
357 357
358 358 def enough_time_interval_between_same_email_registrations
359 359 return if !self.new_record?
360 360 return if self.activated
361 361 open_user = User.find_by_email(self.email,
362 362 :order => 'created_at DESC')
363 363 if open_user and open_user.created_at and
364 364 (open_user.created_at > Time.now.gmtime - 5.minutes)
365 365 self.errors.add_to_base("There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
366 366 end
367 367 end
368 368
369 369 def email_validation?
370 370 begin
371 371 return VALIDATE_USER_EMAILS
372 372 rescue
373 373 return false
374 374 end
375 375 end
376 376 end
@@ -1,51 +1,53
1 1 :css
2 2 .fix-width {
3 3 font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier"
4 4 }
5 5
6 6 %h1 Problem stat: #{@problem.name}
7 7 %h2 Overview
8 8
9 9
10 10 %table.info
11 11 %thead
12 12 %tr.info-head
13 13 %th Stat
14 14 %th Value
15 15 %tbody
16 16 %tr{class: cycle('info-even','info-odd')}
17 17 %td Submissions
18 18 %td= @submissions.count
19 19 %tr{class: cycle('info-even','info-odd')}
20 20 %td Solved/Attempted User
21 21 %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
22 22
23 23 %h2 Submissions Count
24 24 = render partial: 'application/bar_graph', locals: { histogram: @histogram }
25 25
26 26 %h2 Submissions
27 27 - if @submissions and @submissions.count > 0
28 28 %table.info#main_table
29 29 %thead
30 30 %tr.info-head
31 31 %th ID
32 32 %th Login
33 33 %th Name
34 34 %th Submitted_at
35 35 %th Points
36 36 %th comment
37 + %th IP
37 38 %tbody
38 39 - row_odd,curr = true,''
39 40 - @submissions.each do |sub|
40 41 - next unless sub.user
41 42 - row_odd,curr = !row_odd, sub.user if curr != sub.user
42 43 %tr{class: row_odd ? "info-odd" : "info-even"}
43 44 %td= link_to sub.id, controller: 'graders', action: 'submission', id: sub.id
44 45 %td= link_to sub.user.login, controller: :users, action: :profile, id: sub.user.id
45 46 %td= sub.user.full_name
46 47 %td= time_ago_in_words(sub.submitted_at) + " ago"
47 48 %td= sub.points
48 49 %td.fix-width= sub.grader_comment
50 + %td= sub.ip_address
49 51 - else
50 52 No submission
51 53
@@ -1,20 +1,37
1 1 - content_for :header do
2 2 = stylesheet_link_tag 'tablesorter-theme.cafe'
3 3 = javascript_include_tag 'local_jquery'
4 4
5 5 %h1 Login status
6 6
7 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 12 %table.tablesorter-cafe#my_table
10 13 %thead
11 14 %tr
12 15 %th login
13 16 %th full name
14 17 %th IP
15 18 %tbody
16 - - @multiple.each do |l|
19 + - @users.each do |l|
17 20 %tr{class: cycle('info-even','info-odd')}
18 - %td= link_to l[:login], controller: 'users', action: 'profile', id: l[:id]
19 - %td= l[:full_name]
21 + %td= link_to l.user.login, controller: 'users', action: 'profile', id: l[:id]
22 + %td= l.user.full_name
20 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