Description:
added individual contest mode
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
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,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 |
@@ -62,8 +62,8 | |||
|
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. |
|
|
66 |
- flash[:notice] = 'Error: the contest |
|
|
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 |
@@ -9,12 +9,9 | |||
|
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' |
@@ -1,7 +1,5 | |||
|
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 | |
@@ -59,8 +57,7 | |||
|
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 |
@@ -1,7 +1,5 | |||
|
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 | # |
@@ -26,8 +24,8 | |||
|
26 | 24 | render :action => 'index' and return |
|
27 | 25 | end |
|
28 | 26 | |
|
29 | - if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' | |
|
30 |
- if @user. |
|
|
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 |
@@ -1,8 +1,6 | |||
|
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]) |
@@ -12,10 +10,11 | |||
|
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 |
|
|
16 |
- append_to menu_items, '[User |
|
|
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/>" |
@@ -57,6 +56,12 | |||
|
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) |
@@ -71,27 +76,25 | |||
|
71 | 76 | |
|
72 | 77 | # |
|
73 | 78 | # if the contest is over |
|
74 | - if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' | |
|
75 |
- if user. |
|
|
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. |
|
|
87 | + if !user.contest_started? | |
|
83 | 88 | time_left = " " + (t 'title_bar.contest_not_started') |
|
84 | 89 | else |
|
85 | - if user.site!=nil | |
|
86 | - time_left = " " + (t 'title_bar.remaining_time') + | |
|
87 | - " #{Time.at(user.site.time_left).gmtime.strftime("%X")}" | |
|
88 | - end | |
|
90 | + time_left = " " + (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 |
|
|
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> |
@@ -59,9 +59,8 | |||
|
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 |
@@ -89,10 +88,48 | |||
|
89 | 88 | return @@task_grading_info |
|
90 | 89 | end |
|
91 | 90 | |
|
92 |
- def self. |
|
|
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) |
@@ -10,29 +10,23 | |||
|
10 | 10 | end |
|
11 | 11 | |
|
12 | 12 | def time_left |
|
13 |
- contest_time = Configuration |
|
|
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 |
- |
|
|
15 | + return nil if contest_time == nil | |
|
16 | + | |
|
17 | + return contest_time if !self.started | |
|
21 | 18 | |
|
22 |
- |
|
|
23 |
- |
|
|
24 |
- |
|
|
25 |
- |
|
|
26 |
- |
|
|
27 |
- |
|
|
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 |
- |
|
|
30 |
- |
|
|
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 | |
@@ -41,11 +35,9 | |||
|
41 | 35 | return false |
|
42 | 36 | end |
|
43 | 37 | |
|
44 |
- contest_time = Configuration |
|
|
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 |
@@ -16,6 +16,8 | |||
|
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 | |
@@ -118,6 +120,54 | |||
|
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? |
@@ -19,8 +19,7 | |||
|
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) |
@@ -9,7 +9,7 | |||
|
9 | 9 | # |
|
10 | 10 | # It's strongly recommended to check this file into your version control system. |
|
11 | 11 | |
|
12 |
- ActiveRecord::Schema.define(:version => 201001 |
|
|
12 | + ActiveRecord::Schema.define(:version => 20100124054458) do | |
|
13 | 13 | |
|
14 | 14 | create_table "announcements", :force => true do |t| |
|
15 | 15 | t.string "author" |
@@ -28,6 +28,7 | |||
|
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| |
@@ -129,6 +130,15 | |||
|
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" |
@@ -155,12 +165,24 | |||
|
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| |
@@ -185,6 +207,13 | |||
|
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" |
You need to be logged in to leave comments.
Login now