Description:
added individual contest mode
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r217:f2fe2340b1dc - - 24 files changed: 299 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,2
1 + .sass-cache
2 +
@@ -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
@@ -1,74 +1,74
1 1 # Filters added to this controller apply to all controllers in the application.
2 2 # Likewise, all the methods added will be available for all controllers.
3 3
4 4 class ApplicationController < ActionController::Base
5 5
6 6 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
7 7
8 8 def admin_authorization
9 9 return false unless authenticate
10 10 user = User.find(session[:user_id], :include => ['roles'])
11 11 redirect_to :controller => 'main', :action => 'login' unless user.admin?
12 12 end
13 13
14 14 def authorization_by_roles(allowed_roles)
15 15 return false unless authenticate
16 16 user = User.find(session[:user_id])
17 17 unless user.roles.detect { |role| allowed_roles.member?(role.name) }
18 18 flash[:notice] = 'You are not authorized to view the page you requested'
19 19 redirect_to :controller => 'main', :action => 'login'
20 20 return false
21 21 end
22 22 end
23 23
24 24 protected
25 25
26 26 def authenticate
27 27 unless session[:user_id]
28 28 redirect_to :controller => 'main', :action => 'login'
29 29 return false
30 30 end
31 31
32 32 #Configuration.reload
33 33 # check if run in single user mode
34 34 if (Configuration[SINGLE_USER_MODE_CONF_KEY])
35 35 user = User.find(session[:user_id])
36 36 if user==nil or user.login != 'root'
37 37 redirect_to :controller => 'main', :action => 'login'
38 38 return false
39 39 end
40 40 end
41 41
42 42 return true
43 43 end
44 44
45 45 def authorization
46 46 return false unless authenticate
47 47 user = User.find(session[:user_id])
48 48 unless user.roles.detect { |role|
49 49 role.rights.detect{ |right|
50 50 right.controller == self.class.controller_name and
51 51 (right.action == 'all' or right.action == action_name)
52 52 }
53 53 }
54 54 flash[:notice] = 'You are not authorized to view the page you requested'
55 55 #request.env['HTTP_REFERER'] ? (redirect_to :back) : (redirect_to :controller => 'login')
56 56 redirect_to :controller => 'main', :action => 'login'
57 57 return false
58 58 end
59 59 end
60 60
61 61 def verify_time_limit
62 62 return true if session[:user_id]==nil
63 63 user = User.find(session[:user_id], :include => :site)
64 64 return true if user==nil or user.site == nil
65 - if user.site.finished?
66 - flash[:notice] = 'Error: the contest on your site is over.'
65 + if user.contest_finished?
66 + flash[:notice] = 'Error: the contest you are participating is over.'
67 67 redirect_to :back
68 68 return false
69 69 end
70 70 return true
71 71 end
72 72
73 73 end
74 74
@@ -1,43 +1,40
1 1 class LoginController < ApplicationController
2 2
3 3 def index
4 4 # show login screen
5 5 reset_session
6 6 redirect_to :controller => 'main', :action => 'login'
7 7 end
8 8
9 9 def login
10 10 if user = User.authenticate(params[:login], params[:password])
11 11 session[:user_id] = user.id
12 + session[:admin] = user.admin?
13 + UserContestStat.update_user_start_time(user)
12 14 redirect_to :controller => 'main', :action => 'list'
13 - if user.admin?
14 - session[:admin] = true
15 - else
16 - session[:admin] = false
17 - end
18 15 else
19 16 flash[:notice] = 'Wrong password'
20 17 redirect_to :controller => 'main', :action => 'login'
21 18 end
22 19 end
23 20
24 21 def site_login
25 22 begin
26 23 site = Site.find(params[:login][:site_id])
27 24 rescue ActiveRecord::RecordNotFound
28 25 site = nil
29 26 end
30 27 if site==nil
31 28 flash[:notice] = 'Wrong site'
32 29 redirect_to :controller => 'main', :action => 'login' and return
33 30 end
34 31 if (site.password) and (site.password == params[:login][:password])
35 32 session[:site_id] = site.id
36 33 redirect_to :controller => 'site', :action => 'index'
37 34 else
38 35 flash[:notice] = 'Wrong site password'
39 36 redirect_to :controller => 'site', :action => 'login'
40 37 end
41 38 end
42 39
43 40 end
@@ -1,159 +1,156
1 1 class MainController < ApplicationController
2 2
3 - SYSTEM_MODE_CONF_KEY = 'system.mode'
4 -
5 3 before_filter :authenticate, :except => [:index, :login]
6 4 before_filter :check_viewability, :except => [:index, :login]
7 5
8 6 # COMMENTED OUT: filter in each action instead
9 7 # before_filter :verify_time_limit, :only => [:submit]
10 8
11 9 verify :method => :post, :only => [:submit],
12 10 :redirect_to => { :action => :index }
13 11
14 12 # COMMENT OUT: only need when having high load
15 13 # caches_action :index, :login
16 14
17 15 # NOTE: This method is not actually needed, 'config/routes.rb' has
18 16 # assigned action login as a default action.
19 17 def index
20 18 redirect_to :action => 'login'
21 19 end
22 20
23 21 def login
24 22 saved_notice = flash[:notice]
25 23 reset_session
26 24 flash[:notice] = saved_notice
27 25
28 26 # EXPERIMENT:
29 27 # Hide login if in single user mode and the url does not
30 28 # explicitly specify /login
31 29 #
32 30 # logger.info "PATH: #{request.path}"
33 31 # if Configuration['system.single_user_mode'] and
34 32 # request.path!='/main/login'
35 33 # @hidelogin = true
36 34 # end
37 35
38 36 @announcements = Announcement.find_for_frontpage
39 37 render :action => 'login', :layout => 'empty'
40 38 end
41 39
42 40 def list
43 41 prepare_list_information
44 42 end
45 43
46 44 def help
47 45 @user = User.find(session[:user_id])
48 46 end
49 47
50 48 def submit
51 49 user = User.find(session[:user_id])
52 50
53 51 @submission = Submission.new(params[:submission])
54 52 @submission.user = user
55 53 @submission.language_id = 0
56 54 if (params['file']) and (params['file']!='')
57 55 @submission.source = params['file'].read
58 56 @submission.source_filename = params['file'].original_filename
59 57 end
60 58 @submission.submitted_at = Time.new.gmtime
61 59
62 - if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' and
63 - user.site!=nil and user.site.finished?
60 + if Configuration.time_limit_mode? and user.contest_finished?
64 61 @submission.errors.add_to_base "The contest is over."
65 62 prepare_list_information
66 63 render :action => 'list' and return
67 64 end
68 65
69 66 if @submission.valid?
70 67 if @submission.save == false
71 68 flash[:notice] = 'Error saving your submission'
72 69 elsif Task.create(:submission_id => @submission.id,
73 70 :status => Task::STATUS_INQUEUE) == false
74 71 flash[:notice] = 'Error adding your submission to task queue'
75 72 end
76 73 else
77 74 prepare_list_information
78 75 render :action => 'list' and return
79 76 end
80 77 redirect_to :action => 'list'
81 78 end
82 79
83 80 def source
84 81 submission = Submission.find(params[:id])
85 82 if submission.user_id == session[:user_id]
86 83 send_data(submission.source,
87 84 {:filename => submission.download_filename,
88 85 :type => 'text/plain'})
89 86 else
90 87 flash[:notice] = 'Error viewing source'
91 88 redirect_to :action => 'list'
92 89 end
93 90 end
94 91
95 92 def compiler_msg
96 93 @submission = Submission.find(params[:id])
97 94 if @submission.user_id == session[:user_id]
98 95 render :action => 'compiler_msg', :layout => 'empty'
99 96 else
100 97 flash[:notice] = 'Error viewing source'
101 98 redirect_to :action => 'list'
102 99 end
103 100 end
104 101
105 102 def submission
106 103 @user = User.find(session[:user_id])
107 104 @problems = Problem.find_available_problems
108 105 if params[:id]==nil
109 106 @problem = nil
110 107 @submissions = nil
111 108 else
112 109 @problem = Problem.find_by_name(params[:id])
113 110 if not @problem.available
114 111 redirect_to :action => 'list'
115 112 flash[:notice] = 'Error: submissions for that problem are not viewable.'
116 113 return
117 114 end
118 115 @submissions = Submission.find_all_by_user_problem(@user.id, @problem.id)
119 116 end
120 117 end
121 118
122 119 def result
123 120 if !Configuration.show_grading_result
124 121 redirect_to :action => 'list' and return
125 122 end
126 123 @user = User.find(session[:user_id])
127 124 @submission = Submission.find(params[:id])
128 125 if @submission.user!=@user
129 126 flash[:notice] = 'You are not allowed to view result of other users.'
130 127 redirect_to :action => 'list' and return
131 128 end
132 129 prepare_grading_result(@submission)
133 130 end
134 131
135 132 def load_output
136 133 if !Configuration.show_grading_result or params[:num]==nil
137 134 redirect_to :action => 'list' and return
138 135 end
139 136 @user = User.find(session[:user_id])
140 137 @submission = Submission.find(params[:id])
141 138 if @submission.user!=@user
142 139 flash[:notice] = 'You are not allowed to view result of other users.'
143 140 redirect_to :action => 'list' and return
144 141 end
145 142 case_num = params[:num].to_i
146 143 out_filename = output_filename(@user.login,
147 144 @submission.problem.name,
148 145 @submission.id,
149 146 case_num)
150 147 if !FileTest.exists?(out_filename)
151 148 flash[:notice] = 'Output not found.'
152 149 redirect_to :action => 'list' and return
153 150 end
154 151
155 152 response.headers['Content-Type'] = "application/force-download"
156 153 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
157 154 response.headers["X-Sendfile"] = out_filename
158 155 response.headers['Content-length'] = File.size(out_filename)
159 156 render :nothing => true
@@ -1,120 +1,118
1 1 class TestController < ApplicationController
2 2
3 - SYSTEM_MODE_CONF_KEY = 'system.mode'
4 -
5 3 before_filter :authenticate, :check_viewability
6 4
7 5 #
8 6 # COMMENT OUT: filter in each action instead
9 7 #
10 8 # before_filter :verify_time_limit, :only => [:submit]
11 9
12 10 verify :method => :post, :only => [:submit],
13 11 :redirect_to => { :action => :index }
14 12
15 13 def index
16 14 prepare_index_information
17 15 end
18 16
19 17 def submit
20 18 @user = User.find(session[:user_id])
21 19
22 20 @submitted_test_request = TestRequest.new_from_form_params(@user,params[:test_request])
23 21
24 22 if @submitted_test_request.errors.length != 0
25 23 prepare_index_information
26 24 render :action => 'index' and return
27 25 end
28 26
29 - if Configuration[SYSTEM_MODE_CONF_KEY]=='contest'
30 - if @user.site!=nil and @user.site.finished?
27 + if Configuration.time_limit_mode?
28 + if @user.contest_finished?
31 29 @submitted_test_request.errors.add_to_base('Contest is over.')
32 30 prepare_index_information
33 31 render :action => 'index' and return
34 32 end
35 33
36 34 if !Configuration.allow_test_request(@user)
37 35 prepare_index_information
38 36 flash[:notice] = 'Test request is not allowed during the last 30 minutes'
39 37 redirect_to :action => 'index' and return
40 38 end
41 39 end
42 40
43 41 if @submitted_test_request.save
44 42 redirect_to :action => 'index'
45 43 else
46 44 prepare_index_information
47 45 render :action => 'index'
48 46 end
49 47 end
50 48
51 49 def read
52 50 user = User.find(session[:user_id])
53 51 begin
54 52 test_request = TestRequest.find(params[:id])
55 53 rescue
56 54 test_request = nil
57 55 end
58 56 if test_request==nil or test_request.user_id != user.id
59 57 flash[:notice] = 'Invalid output'
60 58 redirect_to :action => 'index'
61 59 return
62 60 end
63 61 if test_request.output_file_name!=nil
64 62 data = File.open(test_request.output_file_name).read(2048)
65 63 if data==nil
66 64 data=""
67 65 end
68 66 send_data(data,
69 67 {:filename => 'output.txt',
70 68 :type => 'text/plain'})
71 69 return
72 70 end
73 71 redirect_to :action => 'index'
74 72 end
75 73
76 74 def result
77 75 @user = User.find(session[:user_id])
78 76 begin
79 77 @test_request = TestRequest.find(params[:id])
80 78 rescue
81 79 @test_request = nil
82 80 end
83 81 if @test_request==nil or @test_request.user_id != @user.id
84 82 flash[:notice] = 'Invalid request'
85 83 redirect_to :action => 'index'
86 84 return
87 85 end
88 86 end
89 87
90 88 protected
91 89
92 90 def prepare_index_information
93 91 @user = User.find(session[:user_id])
94 92 @submissions = Submission.find_last_for_all_available_problems(@user.id)
95 93 all_problems = @submissions.collect { |submission| submission.problem }
96 94 @problems = []
97 95 all_problems.each do |problem|
98 96 if problem.test_allowed
99 97 @problems << problem
100 98 end
101 99 end
102 100 @test_requests = []
103 101 @user.test_requests.each do |ts|
104 102 if ts.problem and ts.problem.available
105 103 @test_requests << ts
106 104 end
107 105 end
108 106 end
109 107
110 108 def check_viewability
111 109 user = User.find(session[:user_id])
112 110 if !Configuration.show_tasks_to?(user)
113 111 redirect_to :controller => 'main', :action => 'list'
114 112 end
115 113 if (!Configuration.show_submitbox_to?(user)) and (action_name=='submit')
116 114 redirect_to :controller => 'test', :action => 'index'
117 115 end
118 116 end
119 117
120 118 end
@@ -1,124 +1,127
1 1 # Methods added to this helper will be available to all templates in the application.
2 2 module ApplicationHelper
3 3
4 - SYSTEM_MODE_CONF_KEY = 'system.mode'
5 -
6 4 def user_header
7 5 menu_items = ''
8 6 user = User.find(session[:user_id])
9 7
10 8 if (user!=nil) and (session[:admin])
11 9 # admin menu
12 10 menu_items << "<b>Administrative task:</b> "
13 11 append_to menu_items, '[Announcements]', 'announcements', 'index'
14 12 append_to menu_items, '[Msg console]', 'messages', 'console'
15 - append_to menu_items, '[Problem admin]', 'problems', 'index'
16 - append_to menu_items, '[User admin]', 'user_admin', 'index'
13 + append_to menu_items, '[Problems]', 'problems', 'index'
14 + append_to menu_items, '[Users]', 'user_admin', 'index'
17 15 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
18 16 append_to menu_items, '[Graders]', 'graders', 'list'
17 + append_to menu_items, '[Contests]', 'contests', 'index'
19 18 append_to menu_items, '[Sites]', 'sites', 'index'
20 19 append_to menu_items, '[System config]', 'configurations', 'index'
21 20 menu_items << "<br/>"
22 21 end
23 22
24 23 # main page
25 24 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
26 25 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
27 26
28 27 if (user!=nil) and (Configuration.show_tasks_to?(user))
29 28 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
30 29 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
31 30 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
32 31 end
33 32 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
34 33
35 34 if Configuration['system.user_setting_enabled']
36 35 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
37 36 end
38 37 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
39 38
40 39 menu_items
41 40 end
42 41
43 42 def append_to(option,label, controller, action)
44 43 option << ' ' if option!=''
45 44 option << link_to_unless_current(label,
46 45 :controller => controller,
47 46 :action => action)
48 47 end
49 48
50 49 def format_short_time(time)
51 50 now = Time.now.gmtime
52 51 st = ''
53 52 if (time.yday != now.yday) or
54 53 (time.year != now.year)
55 54 st = time.strftime("%x ")
56 55 end
57 56 st + time.strftime("%X")
58 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 65 def read_textfile(fname,max_size=2048)
61 66 begin
62 67 File.open(fname).read(max_size)
63 68 rescue
64 69 nil
65 70 end
66 71 end
67 72
68 73 def user_title_bar(user)
69 74 header = ''
70 75 time_left = ''
71 76
72 77 #
73 78 # if the contest is over
74 - if Configuration[SYSTEM_MODE_CONF_KEY]=='contest'
75 - if user.site!=nil and user.site.finished?
79 + if Configuration.time_limit_mode?
80 + if user.contest_finished?
76 81 header = <<CONTEST_OVER
77 82 <tr><td colspan="2" align="center">
78 83 <span class="contest-over-msg">THE CONTEST IS OVER</span>
79 84 </td></tr>
80 85 CONTEST_OVER
81 86 end
82 - if !user.site.started
87 + if !user.contest_started?
83 88 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
84 89 else
85 - if user.site!=nil
86 - time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
87 - " #{Time.at(user.site.time_left).gmtime.strftime("%X")}"
88 - end
90 + time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
91 + " #{format_short_duration(user.contest_time_left)}"
89 92 end
90 93 end
91 94
92 95 #
93 96 # if the contest is in the anaysis mode
94 - if Configuration[SYSTEM_MODE_CONF_KEY]=='analysis'
97 + if Configuration.analysis_mode?
95 98 header = <<ANALYSISMODE
96 99 <tr><td colspan="2" align="center">
97 100 <span class="contest-over-msg">ANALYSIS MODE</span>
98 101 </td></tr>
99 102 ANALYSISMODE
100 103 end
101 104
102 105 contest_name = Configuration['contest.name']
103 106
104 107 #
105 108 # build real title bar
106 109 <<TITLEBAR
107 110 <div class="title">
108 111 <table>
109 112 #{header}
110 113 <tr>
111 114 <td class="left-col">
112 115 #{user.full_name}<br/>
113 116 #{t 'title_bar.current_time'} #{format_short_time(Time.new)}
114 117 #{time_left}
115 118 <br/>
116 119 </td>
117 120 <td class="right-col">#{contest_name}</td>
118 121 </tr>
119 122 </table>
120 123 </div>
121 124 TITLEBAR
122 125 end
123 126
124 127 end
@@ -1,135 +1,172
1 1 require 'yaml'
2 2
3 3 #
4 4 # This class also contains various login of the system.
5 5 #
6 6 class Configuration < ActiveRecord::Base
7 7
8 8 SYSTEM_MODE_CONF_KEY = 'system.mode'
9 9 TEST_REQUEST_EARLY_TIMEOUT_KEY = 'contest.test_request.early_timeout'
10 10
11 11 # set @@cache = true to only reload once.
12 12 @@cache = false
13 13
14 14 @@configurations = nil
15 15 @@task_grading_info = nil
16 16
17 17 def self.get(key)
18 18 if @@cache
19 19 if @@configurations == nil
20 20 self.read_config
21 21 end
22 22 return @@configurations[key]
23 23 else
24 24 return Configuration.read_one_key(key)
25 25 end
26 26 end
27 27
28 28 def self.[](key)
29 29 self.get(key)
30 30 end
31 31
32 32 def self.reload
33 33 self.read_config
34 34 end
35 35
36 36 def self.clear
37 37 @@configurations = nil
38 38 end
39 39
40 40 def self.cache?
41 41 @@cache
42 42 end
43 43
44 44 def self.enable_caching
45 45 @@cache = true
46 46 end
47 47
48 48 #
49 49 # View decision
50 50 #
51 51 def self.show_submitbox_to?(user)
52 52 mode = get(SYSTEM_MODE_CONF_KEY)
53 53 return false if mode=='analysis'
54 54 if (mode=='contest')
55 55 return false if (user.site!=nil) and
56 56 ((user.site.started!=true) or (user.site.finished?))
57 57 end
58 58 return true
59 59 end
60 60
61 61 def self.show_tasks_to?(user)
62 - mode = get(SYSTEM_MODE_CONF_KEY)
63 - if (mode=='contest')
64 - return false if (user.site!=nil) and (user.site.started!=true)
62 + if time_limit_mode?
63 + return false if not user.contest_started?
65 64 end
66 65 return true
67 66 end
68 67
69 68 def self.show_grading_result
70 69 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
71 70 end
72 71
73 72 def self.allow_test_request(user)
74 73 mode = get(SYSTEM_MODE_CONF_KEY)
75 74 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
76 75 if (mode=='contest')
77 76 return false if ((user.site!=nil) and
78 77 ((user.site.started!=true) or
79 78 (early_timeout and (user.site.time_left < 30.minutes))))
80 79 end
81 80 return false if mode=='analysis'
82 81 return true
83 82 end
84 83
85 84 def self.task_grading_info
86 85 if @@task_grading_info==nil
87 86 read_grading_info
88 87 end
89 88 return @@task_grading_info
90 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 96 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
94 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 133 protected
97 134
98 135 def self.convert_type(val,type)
99 136 case type
100 137 when 'string'
101 138 return val
102 139
103 140 when 'integer'
104 141 return val.to_i
105 142
106 143 when 'boolean'
107 144 return (val=='true')
108 145 end
109 146 end
110 147
111 148 def self.read_config
112 149 @@configurations = {}
113 150 Configuration.find(:all).each do |conf|
114 151 key = conf.key
115 152 val = conf.value
116 153 @@configurations[key] = Configuration.convert_type(val,conf.value_type)
117 154 end
118 155 end
119 156
120 157 def self.read_one_key(key)
121 158 conf = Configuration.find_by_key(key)
122 159 if conf
123 160 return Configuration.convert_type(conf.value,conf.value_type)
124 161 else
125 162 return nil
126 163 end
127 164 end
128 165
129 166 def self.read_grading_info
130 167 f = File.open(TASK_GRADING_INFO_FILENAME)
131 168 @@task_grading_info = YAML.load(f)
132 169 f.close
133 170 end
134 171
135 172 end
@@ -1,54 +1,46
1 1 class Site < ActiveRecord::Base
2 2
3 3 belongs_to :country
4 4 has_many :users
5 5
6 6 def clear_start_time_if_not_started
7 7 if !self.started
8 8 self.start_time = nil
9 9 end
10 10 end
11 11
12 12 def time_left
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
13 + contest_time = Configuration.contest_time_limit
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
23 - if self.start_time!=nil
24 - finish_time = self.start_time + contest_time
25 - else
26 - finish_time = current_time + contest_time
27 - end
19 + current_time = Time.now.gmtime
20 + if self.start_time!=nil
21 + finish_time = self.start_time + contest_time
22 + else
23 + finish_time = current_time + contest_time
24 + end
28 25
29 - if current_time > finish_time
30 - return current_time - current_time
31 - else
32 - finish_time - current_time
33 - end
26 + if current_time > finish_time
27 + return current_time - current_time
34 28 else
35 - nil
29 + return finish_time - current_time
36 30 end
37 31 end
38 32
39 33 def finished?
40 34 if !self.started
41 35 return false
42 36 end
43 37
44 - contest_time = Configuration['contest.time_limit']
45 - if tmatch = /(\d+):(\d+)/.match(contest_time)
46 - h = tmatch[1].to_i
47 - m = tmatch[2].to_i
48 - return Time.now.gmtime > (self.start_time + h.hour + m.minute)
38 + contest_time = Configuration.contest_time_limit
39 + if contest_time!=nil
40 + return Time.now.gmtime > (self.start_time + contest_time)
49 41 else
50 42 false
51 43 end
52 44 end
53 45
54 46 end
@@ -1,174 +1,224
1 1 require 'digest/sha1'
2 2
3 3 class User < ActiveRecord::Base
4 4
5 5 has_and_belongs_to_many :roles
6 6
7 7 has_many :test_requests, :order => "submitted_at DESC"
8 8
9 9 has_many :messages,
10 10 :class_name => "Message",
11 11 :foreign_key => "sender_id",
12 12 :order => 'created_at DESC'
13 13
14 14 has_many :replied_messages,
15 15 :class_name => "Message",
16 16 :foreign_key => "receiver_id",
17 17 :order => 'created_at DESC'
18 18
19 + has_one :contest_stat, :class_name => "UserContestStat"
20 +
19 21 belongs_to :site
20 22 belongs_to :country
21 23
22 24 named_scope :activated_users, :conditions => {:activated => true}
23 25
24 26 validates_presence_of :login
25 27 validates_uniqueness_of :login
26 28 validates_format_of :login, :with => /^[\_A-Za-z0-9]+$/
27 29 validates_length_of :login, :within => 3..30
28 30
29 31 validates_presence_of :full_name
30 32 validates_length_of :full_name, :minimum => 1
31 33
32 34 validates_presence_of :password, :if => :password_required?
33 35 validates_length_of :password, :within => 4..20, :if => :password_required?
34 36 validates_confirmation_of :password, :if => :password_required?
35 37
36 38 validates_format_of :email,
37 39 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
38 40 :if => :email_validation?
39 41 validate :uniqueness_of_email_from_activated_users,
40 42 :if => :email_validation?
41 43 validate :enough_time_interval_between_same_email_registrations,
42 44 :if => :email_validation?
43 45
44 46 # these are for ytopc
45 47 # disable for now
46 48 #validates_presence_of :province
47 49
48 50 attr_accessor :password
49 51
50 52 before_save :encrypt_new_password
51 53 before_save :assign_default_site
52 54
53 55 def self.authenticate(login, password)
54 56 user = find_by_login(login)
55 57 return user if user && user.authenticated?(password)
56 58 end
57 59
58 60 def authenticated?(password)
59 61 if self.activated
60 62 hashed_password == User.encrypt(password,self.salt)
61 63 else
62 64 false
63 65 end
64 66 end
65 67
66 68 def admin?
67 69 self.roles.detect {|r| r.name == 'admin' }
68 70 end
69 71
70 72 def email_for_editing
71 73 if self.email==nil
72 74 "(unknown)"
73 75 elsif self.email==''
74 76 "(blank)"
75 77 else
76 78 self.email
77 79 end
78 80 end
79 81
80 82 def email_for_editing=(e)
81 83 self.email=e
82 84 end
83 85
84 86 def alias_for_editing
85 87 if self.alias==nil
86 88 "(unknown)"
87 89 elsif self.alias==''
88 90 "(blank)"
89 91 else
90 92 self.alias
91 93 end
92 94 end
93 95
94 96 def alias_for_editing=(e)
95 97 self.alias=e
96 98 end
97 99
98 100 def activation_key
99 101 if self.hashed_password==nil
100 102 encrypt_new_password
101 103 end
102 104 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
103 105 end
104 106
105 107 def verify_activation_key(key)
106 108 key == activation_key
107 109 end
108 110
109 111 def self.random_password(length=5)
110 112 chars = 'abcdefghjkmnopqrstuvwxyz'
111 113 password = ''
112 114 length.times { password << chars[rand(chars.length - 1)] }
113 115 password
114 116 end
115 117
116 118 def self.find_non_admin_with_prefix(prefix='')
117 119 users = User.find(:all)
118 120 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
119 121 end
120 122
123 + # Contest information
124 +
125 + def contest_time_left
126 + if Configuration.contest_mode?
127 + return nil if site==nil
128 + return site.time_left
129 + elsif Configuration.indv_contest_mode?
130 + time_limit = Configuration.contest_time_limit
131 + if contest_stat==nil
132 + return (Time.now.gmtime + time_limit) - Time.now.gmtime
133 + else
134 + finish_time = contest_stat.started_at + time_limit
135 + current_time = Time.now.gmtime
136 + if current_time > finish_time
137 + return 0
138 + else
139 + return finish_time - current_time
140 + end
141 + end
142 + else
143 + return nil
144 + end
145 + end
146 +
147 + def contest_finished?
148 + if Configuration.contest_mode?
149 + return false if site==nil
150 + return site.finished?
151 + elsif Configuration.indv_contest_mode?
152 + time_limit = Configuration.contest_time_limit
153 +
154 + return false if contest_stat==nil
155 +
156 + return contest_time_left == 0
157 + else
158 + return false
159 + end
160 + end
161 +
162 + def contest_started?
163 + if Configuration.contest_mode?
164 + return true if site==nil
165 + return site.started
166 + else
167 + return true
168 + end
169 + end
170 +
121 171 protected
122 172 def encrypt_new_password
123 173 return if password.blank?
124 174 self.salt = (10+rand(90)).to_s
125 175 self.hashed_password = User.encrypt(self.password,self.salt)
126 176 end
127 177
128 178 def assign_default_site
129 179 # have to catch error when migrating (because self.site is not available).
130 180 begin
131 181 if self.site==nil
132 182 self.site = Site.find_by_name('default')
133 183 if self.site==nil
134 184 self.site = Site.find(1) # when 'default has be renamed'
135 185 end
136 186 end
137 187 rescue
138 188 end
139 189 end
140 190
141 191 def password_required?
142 192 self.hashed_password.blank? || !self.password.blank?
143 193 end
144 194
145 195 def self.encrypt(string,salt)
146 196 Digest::SHA1.hexdigest(salt + string)
147 197 end
148 198
149 199 def uniqueness_of_email_from_activated_users
150 200 user = User.activated_users.find_by_email(self.email)
151 201 if user and (user.login != self.login)
152 202 self.errors.add_to_base("Email has already been taken")
153 203 end
154 204 end
155 205
156 206 def enough_time_interval_between_same_email_registrations
157 207 return if !self.new_record?
158 208 return if self.activated
159 209 open_user = User.find_by_email(self.email,
160 210 :order => 'created_at DESC')
161 211 if open_user and open_user.created_at and
162 212 (open_user.created_at > Time.now.gmtime - 5.minutes)
163 213 self.errors.add_to_base("There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
164 214 end
165 215 end
166 216
167 217 def email_validation?
168 218 begin
169 219 return VALIDATE_USER_EMAILS
170 220 rescue
171 221 return false
172 222 end
173 223 end
174 224 end
@@ -1,39 +1,38
1 1 - content_for :head do
2 2 = javascript_include_tag :defaults
3 3 %script{:type => 'text/javascript', :src => '/javascripts/announcement_refresh.js'}
4 4
5 5 = user_title_bar(@user)
6 6
7 7 - if @announcements.length!=0
8 8 .announcementbox
9 9 %span{:class => 'title'}
10 10 Announcements
11 11 #announcementbox-body
12 12 = render :partial => 'announcement', :collection => @announcements
13 13
14 14 - if Configuration.show_submitbox_to?(@user)
15 15 .submitbox
16 16 = error_messages_for 'submission'
17 17 = render :partial => 'submission_box'
18 18
19 19
20 20 %hr/
21 21
22 - - if (Configuration.contest_mode) and (@user.site!=nil) |
23 - and (@user.site.started!=true)
22 + - if (Configuration.contest_mode?) and (@user.site!=nil) and (@user.site.started!=true)
24 23 %p=t 'main.start_soon'
25 24
26 25 - if Configuration.show_tasks_to?(@user)
27 26 %table.info
28 27 %tr.info-head
29 28 %th
30 29 %th Tasks
31 30 %th # of sub(s)
32 31 %th Results
33 32 = render :partial => 'problem', :collection => @problems
34 33
35 34 %hr/
36 35
37 36 :javascript
38 37 Announcement.registerRefreshEventTimer();
39 38
@@ -1,204 +1,233
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 => 20100113094740) do
12 + ActiveRecord::Schema.define(:version => 20100124054458) 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 "configurations", :force => true do |t|
26 26 t.string "key"
27 27 t.string "value_type"
28 28 t.string "value"
29 29 t.datetime "created_at"
30 30 t.datetime "updated_at"
31 + t.text "description"
31 32 end
32 33
33 34 create_table "countries", :force => true do |t|
34 35 t.string "name"
35 36 t.datetime "created_at"
36 37 t.datetime "updated_at"
37 38 end
38 39
39 40 create_table "descriptions", :force => true do |t|
40 41 t.text "body"
41 42 t.boolean "markdowned"
42 43 t.datetime "created_at"
43 44 t.datetime "updated_at"
44 45 end
45 46
46 47 create_table "grader_processes", :force => true do |t|
47 48 t.string "host", :limit => 20
48 49 t.integer "pid"
49 50 t.string "mode"
50 51 t.boolean "active"
51 52 t.datetime "created_at"
52 53 t.datetime "updated_at"
53 54 t.integer "task_id"
54 55 t.string "task_type"
55 56 t.boolean "terminated"
56 57 end
57 58
58 59 add_index "grader_processes", ["host", "pid"], :name => "index_grader_processes_on_ip_and_pid"
59 60
60 61 create_table "languages", :force => true do |t|
61 62 t.string "name", :limit => 10
62 63 t.string "pretty_name"
63 64 t.string "ext", :limit => 10
64 65 t.string "common_ext"
65 66 end
66 67
67 68 create_table "messages", :force => true do |t|
68 69 t.integer "sender_id"
69 70 t.integer "receiver_id"
70 71 t.integer "replying_message_id"
71 72 t.text "body"
72 73 t.boolean "replied"
73 74 t.datetime "created_at"
74 75 t.datetime "updated_at"
75 76 end
76 77
77 78 create_table "problems", :force => true do |t|
78 79 t.string "name", :limit => 30
79 80 t.string "full_name"
80 81 t.integer "full_score"
81 82 t.date "date_added"
82 83 t.boolean "available"
83 84 t.string "url"
84 85 t.integer "description_id"
85 86 t.boolean "test_allowed"
86 87 t.boolean "output_only"
87 88 end
88 89
89 90 create_table "rights", :force => true do |t|
90 91 t.string "name"
91 92 t.string "controller"
92 93 t.string "action"
93 94 end
94 95
95 96 create_table "rights_roles", :id => false, :force => true do |t|
96 97 t.integer "right_id"
97 98 t.integer "role_id"
98 99 end
99 100
100 101 add_index "rights_roles", ["role_id"], :name => "index_rights_roles_on_role_id"
101 102
102 103 create_table "roles", :force => true do |t|
103 104 t.string "name"
104 105 end
105 106
106 107 create_table "roles_users", :id => false, :force => true do |t|
107 108 t.integer "role_id"
108 109 t.integer "user_id"
109 110 end
110 111
111 112 add_index "roles_users", ["user_id"], :name => "index_roles_users_on_user_id"
112 113
113 114 create_table "sessions", :force => true do |t|
114 115 t.string "session_id"
115 116 t.text "data"
116 117 t.datetime "updated_at"
117 118 end
118 119
119 120 add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
120 121 add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
121 122
122 123 create_table "sites", :force => true do |t|
123 124 t.string "name"
124 125 t.boolean "started"
125 126 t.datetime "start_time"
126 127 t.datetime "created_at"
127 128 t.datetime "updated_at"
128 129 t.integer "country_id"
129 130 t.string "password"
130 131 end
131 132
133 + create_table "submission_statuses", :force => true do |t|
134 + t.integer "user_id"
135 + t.integer "problem_id"
136 + t.boolean "passed"
137 + t.integer "submission_count"
138 + t.datetime "created_at"
139 + t.datetime "updated_at"
140 + end
141 +
132 142 create_table "submissions", :force => true do |t|
133 143 t.integer "user_id"
134 144 t.integer "problem_id"
135 145 t.integer "language_id"
136 146 t.text "source"
137 147 t.binary "binary"
138 148 t.datetime "submitted_at"
139 149 t.datetime "compiled_at"
140 150 t.text "compiler_message"
141 151 t.datetime "graded_at"
142 152 t.integer "points"
143 153 t.text "grader_comment"
144 154 t.integer "number"
145 155 t.string "source_filename"
146 156 end
147 157
148 158 add_index "submissions", ["user_id", "problem_id", "number"], :name => "index_submissions_on_user_id_and_problem_id_and_number", :unique => true
149 159 add_index "submissions", ["user_id", "problem_id"], :name => "index_submissions_on_user_id_and_problem_id"
150 160
151 161 create_table "tasks", :force => true do |t|
152 162 t.integer "submission_id"
153 163 t.datetime "created_at"
154 164 t.integer "status"
155 165 t.datetime "updated_at"
156 166 end
157 167
168 + create_table "test_pair_assignments", :force => true do |t|
169 + t.integer "user_id"
170 + t.integer "problem_id"
171 + t.integer "test_pair_id"
172 + t.integer "test_pair_number"
173 + t.integer "request_number"
174 + t.datetime "created_at"
175 + t.datetime "updated_at"
176 + t.boolean "submitted"
177 + end
178 +
158 179 create_table "test_pairs", :force => true do |t|
159 180 t.integer "problem_id"
160 181 t.text "input"
161 182 t.text "solution"
162 183 t.datetime "created_at"
163 184 t.datetime "updated_at"
185 + t.integer "number"
164 186 end
165 187
166 188 create_table "test_requests", :force => true do |t|
167 189 t.integer "user_id"
168 190 t.integer "problem_id"
169 191 t.integer "submission_id"
170 192 t.string "input_file_name"
171 193 t.string "output_file_name"
172 194 t.string "running_stat"
173 195 t.integer "status"
174 196 t.datetime "updated_at"
175 197 t.datetime "submitted_at"
176 198 t.datetime "compiled_at"
177 199 t.text "compiler_message"
178 200 t.datetime "graded_at"
179 201 t.string "grader_comment"
180 202 t.datetime "created_at"
181 203 t.float "running_time"
182 204 t.string "exit_status"
183 205 t.integer "memory_usage"
184 206 end
185 207
186 208 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
187 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 +
188 217 create_table "users", :force => true do |t|
189 218 t.string "login", :limit => 50
190 219 t.string "full_name"
191 220 t.string "hashed_password"
192 221 t.string "salt", :limit => 5
193 222 t.string "alias"
194 223 t.string "email"
195 224 t.integer "site_id"
196 225 t.integer "country_id"
197 226 t.boolean "activated", :default => false
198 227 t.datetime "created_at"
199 228 t.datetime "updated_at"
200 229 end
201 230
202 231 add_index "users", ["login"], :name => "index_users_on_login", :unique => true
203 232
204 233 end
You need to be logged in to leave comments. Login now