Description:
Merge branch 'master' into codejom: added individual contest mode
Commit status:
[Not Reviewed]
References:
merge default
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r218:915a5c2d7529 - - 22 files changed: 274 inserted, 58 deleted

@@ -0,0 +1,30
1 + class ContestsController < ApplicationController
2 +
3 + before_filter :admin_authorization
4 +
5 + def index
6 + end
7 +
8 + def user_stat
9 + if not Configuration.indv_contest_mode?
10 + redirect_to :action => 'index' and return
11 + end
12 +
13 + @users = User.find(:all)
14 + @start_times = {}
15 + UserContestStat.find(:all).each do |stat|
16 + @start_times[stat.user_id] = stat.started_at
17 + end
18 + end
19 +
20 + def clear_all_stat
21 + if not Configuration.indv_contest_mode?
22 + redirect_to :action => 'index' and return
23 + end
24 +
25 + UserContestStat.delete_all()
26 + flash[:notice] = 'All start time statistic cleared.'
27 + redirect_to :action => 'index'
28 + end
29 +
30 + end
@@ -0,0 +1,2
1 + module ContestsHelper
2 + end
@@ -0,0 +1,14
1 + class UserContestStat < ActiveRecord::Base
2 +
3 + belongs_to :user
4 +
5 + def self.update_user_start_time(user)
6 + stat = user.contest_stat
7 + if stat == nil
8 + stat = UserContestStat.new(:user => user,
9 + :started_at => Time.now.gmtime)
10 + stat.save
11 + end
12 + end
13 +
14 + end
@@ -0,0 +1,4
1 + .submitbox
2 + %b Menu:
3 + = link_to '[View user start time]', :action => 'user_stat'
4 + = link_to '[Clear all start times]', {:action => 'clear_all_stat'}, {:confirm => 'Do you really want to clear all start time statistics?'}
@@ -0,0 +1,9
1 + %h1 Contest management
2 +
3 + - if (not Configuration.contest_mode?) and (not Configuration.indv_contest_mode?)
4 + Currently the system is not running in contest mode.
5 + - elsif Configuration.contest_mode?
6 + System running in normal contest mode.
7 + - else
8 + System running in individual contest mode.
9 + = render :partial => 'indv_contest_mode_index'
@@ -0,0 +1,24
1 + %h1 Individual Contest: User start time
2 +
3 + If you want to restart contest, you may want to
4 + %b
5 + = link_to '[clear all start times]', {:action => 'clear_all_stat'}, {:confirm => 'Do you really want to clear all start time statistics?'}
6 + so that users can participate again.
7 +
8 + %table.info
9 + %tr.info-head
10 + %th Login
11 + %th Start time
12 + %th Time left
13 + %th
14 + - @users.each_with_index do |user,i|
15 + %tr{:class => 'info-' + cycle('even','odd')}
16 + %td= user.login
17 + %td
18 + - if @start_times.has_key? user.id
19 + = @start_times[user.id]
20 + - else
21 + n/a
22 + %td= format_short_duration(user.contest_time_left)
23 + %td
24 + = link_to '[reset]', {:action => 'clear_stat', :id => user.id}, :confirm => 'Are you sure?'
@@ -0,0 +1,9
1 + class AddDescriptionToConfig < ActiveRecord::Migration
2 + def self.up
3 + add_column :configurations, :description, :text
4 + end
5 +
6 + def self.down
7 + remove_column :configurations, :description
8 + end
9 + end
@@ -0,0 +1,14
1 + class CreateUserContestStats < ActiveRecord::Migration
2 + def self.up
3 + create_table :user_contest_stats do |t|
4 + t.integer :user_id
5 + t.timestamp :started_at
6 +
7 + t.timestamps
8 + end
9 + end
10 +
11 + def self.down
12 + drop_table :user_contest_stats
13 + end
14 + end
@@ -0,0 +1,9
1 + # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2 +
3 + one:
4 + user_id: 1
5 + started_at: 2010-01-24 12:44:58
6 +
7 + two:
8 + user_id: 1
9 + started_at: 2010-01-24 12:44:58
@@ -0,0 +1,8
1 + require 'test_helper'
2 +
3 + class ContestsControllerTest < ActionController::TestCase
4 + # Replace this with your real tests.
5 + test "the truth" do
6 + assert true
7 + end
8 + end
@@ -0,0 +1,4
1 + require 'test_helper'
2 +
3 + class ContestsHelperTest < ActionView::TestCase
4 + end
@@ -0,0 +1,8
1 + require 'test_helper'
2 +
3 + class UserContestStatTest < ActiveSupport::TestCase
4 + # Replace this with your real tests.
5 + test "the truth" do
6 + assert true
7 + end
8 + end
@@ -59,14 +59,14
59 end
59 end
60
60
61 def verify_time_limit
61 def verify_time_limit
62 return true if session[:user_id]==nil
62 return true if session[:user_id]==nil
63 user = User.find(session[:user_id], :include => :site)
63 user = User.find(session[:user_id], :include => :site)
64 return true if user==nil or user.site == nil
64 return true if user==nil or user.site == nil
65 - if user.site.finished?
65 + if user.contest_finished?
66 - flash[:notice] = 'Error: the contest on your site is over.'
66 + flash[:notice] = 'Error: the contest you are participating is over.'
67 redirect_to :back
67 redirect_to :back
68 return false
68 return false
69 end
69 end
70 return true
70 return true
71 end
71 end
72
72
@@ -6,18 +6,15
6 redirect_to :controller => 'main', :action => 'login'
6 redirect_to :controller => 'main', :action => 'login'
7 end
7 end
8
8
9 def login
9 def login
10 if user = User.authenticate(params[:login], params[:password])
10 if user = User.authenticate(params[:login], params[:password])
11 session[:user_id] = user.id
11 session[:user_id] = user.id
12 + session[:admin] = user.admin?
13 + UserContestStat.update_user_start_time(user)
12 redirect_to :controller => 'main', :action => 'list'
14 redirect_to :controller => 'main', :action => 'list'
13 - if user.admin?
14 - session[:admin] = true
15 - else
16 - session[:admin] = false
17 - end
18 else
15 else
19 flash[:notice] = 'Wrong password'
16 flash[:notice] = 'Wrong password'
20 redirect_to :controller => 'main', :action => 'login'
17 redirect_to :controller => 'main', :action => 'login'
21 end
18 end
22 end
19 end
23
20
@@ -1,10 +1,8
1 class MainController < ApplicationController
1 class MainController < ApplicationController
2
2
3 - SYSTEM_MODE_CONF_KEY = 'system.mode'
4 -
5 before_filter :authenticate, :except => [:index, :login]
3 before_filter :authenticate, :except => [:index, :login]
6 before_filter :check_viewability, :except => [:index, :login]
4 before_filter :check_viewability, :except => [:index, :login]
7
5
8 # COMMENTED OUT: filter in each action instead
6 # COMMENTED OUT: filter in each action instead
9 # before_filter :verify_time_limit, :only => [:submit]
7 # before_filter :verify_time_limit, :only => [:submit]
10
8
@@ -56,14 +54,13
56 if (params['file']) and (params['file']!='')
54 if (params['file']) and (params['file']!='')
57 @submission.source = params['file'].read
55 @submission.source = params['file'].read
58 @submission.source_filename = params['file'].original_filename
56 @submission.source_filename = params['file'].original_filename
59 end
57 end
60 @submission.submitted_at = Time.new.gmtime
58 @submission.submitted_at = Time.new.gmtime
61
59
62 - if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' and
60 + if Configuration.time_limit_mode? and user.contest_finished?
63 - user.site!=nil and user.site.finished?
64 @submission.errors.add_to_base "The contest is over."
61 @submission.errors.add_to_base "The contest is over."
65 prepare_list_information
62 prepare_list_information
66 render :action => 'list' and return
63 render :action => 'list' and return
67 end
64 end
68
65
69 if @submission.valid?
66 if @submission.valid?
@@ -1,10 +1,8
1 class TestController < ApplicationController
1 class TestController < ApplicationController
2
2
3 - SYSTEM_MODE_CONF_KEY = 'system.mode'
4 -
5 before_filter :authenticate, :check_viewability
3 before_filter :authenticate, :check_viewability
6
4
7 #
5 #
8 # COMMENT OUT: filter in each action instead
6 # COMMENT OUT: filter in each action instead
9 #
7 #
10 # before_filter :verify_time_limit, :only => [:submit]
8 # before_filter :verify_time_limit, :only => [:submit]
@@ -23,14 +21,14
23
21
24 if @submitted_test_request.errors.length != 0
22 if @submitted_test_request.errors.length != 0
25 prepare_index_information
23 prepare_index_information
26 render :action => 'index' and return
24 render :action => 'index' and return
27 end
25 end
28
26
29 - if Configuration[SYSTEM_MODE_CONF_KEY]=='contest'
27 + if Configuration.time_limit_mode?
30 - if @user.site!=nil and @user.site.finished?
28 + if @user.contest_finished?
31 @submitted_test_request.errors.add_to_base('Contest is over.')
29 @submitted_test_request.errors.add_to_base('Contest is over.')
32 prepare_index_information
30 prepare_index_information
33 render :action => 'index' and return
31 render :action => 'index' and return
34 end
32 end
35
33
36 if !Configuration.allow_test_request(@user)
34 if !Configuration.allow_test_request(@user)
@@ -1,24 +1,23
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 - SYSTEM_MODE_CONF_KEY = 'system.mode'
5 -
6 def user_header
4 def user_header
7 menu_items = ''
5 menu_items = ''
8 user = User.find(session[:user_id])
6 user = User.find(session[:user_id])
9
7
10 if (user!=nil) and (session[:admin])
8 if (user!=nil) and (session[:admin])
11 # admin menu
9 # admin menu
12 menu_items << "<b>Administrative task:</b> "
10 menu_items << "<b>Administrative task:</b> "
13 append_to menu_items, '[Announcements]', 'announcements', 'index'
11 append_to menu_items, '[Announcements]', 'announcements', 'index'
14 append_to menu_items, '[Msg console]', 'messages', 'console'
12 append_to menu_items, '[Msg console]', 'messages', 'console'
15 - append_to menu_items, '[Problem admin]', 'problems', 'index'
13 + append_to menu_items, '[Problems]', 'problems', 'index'
16 - append_to menu_items, '[User admin]', 'user_admin', 'index'
14 + append_to menu_items, '[Users]', 'user_admin', 'index'
17 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
15 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
18 append_to menu_items, '[Graders]', 'graders', 'list'
16 append_to menu_items, '[Graders]', 'graders', 'list'
17 + append_to menu_items, '[Contests]', 'contests', 'index'
19 append_to menu_items, '[Sites]', 'sites', 'index'
18 append_to menu_items, '[Sites]', 'sites', 'index'
20 append_to menu_items, '[System config]', 'configurations', 'index'
19 append_to menu_items, '[System config]', 'configurations', 'index'
21 menu_items << "<br/>"
20 menu_items << "<br/>"
22 end
21 end
23
22
24 # main page
23 # main page
@@ -54,12 +53,18
54 (time.year != now.year)
53 (time.year != now.year)
55 st = time.strftime("%x ")
54 st = time.strftime("%x ")
56 end
55 end
57 st + time.strftime("%X")
56 st + time.strftime("%X")
58 end
57 end
59
58
59 + def format_short_duration(duration)
60 + return '' if duration==nil
61 + d = duration.to_f
62 + return Time.at(d).gmtime.strftime("%X")
63 + end
64 +
60 def read_textfile(fname,max_size=2048)
65 def read_textfile(fname,max_size=2048)
61 begin
66 begin
62 File.open(fname).read(max_size)
67 File.open(fname).read(max_size)
63 rescue
68 rescue
64 nil
69 nil
65 end
70 end
@@ -68,33 +73,31
68 def user_title_bar(user)
73 def user_title_bar(user)
69 header = ''
74 header = ''
70 time_left = ''
75 time_left = ''
71
76
72 #
77 #
73 # if the contest is over
78 # if the contest is over
74 - if Configuration[SYSTEM_MODE_CONF_KEY]=='contest'
79 + if Configuration.time_limit_mode?
75 - if user.site!=nil and user.site.finished?
80 + if user.contest_finished?
76 header = <<CONTEST_OVER
81 header = <<CONTEST_OVER
77 <tr><td colspan="2" align="center">
82 <tr><td colspan="2" align="center">
78 <span class="contest-over-msg">THE CONTEST IS OVER</span>
83 <span class="contest-over-msg">THE CONTEST IS OVER</span>
79 </td></tr>
84 </td></tr>
80 CONTEST_OVER
85 CONTEST_OVER
81 end
86 end
82 - if !user.site.started
87 + if !user.contest_started?
83 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
88 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
84 else
89 else
85 - if user.site!=nil
90 + time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
86 - time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
91 + " #{format_short_duration(user.contest_time_left)}"
87 - " #{Time.at(user.site.time_left).gmtime.strftime("%X")}"
88 - end
89 end
92 end
90 end
93 end
91
94
92 #
95 #
93 # if the contest is in the anaysis mode
96 # if the contest is in the anaysis mode
94 - if Configuration[SYSTEM_MODE_CONF_KEY]=='analysis'
97 + if Configuration.analysis_mode?
95 header = <<ANALYSISMODE
98 header = <<ANALYSISMODE
96 <tr><td colspan="2" align="center">
99 <tr><td colspan="2" align="center">
97 <span class="contest-over-msg">ANALYSIS MODE</span>
100 <span class="contest-over-msg">ANALYSIS MODE</span>
98 </td></tr>
101 </td></tr>
99 ANALYSISMODE
102 ANALYSISMODE
100 end
103 end
@@ -56,15 +56,14
56 ((user.site.started!=true) or (user.site.finished?))
56 ((user.site.started!=true) or (user.site.finished?))
57 end
57 end
58 return true
58 return true
59 end
59 end
60
60
61 def self.show_tasks_to?(user)
61 def self.show_tasks_to?(user)
62 - mode = get(SYSTEM_MODE_CONF_KEY)
62 + if time_limit_mode?
63 - if (mode=='contest')
63 + return false if not user.contest_started?
64 - return false if (user.site!=nil) and (user.site.started!=true)
65 end
64 end
66 return true
65 return true
67 end
66 end
68
67
69 def self.show_grading_result
68 def self.show_grading_result
70 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
69 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
@@ -86,16 +85,54
86 if @@task_grading_info==nil
85 if @@task_grading_info==nil
87 read_grading_info
86 read_grading_info
88 end
87 end
89 return @@task_grading_info
88 return @@task_grading_info
90 end
89 end
91
90
92 - def self.contest_mode
91 + def self.standard_mode?
92 + return get(SYSTEM_MODE_CONF_KEY) == 'standard'
93 + end
94 +
95 + def self.contest_mode?
93 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
96 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
94 end
97 end
95
98
99 + def self.indv_contest_mode?
100 + return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
101 + end
102 +
103 + def self.time_limit_mode?
104 + mode = get(SYSTEM_MODE_CONF_KEY)
105 + return ((mode == 'contest') or (mode == 'indv-contest'))
106 + end
107 +
108 + def self.analysis_mode?
109 + return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
110 + end
111 +
112 + def self.contest_time_limit
113 + contest_time_str = Configuration['contest.time_limit']
114 +
115 + if not defined? @@contest_time_str
116 + @@contest_time_str = nil
117 + end
118 +
119 + if @@contest_time_str != contest_time_str
120 + @@contest_time_str = contest_time_str
121 + if tmatch = /(\d+):(\d+)/.match(contest_time_str)
122 + h = tmatch[1].to_i
123 + m = tmatch[2].to_i
124 +
125 + @@contest_time = h.hour + m.minute
126 + else
127 + @@contest_time = nil
128 + end
129 + end
130 + return @@contest_time
131 + end
132 +
96 protected
133 protected
97
134
98 def self.convert_type(val,type)
135 def self.convert_type(val,type)
99 case type
136 case type
100 when 'string'
137 when 'string'
101 return val
138 return val
@@ -7,48 +7,40
7 if !self.started
7 if !self.started
8 self.start_time = nil
8 self.start_time = nil
9 end
9 end
10 end
10 end
11
11
12 def time_left
12 def time_left
13 - contest_time = Configuration['contest.time_limit']
13 + contest_time = Configuration.contest_time_limit
14 - if tmatch = /(\d+):(\d+)/.match(contest_time)
15 - h = tmatch[1].to_i
16 - m = tmatch[2].to_i
17 -
18 - contest_time = h.hour + m.minute
19
14
20 - return contest_time if !self.started
15 + return nil if contest_time == nil
16 +
17 + return contest_time if !self.started
21
18
22 - current_time = Time.now.gmtime
19 + current_time = Time.now.gmtime
23 - if self.start_time!=nil
20 + if self.start_time!=nil
24 - finish_time = self.start_time + contest_time
21 + finish_time = self.start_time + contest_time
25 - else
22 + else
26 - finish_time = current_time + contest_time
23 + finish_time = current_time + contest_time
27 - end
24 + end
28
25
29 - if current_time > finish_time
26 + if current_time > finish_time
30 - return current_time - current_time
27 + return current_time - current_time
31 - else
32 - finish_time - current_time
33 - end
34 else
28 else
35 - nil
29 + return finish_time - current_time
36 end
30 end
37 end
31 end
38
32
39 def finished?
33 def finished?
40 if !self.started
34 if !self.started
41 return false
35 return false
42 end
36 end
43
37
44 - contest_time = Configuration['contest.time_limit']
38 + contest_time = Configuration.contest_time_limit
45 - if tmatch = /(\d+):(\d+)/.match(contest_time)
39 + if contest_time!=nil
46 - h = tmatch[1].to_i
40 + return Time.now.gmtime > (self.start_time + contest_time)
47 - m = tmatch[2].to_i
48 - return Time.now.gmtime > (self.start_time + h.hour + m.minute)
49 else
41 else
50 false
42 false
51 end
43 end
52 end
44 end
53
45
54 end
46 end
@@ -16,12 +16,14
16 :foreign_key => "receiver_id",
16 :foreign_key => "receiver_id",
17 :order => 'created_at DESC'
17 :order => 'created_at DESC'
18
18
19 has_many :test_pair_assignments, :dependent => :delete_all
19 has_many :test_pair_assignments, :dependent => :delete_all
20 has_many :submission_statuses
20 has_many :submission_statuses
21
21
22 + has_one :contest_stat, :class_name => "UserContestStat"
23 +
22 belongs_to :site
24 belongs_to :site
23 belongs_to :country
25 belongs_to :country
24
26
25 named_scope :activated_users, :conditions => {:activated => true}
27 named_scope :activated_users, :conditions => {:activated => true}
26
28
27 validates_presence_of :login
29 validates_presence_of :login
@@ -168,12 +170,60
168
170
169 def self.find_non_admin_with_prefix(prefix='')
171 def self.find_non_admin_with_prefix(prefix='')
170 users = User.find(:all)
172 users = User.find(:all)
171 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
173 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
172 end
174 end
173
175
176 + # Contest information
177 +
178 + def contest_time_left
179 + if Configuration.contest_mode?
180 + return nil if site==nil
181 + return site.time_left
182 + elsif Configuration.indv_contest_mode?
183 + time_limit = Configuration.contest_time_limit
184 + if contest_stat==nil
185 + return (Time.now.gmtime + time_limit) - Time.now.gmtime
186 + else
187 + finish_time = contest_stat.started_at + time_limit
188 + current_time = Time.now.gmtime
189 + if current_time > finish_time
190 + return 0
191 + else
192 + return finish_time - current_time
193 + end
194 + end
195 + else
196 + return nil
197 + end
198 + end
199 +
200 + def contest_finished?
201 + if Configuration.contest_mode?
202 + return false if site==nil
203 + return site.finished?
204 + elsif Configuration.indv_contest_mode?
205 + time_limit = Configuration.contest_time_limit
206 +
207 + return false if contest_stat==nil
208 +
209 + return contest_time_left == 0
210 + else
211 + return false
212 + end
213 + end
214 +
215 + def contest_started?
216 + if Configuration.contest_mode?
217 + return true if site==nil
218 + return site.started
219 + else
220 + return true
221 + end
222 + end
223 +
174 protected
224 protected
175 def encrypt_new_password
225 def encrypt_new_password
176 return if password.blank?
226 return if password.blank?
177 self.salt = (10+rand(90)).to_s
227 self.salt = (10+rand(90)).to_s
178 self.hashed_password = User.encrypt(self.password,self.salt)
228 self.hashed_password = User.encrypt(self.password,self.salt)
179 end
229 end
@@ -10,14 +10,13
10 Announcements
10 Announcements
11 #announcementbox-body
11 #announcementbox-body
12 = render :partial => 'announcement', :collection => @announcements
12 = render :partial => 'announcement', :collection => @announcements
13
13
14 %hr/
14 %hr/
15
15
16 - - if (Configuration.contest_mode) and (@user.site!=nil) |
16 + - if (Configuration.contest_mode?) and (@user.site!=nil) and (@user.site.started!=true)
17 - and (@user.site.started!=true)
18 %p=t 'main.start_soon'
17 %p=t 'main.start_soon'
19
18
20 - if Configuration.show_tasks_to?(@user)
19 - if Configuration.show_tasks_to?(@user)
21 .problem-list
20 .problem-list
22 = render :partial => 'problem_title', :collection => @problems, :as => :problem
21 = render :partial => 'problem_title', :collection => @problems, :as => :problem
23 .problem-content
22 .problem-content
@@ -6,13 +6,13
6 # to create the application database on another system, you should be using db:schema:load, not running
6 # to create the application database on another system, you should be using db:schema:load, not running
7 # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
7 # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8 # you'll amass, the slower it'll run and the greater likelihood for issues).
8 # you'll amass, the slower it'll run and the greater likelihood for issues).
9 #
9 #
10 # It's strongly recommended to check this file into your version control system.
10 # It's strongly recommended to check this file into your version control system.
11
11
12 - ActiveRecord::Schema.define(:version => 20100123154326) do
12 + ActiveRecord::Schema.define(:version => 20100124054458) do
13
13
14 create_table "announcements", :force => true do |t|
14 create_table "announcements", :force => true do |t|
15 t.string "author"
15 t.string "author"
16 t.text "body"
16 t.text "body"
17 t.boolean "published"
17 t.boolean "published"
18 t.datetime "created_at"
18 t.datetime "created_at"
@@ -25,12 +25,13
25 create_table "configurations", :force => true do |t|
25 create_table "configurations", :force => true do |t|
26 t.string "key"
26 t.string "key"
27 t.string "value_type"
27 t.string "value_type"
28 t.string "value"
28 t.string "value"
29 t.datetime "created_at"
29 t.datetime "created_at"
30 t.datetime "updated_at"
30 t.datetime "updated_at"
31 + t.text "description"
31 end
32 end
32
33
33 create_table "countries", :force => true do |t|
34 create_table "countries", :force => true do |t|
34 t.string "name"
35 t.string "name"
35 t.datetime "created_at"
36 t.datetime "created_at"
36 t.datetime "updated_at"
37 t.datetime "updated_at"
@@ -203,12 +204,19
203 t.string "exit_status"
204 t.string "exit_status"
204 t.integer "memory_usage"
205 t.integer "memory_usage"
205 end
206 end
206
207
207 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
208 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
208
209
210 + create_table "user_contest_stats", :force => true do |t|
211 + t.integer "user_id"
212 + t.datetime "started_at"
213 + t.datetime "created_at"
214 + t.datetime "updated_at"
215 + end
216 +
209 create_table "users", :force => true do |t|
217 create_table "users", :force => true do |t|
210 t.string "login", :limit => 50
218 t.string "login", :limit => 50
211 t.string "full_name"
219 t.string "full_name"
212 t.string "hashed_password"
220 t.string "hashed_password"
213 t.string "salt", :limit => 5
221 t.string "salt", :limit => 5
214 t.string "alias"
222 t.string "alias"
You need to be logged in to leave comments. Login now