Description:
added time out check for code jom submissions
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r242:803c8843ad9b - - 3 files changed: 18 inserted, 4 deleted

@@ -1,396 +1,405
1 1 class MainController < ApplicationController
2 2
3 3 before_filter :authenticate, :except => [:index, :login]
4 4 before_filter :check_viewability, :except => [:index, :login]
5 5
6 6 # COMMENTED OUT: filter in each action instead
7 7 # before_filter :verify_time_limit, :only => [:submit]
8 8
9 9 verify :method => :post, :only => [:submit, :download_input, :submit_solution],
10 10 :redirect_to => { :action => :index }
11 11
12 12 # COMMENT OUT: only need when having high load
13 13 # caches_action :index, :login
14 14
15 15 # NOTE: This method is not actually needed, 'config/routes.rb' has
16 16 # assigned action login as a default action.
17 17 def index
18 18 redirect_to :action => 'login'
19 19 end
20 20
21 21 def login
22 22 saved_notice = flash[:notice]
23 23 reset_session
24 24 flash.now[:notice] = saved_notice
25 25
26 26 # EXPERIMENT:
27 27 # Hide login if in single user mode and the url does not
28 28 # explicitly specify /login
29 29 #
30 30 # logger.info "PATH: #{request.path}"
31 31 # if Configuration['system.single_user_mode'] and
32 32 # request.path!='/main/login'
33 33 # @hidelogin = true
34 34 # end
35 35
36 36 @announcements = Announcement.find_for_frontpage
37 37 render :action => 'login', :layout => 'empty'
38 38 end
39 39
40 40 def list
41 41 prepare_list_information
42 42 end
43 43
44 44 def help
45 45 @user = User.find(session[:user_id])
46 46 end
47 47
48 48 def submit
49 49 user = User.find(session[:user_id])
50 50
51 51 @submission = Submission.new(params[:submission])
52 52 @submission.user = user
53 53 @submission.language_id = 0
54 54 if (params['file']) and (params['file']!='')
55 55 @submission.source = params['file'].read
56 56 @submission.source_filename = params['file'].original_filename
57 57 end
58 58 @submission.submitted_at = Time.new.gmtime
59 59
60 60 if Configuration.time_limit_mode? and user.contest_finished?
61 61 @submission.errors.add_to_base "The contest is over."
62 62 prepare_list_information
63 63 render :action => 'list' and return
64 64 end
65 65
66 66 if @submission.valid?
67 67 if @submission.save == false
68 68 flash[:notice] = 'Error saving your submission'
69 69 elsif Task.create(:submission_id => @submission.id,
70 70 :status => Task::STATUS_INQUEUE) == false
71 71 flash[:notice] = 'Error adding your submission to task queue'
72 72 end
73 73 else
74 74 prepare_list_information
75 75 render :action => 'list' and return
76 76 end
77 77 redirect_to :action => 'list'
78 78 end
79 79
80 80 def source
81 81 submission = Submission.find(params[:id])
82 82 if submission.user_id == session[:user_id]
83 83 send_data(submission.source,
84 84 {:filename => submission.download_filename,
85 85 :type => 'text/plain'})
86 86 else
87 87 flash[:notice] = 'Error viewing source'
88 88 redirect_to :action => 'list'
89 89 end
90 90 end
91 91
92 92 def compiler_msg
93 93 @submission = Submission.find(params[:id])
94 94 if @submission.user_id == session[:user_id]
95 95 render :action => 'compiler_msg', :layout => 'empty'
96 96 else
97 97 flash[:notice] = 'Error viewing source'
98 98 redirect_to :action => 'list'
99 99 end
100 100 end
101 101
102 102 def submission
103 103 # protect the action for Code Jom
104 104 redirect_to :action => 'list'
105 105
106 106 @user = User.find(session[:user_id])
107 107 @problems = Problem.find_available_problems
108 108 if params[:id]==nil
109 109 @problem = nil
110 110 @submissions = nil
111 111 else
112 112 @problem = Problem.find_by_name(params[:id])
113 113 if not @problem.available
114 114 redirect_to :action => 'list'
115 115 flash[:notice] = 'Error: submissions for that problem are not viewable.'
116 116 return
117 117 end
118 118 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
119 119 end
120 120 end
121 121
122 122 def result
123 123 if !Configuration.show_grading_result
124 124 redirect_to :action => 'list' and return
125 125 end
126 126 @user = User.find(session[:user_id])
127 127 @submission = Submission.find(params[:id])
128 128 if @submission.user!=@user
129 129 flash[:notice] = 'You are not allowed to view result of other users.'
130 130 redirect_to :action => 'list' and return
131 131 end
132 132 prepare_grading_result(@submission)
133 133 end
134 134
135 135 def load_output
136 136 if !Configuration.show_grading_result or params[:num]==nil
137 137 redirect_to :action => 'list' and return
138 138 end
139 139 @user = User.find(session[:user_id])
140 140 @submission = Submission.find(params[:id])
141 141 if @submission.user!=@user
142 142 flash[:notice] = 'You are not allowed to view result of other users.'
143 143 redirect_to :action => 'list' and return
144 144 end
145 145 case_num = params[:num].to_i
146 146 out_filename = output_filename(@user.login,
147 147 @submission.problem.name,
148 148 @submission.id,
149 149 case_num)
150 150 if !FileTest.exists?(out_filename)
151 151 flash[:notice] = 'Output not found.'
152 152 redirect_to :action => 'list' and return
153 153 end
154 154
155 155 response.headers['Content-Type'] = "application/force-download"
156 156 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
157 157 response.headers["X-Sendfile"] = out_filename
158 158 response.headers['Content-length'] = File.size(out_filename)
159 159 render :nothing => true
160 160 end
161 161
162 162 def error
163 163 @user = User.find(session[:user_id])
164 164 end
165 165
166 166 # announcement refreshing and hiding methods
167 167
168 168 def announcements
169 169 if params.has_key? 'recent'
170 170 prepare_announcements(params[:recent])
171 171 else
172 172 prepare_announcements
173 173 end
174 174 render(:partial => 'announcement',
175 175 :collection => @announcements,
176 176 :locals => {:announcement_effect => true})
177 177 end
178 178
179 179 #
180 180 # actions for Code Jom
181 181 #
182 182 def download_input
183 + user = User.find(session[:user_id])
184 +
185 + if Configuration.time_limit_mode? and user.contest_finished?
186 + redirect_to :action => 'list' and return
187 + end
188 +
183 189 problem = Problem.find(params[:id])
184 - user = User.find(session[:user_id])
185 190 if user.can_request_new_test_pair_for? problem
186 191 assignment = user.get_new_test_pair_assignment_for problem
187 192 assignment.save
188 193
189 194 send_data(assignment.test_pair.input,
190 195 { :filename => "#{problem.name}-#{assignment.request_number}.in",
191 196 :type => 'text/plain' })
192 197 else
193 198 recent_assignment = user.get_recent_test_pair_assignment_for problem
194 199 send_data(recent_assignment.test_pair.input,
195 200 { :filename => "#{problem.name}-#{recent_assignment.request_number}.in",
196 201 :type => 'text/plain' })
197 202 end
198 203 end
199 204
200 205 def submit_solution
201 206 problem = Problem.find(params[:id])
202 207 user = User.find(session[:user_id])
203 208 recent_assignment = user.get_recent_test_pair_assignment_for problem
204 209
210 + if Configuration.time_limit_mode? and user.contest_finished?
211 + redirect_to :action => 'list' and return
212 + end
213 +
205 214 if recent_assignment == nil
206 215 flash[:notice] = 'You have not requested for any input data for this problem. Please download an input first.'
207 216 session[:current_problem_id] = problem.id
208 217 redirect_to :action => 'list' and return
209 218 end
210 219
211 220 if recent_assignment.expired?
212 221 flash[:notice] = 'The current input is expired. Please download a new input data.'
213 222 session[:current_problem_id] = problem.id
214 223 redirect_to :action => 'list' and return
215 224 end
216 225
217 226 if recent_assignment.submitted
218 227 flash[:notice] = 'You have already submitted an incorrect solution for this input. Please download a new input data.'
219 228 session[:current_problem_id] = problem.id
220 229 redirect_to :action => 'list' and return
221 230 end
222 231
223 232 if params[:file] == nil
224 233 flash[:notice] = 'You have not submitted any output.'
225 234 session[:current_problem_id] = problem.id
226 235 redirect_to :action => 'list' and return
227 236 end
228 237
229 238 submitted_solution = params[:file].read
230 239 test_pair = recent_assignment.test_pair
231 240 passed = test_pair.grade(submitted_solution)
232 241 points = passed ? 100 : 0
233 242 submission = Submission.new(:user => user,
234 243 :problem => problem,
235 244 :source => submitted_solution,
236 245 :source_filename => params['file'].original_filename,
237 246 :language_id => 0,
238 247 :submitted_at => Time.new.gmtime,
239 248 :graded_at => Time.new.gmtime,
240 249 :points => points)
241 250 submission.save
242 251 recent_assignment.submitted = true
243 252 recent_assignment.save
244 253
245 254 status = user.get_submission_status_for(problem)
246 255 if status == nil
247 256 status = SubmissionStatus.new :user => user, :problem => problem, :submission_count => 0
248 257 end
249 258
250 259 status.submission_count += 1
251 260 status.passed = passed
252 261 status.save
253 262
254 263 if passed
255 264 flash[:notice] = 'Correct solution.'
256 265 user.update_codejom_status
257 266 else
258 267 session[:current_problem_id] = problem.id
259 268 flash[:notice] = 'Incorrect solution.'
260 269 end
261 270 redirect_to :action => 'list'
262 271 end
263 272
264 273 protected
265 274
266 275 def prepare_announcements(recent=nil)
267 276 if Configuration.show_tasks_to?(@user)
268 277 @announcements = Announcement.find_published(true)
269 278 else
270 279 @announcements = Announcement.find_published
271 280 end
272 281 if recent!=nil
273 282 recent_id = recent.to_i
274 283 @announcements = @announcements.find_all { |a| a.id > recent_id }
275 284 end
276 285 end
277 286
278 287 def prepare_timeout_information(problems)
279 288 @submission_timeouts = {}
280 289 problems.each do |problem|
281 290 assignment = @user.get_recent_test_pair_assignment_for(problem)
282 291 if assignment == nil
283 292 timeout = nil
284 293 else
285 294 if (assignment.expired?) or (assignment.submitted)
286 295 timeout = 0
287 296 else
288 297 timeout = assignment.created_at + TEST_ASSIGNMENT_EXPIRATION_DURATION - Time.new.gmtime
289 298 end
290 299 end
291 300 @submission_timeouts[problem.id] = timeout
292 301 end
293 302 @submission_timeouts.each_pair {|k,v| puts "#{k} => #{v}"}
294 303 end
295 304
296 305 def prepare_list_information
297 306 @user = User.find(session[:user_id])
298 307
299 308 all_problems = Problem.find_available_problems
300 309
301 310 passed = {}
302 311 sub_count = {}
303 312 @user.submission_statuses.each do |status|
304 313 if status.passed
305 314 passed[status.problem_id] = true
306 315 end
307 316 sub_count[status.problem_id] = status.submission_count
308 317 end
309 318
310 319 if session.has_key? :current_problem_id
311 320 @current_problem_id = session[:current_problem_id]
312 321 session.delete(:current_problem_id)
313 322 else
314 323 @current_problem_id = nil
315 324 end
316 325
317 326 @problems = all_problems.reject { |problem| passed.has_key? problem.id }
318 327
319 328 prepare_timeout_information(@problems)
320 329
321 330 @prob_submissions = Array.new
322 331 @problems.each do |p|
323 332 if sub_count.has_key? p.id
324 333 @prob_submissions << { :count => sub_count[p.id] }
325 334 else
326 335 @prob_submissions << { :count => 0 }
327 336 end
328 337 end
329 338 prepare_announcements
330 339 end
331 340
332 341 def check_viewability
333 342 @user = User.find(session[:user_id])
334 343 if (!Configuration.show_tasks_to?(@user)) and
335 344 ((action_name=='submission') or (action_name=='submit'))
336 345 redirect_to :action => 'list' and return
337 346 end
338 347 end
339 348
340 349 def prepare_grading_result(submission)
341 350 if Configuration.task_grading_info.has_key? submission.problem.name
342 351 grading_info = Configuration.task_grading_info[submission.problem.name]
343 352 else
344 353 # guess task info from problem.full_score
345 354 cases = submission.problem.full_score / 10
346 355 grading_info = {
347 356 'testruns' => cases,
348 357 'testcases' => cases
349 358 }
350 359 end
351 360 @test_runs = []
352 361 if grading_info['testruns'].is_a? Integer
353 362 trun_count = grading_info['testruns']
354 363 trun_count.times do |i|
355 364 @test_runs << [ read_grading_result(@user.login,
356 365 submission.problem.name,
357 366 submission.id,
358 367 i+1) ]
359 368 end
360 369 else
361 370 grading_info['testruns'].keys.sort.each do |num|
362 371 run = []
363 372 testrun = grading_info['testruns'][num]
364 373 testrun.each do |c|
365 374 run << read_grading_result(@user.login,
366 375 submission.problem.name,
367 376 submission.id,
368 377 c)
369 378 end
370 379 @test_runs << run
371 380 end
372 381 end
373 382 end
374 383
375 384 def grading_result_dir(user_name, problem_name, submission_id, case_num)
376 385 return "#{GRADING_RESULT_DIR}/#{user_name}/#{problem_name}/#{submission_id}/test-result/#{case_num}"
377 386 end
378 387
379 388 def output_filename(user_name, problem_name, submission_id, case_num)
380 389 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
381 390 return "#{dir}/output.txt"
382 391 end
383 392
384 393 def read_grading_result(user_name, problem_name, submission_id, case_num)
385 394 dir = grading_result_dir(user_name,problem_name, submission_id, case_num)
386 395 result_file_name = "#{dir}/result"
387 396 if !FileTest.exists?(result_file_name)
388 397 return {:num => case_num, :msg => 'program did not run'}
389 398 else
390 399 results = File.open(result_file_name).readlines
391 400 run_stat = extract_running_stat(results)
392 401 output_filename = "#{dir}/output.txt"
393 402 if FileTest.exists?(output_filename)
394 403 output_file = true
395 404 output_size = File.size(output_filename)
396 405 else
@@ -1,21 +1,26
1 1 .problem-panel{:id => "problem-panel-#{problem.id}", :style => "#{(problem.id != @current_problem_id) ? "display:none" : ""}"}
2 + - if (not Configuration.time_limit_mode?) or (not @user.contest_finished?)
2 3 .problem-form{:id => "problem-form-#{problem.id}"}
3 4 - form_tag({ :action => 'download_input', :id => problem.id }, :method => :post) do
4 5 %b Input:
5 6 %input{:type => "submit", :value => "Download input", :onclick => "CodejomTimeout.updateTimeoutAfterDownloadClick(#{problem.id}); return true;"}
6 7 %span{:id => "problem-timing-message-#{problem.id}"}
7 8 = "After downloading, you have #{TEST_ASSIGNMENT_EXPIRATION_DURATION/60} minutes to submit."
8 9 %div{:id => "problem-submission-form-#{problem.id}"}
9 10 - form_tag({ :action => 'submit_solution', :id => problem.id }, :method => :post, :multipart => true) do
10 11 %b Submit output:
11 12 %input{:type => "file", :name => "file"}
12 13 %input{:type => "submit", :value => "Submit solution"}
14 + - else
15 + .problem-form
16 + %p
17 + The contest is over, you can view the tasks, but you can no longer submit.
13 18
14 19 .problem-description
15 20 - if problem.description!=nil
16 21 - if problem.description.markdowned
17 22 ~ markdown(problem.description.body)
18 23 - else
19 24 = problem.description.body
20 25 - else
21 26 (not available)
@@ -1,248 +1,248
1 1 # This file is auto-generated from the current state of the database. Instead of editing this file,
2 2 # please use the migrations feature of Active Record to incrementally modify your database, and
3 3 # then regenerate this schema definition.
4 4 #
5 5 # Note that this schema.rb definition is the authoritative source for your database schema. If you need
6 6 # to create the application database on another system, you should be using db:schema:load, not running
7 7 # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8 8 # you'll amass, the slower it'll run and the greater likelihood for issues).
9 9 #
10 10 # It's strongly recommended to check this file into your version control system.
11 11
12 - ActiveRecord::Schema.define(:version => 20100126170609) do
12 + ActiveRecord::Schema.define(:version => 20100129041917) do
13 13
14 14 create_table "announcements", :force => true do |t|
15 15 t.string "author"
16 16 t.text "body"
17 17 t.boolean "published"
18 18 t.datetime "created_at"
19 19 t.datetime "updated_at"
20 20 t.boolean "frontpage", :default => false
21 21 t.boolean "contest_only", :default => false
22 22 t.string "title"
23 23 end
24 24
25 25 create_table "codejom_statuses", :force => true do |t|
26 26 t.integer "user_id"
27 27 t.boolean "alive"
28 28 t.integer "num_problems_passed"
29 29 t.datetime "created_at"
30 30 t.datetime "updated_at"
31 31 end
32 32
33 33 create_table "configurations", :force => true do |t|
34 34 t.string "key"
35 35 t.string "value_type"
36 36 t.string "value"
37 37 t.datetime "created_at"
38 38 t.datetime "updated_at"
39 39 t.text "description"
40 40 end
41 41
42 42 create_table "countries", :force => true do |t|
43 43 t.string "name"
44 44 t.datetime "created_at"
45 45 t.datetime "updated_at"
46 46 end
47 47
48 48 create_table "descriptions", :force => true do |t|
49 49 t.text "body"
50 50 t.boolean "markdowned"
51 51 t.datetime "created_at"
52 52 t.datetime "updated_at"
53 53 end
54 54
55 55 create_table "grader_processes", :force => true do |t|
56 56 t.string "host", :limit => 20
57 57 t.integer "pid"
58 58 t.string "mode"
59 59 t.boolean "active"
60 60 t.datetime "created_at"
61 61 t.datetime "updated_at"
62 62 t.integer "task_id"
63 63 t.string "task_type"
64 64 t.boolean "terminated"
65 65 end
66 66
67 67 add_index "grader_processes", ["host", "pid"], :name => "index_grader_processes_on_ip_and_pid"
68 68
69 69 create_table "languages", :force => true do |t|
70 70 t.string "name", :limit => 10
71 71 t.string "pretty_name"
72 72 t.string "ext", :limit => 10
73 73 t.string "common_ext"
74 74 end
75 75
76 76 create_table "messages", :force => true do |t|
77 77 t.integer "sender_id"
78 78 t.integer "receiver_id"
79 79 t.integer "replying_message_id"
80 80 t.text "body"
81 81 t.boolean "replied"
82 82 t.datetime "created_at"
83 83 t.datetime "updated_at"
84 84 end
85 85
86 86 create_table "problems", :force => true do |t|
87 87 t.string "name", :limit => 30
88 88 t.string "full_name"
89 89 t.integer "full_score"
90 90 t.date "date_added"
91 91 t.boolean "available"
92 92 t.string "url"
93 93 t.integer "description_id"
94 94 t.boolean "test_allowed"
95 95 t.boolean "output_only"
96 96 end
97 97
98 98 create_table "rights", :force => true do |t|
99 99 t.string "name"
100 100 t.string "controller"
101 101 t.string "action"
102 102 end
103 103
104 104 create_table "rights_roles", :id => false, :force => true do |t|
105 105 t.integer "right_id"
106 106 t.integer "role_id"
107 107 end
108 108
109 109 add_index "rights_roles", ["role_id"], :name => "index_rights_roles_on_role_id"
110 110
111 111 create_table "roles", :force => true do |t|
112 112 t.string "name"
113 113 end
114 114
115 115 create_table "roles_users", :id => false, :force => true do |t|
116 116 t.integer "role_id"
117 117 t.integer "user_id"
118 118 end
119 119
120 120 add_index "roles_users", ["user_id"], :name => "index_roles_users_on_user_id"
121 121
122 122 create_table "sessions", :force => true do |t|
123 123 t.string "session_id"
124 124 t.text "data"
125 125 t.datetime "updated_at"
126 126 end
127 127
128 128 add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
129 129 add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
130 130
131 131 create_table "sites", :force => true do |t|
132 132 t.string "name"
133 133 t.boolean "started"
134 134 t.datetime "start_time"
135 135 t.datetime "created_at"
136 136 t.datetime "updated_at"
137 137 t.integer "country_id"
138 138 t.string "password"
139 139 end
140 140
141 141 create_table "submission_statuses", :force => true do |t|
142 142 t.integer "user_id"
143 143 t.integer "problem_id"
144 144 t.boolean "passed"
145 145 t.integer "submission_count"
146 146 t.datetime "created_at"
147 147 t.datetime "updated_at"
148 148 end
149 149
150 150 create_table "submissions", :force => true do |t|
151 151 t.integer "user_id"
152 152 t.integer "problem_id"
153 153 t.integer "language_id"
154 154 t.text "source"
155 155 t.binary "binary"
156 156 t.datetime "submitted_at"
157 157 t.datetime "compiled_at"
158 158 t.text "compiler_message"
159 159 t.datetime "graded_at"
160 160 t.integer "points"
161 161 t.text "grader_comment"
162 162 t.integer "number"
163 163 t.string "source_filename"
164 164 end
165 165
166 166 add_index "submissions", ["user_id", "problem_id", "number"], :name => "index_submissions_on_user_id_and_problem_id_and_number", :unique => true
167 167 add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id"
168 168
169 169 create_table "tasks", :force => true do |t|
170 170 t.integer "submission_id"
171 171 t.datetime "created_at"
172 172 t.integer "status"
173 173 t.datetime "updated_at"
174 174 end
175 175
176 176 create_table "test_pair_assignments", :force => true do |t|
177 177 t.integer "user_id"
178 178 t.integer "problem_id"
179 179 t.integer "test_pair_id"
180 180 t.integer "test_pair_number"
181 181 t.integer "request_number"
182 182 t.datetime "created_at"
183 183 t.datetime "updated_at"
184 184 t.boolean "submitted"
185 185 end
186 186
187 187 create_table "test_pairs", :force => true do |t|
188 188 t.integer "problem_id"
189 - t.text "input"
190 - t.text "solution"
189 + t.text "input", :limit => 16777215
190 + t.text "solution", :limit => 16777215
191 191 t.datetime "created_at"
192 192 t.datetime "updated_at"
193 193 t.integer "number"
194 194 end
195 195
196 196 create_table "test_requests", :force => true do |t|
197 197 t.integer "user_id"
198 198 t.integer "problem_id"
199 199 t.integer "submission_id"
200 200 t.string "input_file_name"
201 201 t.string "output_file_name"
202 202 t.string "running_stat"
203 203 t.integer "status"
204 204 t.datetime "updated_at"
205 205 t.datetime "submitted_at"
206 206 t.datetime "compiled_at"
207 207 t.text "compiler_message"
208 208 t.datetime "graded_at"
209 209 t.string "grader_comment"
210 210 t.datetime "created_at"
211 211 t.float "running_time"
212 212 t.string "exit_status"
213 213 t.integer "memory_usage"
214 214 end
215 215
216 216 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
217 217
218 218 create_table "user_contest_stats", :force => true do |t|
219 219 t.integer "user_id"
220 220 t.datetime "started_at"
221 221 t.datetime "created_at"
222 222 t.datetime "updated_at"
223 223 end
224 224
225 225 create_table "users", :force => true do |t|
226 226 t.string "login", :limit => 50
227 227 t.string "full_name"
228 228 t.string "hashed_password"
229 229 t.string "salt", :limit => 5
230 230 t.string "alias"
231 231 t.string "email"
232 232 t.integer "site_id"
233 233 t.integer "country_id"
234 234 t.boolean "activated", :default => false
235 235 t.datetime "created_at"
236 236 t.datetime "updated_at"
237 237 t.string "member1_full_name"
238 238 t.string "member2_full_name"
239 239 t.string "member3_full_name"
240 240 t.boolean "high_school"
241 241 t.string "member1_school_name"
242 242 t.string "member2_school_name"
243 243 t.string "member3_school_name"
244 244 end
245 245
246 246 add_index "users", ["login"], :name => "index_users_on_login", :unique => true
247 247
248 248 end
You need to be logged in to leave comments. Login now