Description:
shows problems availabe in contests
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r278:a0c2d497b31a - - 9 files changed: 217 inserted, 38 deleted

@@ -148,106 +148,152
148 148 case_num = params[:num].to_i
149 149 out_filename = output_filename(@user.login,
150 150 @submission.problem.name,
151 151 @submission.id,
152 152 case_num)
153 153 if !FileTest.exists?(out_filename)
154 154 flash[:notice] = 'Output not found.'
155 155 redirect_to :action => 'list' and return
156 156 end
157 157
158 158 response.headers['Content-Type'] = "application/force-download"
159 159 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
160 160 response.headers["X-Sendfile"] = out_filename
161 161 response.headers['Content-length'] = File.size(out_filename)
162 162 render :nothing => true
163 163 end
164 164
165 165 def error
166 166 @user = User.find(session[:user_id])
167 167 end
168 168
169 169 # announcement refreshing and hiding methods
170 170
171 171 def announcements
172 172 if params.has_key? 'recent'
173 173 prepare_announcements(params[:recent])
174 174 else
175 175 prepare_announcements
176 176 end
177 177 render(:partial => 'announcement',
178 178 :collection => @announcements,
179 179 :locals => {:announcement_effect => true})
180 180 end
181 181
182 182 protected
183 183
184 184 def prepare_announcements(recent=nil)
185 185 if Configuration.show_tasks_to?(@user)
186 186 @announcements = Announcement.find_published(true)
187 187 else
188 188 @announcements = Announcement.find_published
189 189 end
190 190 if recent!=nil
191 191 recent_id = recent.to_i
192 192 @announcements = @announcements.find_all { |a| a.id > recent_id }
193 193 end
194 194 end
195 195
196 + def problem_list_by_user_contests(user)
197 + contest_problems = []
198 + pin = {}
199 + user.contests.each do |contest|
200 + available_problems = contest.problems.available
201 + contest_problems << {
202 + :contest => contest,
203 + :problems => available_problems
204 + }
205 + available_problems.each {|p| pin[p.id] = true}
206 + end
207 + other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
208 + contest_problems << {
209 + :contest => nil,
210 + :problems => other_avaiable_problems
211 + }
212 + return contest_problems
213 + end
214 +
215 + def problem_list_for_user(user, contest_problems=nil)
216 + if not Configuration.multicontests?
217 + return Problem.find_available_problems
218 + else
219 + if contest_problems==nil
220 + contest_problems = problem_list_by_user_contests(user)
221 + end
222 +
223 + problems = []
224 + collected = {}
225 + contest_problems.each do |cp|
226 + cp[:problems].each do |problem|
227 + if not collected[problem.id]
228 + problems << problem
229 + collected[problem.id] = true
230 + end
231 + end
232 + end
233 + return problems
234 + end
235 + end
236 +
196 237 def prepare_list_information
197 - @problems = Problem.find_available_problems
198 - @prob_submissions = Array.new
199 238 @user = User.find(session[:user_id])
239 + if not Configuration.multicontests?
240 + @problems = problem_list_for_user(@user)
241 + else
242 + @contest_problems = problem_list_by_user_contests(@user)
243 + @problems = problem_list_for_user(@user, @contest_problems)
244 + end
245 + @prob_submissions = {}
200 246 @problems.each do |p|
201 247 sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
202 248 if sub!=nil
203 - @prob_submissions << { :count => sub.number, :submission => sub }
249 + @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
204 250 else
205 - @prob_submissions << { :count => 0, :submission => nil }
251 + @prob_submissions[p.id] = { :count => 0, :submission => nil }
206 252 end
207 253 end
208 254 prepare_announcements
209 255 end
210 256
211 257 def check_viewability
212 258 @user = User.find(session[:user_id])
213 259 if (!Configuration.show_tasks_to?(@user)) and
214 260 ((action_name=='submission') or (action_name=='submit'))
215 261 redirect_to :action => 'list' and return
216 262 end
217 263 end
218 264
219 265 def prepare_grading_result(submission)
220 266 if Configuration.task_grading_info.has_key? submission.problem.name
221 267 grading_info = Configuration.task_grading_info[submission.problem.name]
222 268 else
223 269 # guess task info from problem.full_score
224 270 cases = submission.problem.full_score / 10
225 271 grading_info = {
226 272 'testruns' => cases,
227 273 'testcases' => cases
228 274 }
229 275 end
230 276 @test_runs = []
231 277 if grading_info['testruns'].is_a? Integer
232 278 trun_count = grading_info['testruns']
233 279 trun_count.times do |i|
234 280 @test_runs << [ read_grading_result(@user.login,
235 281 submission.problem.name,
236 282 submission.id,
237 283 i+1) ]
238 284 end
239 285 else
240 286 grading_info['testruns'].keys.sort.each do |num|
241 287 run = []
242 288 testrun = grading_info['testruns'][num]
243 289 testrun.each do |c|
244 290 run << read_grading_result(@user.login,
245 291 submission.problem.name,
246 292 submission.id,
247 293 c)
248 294 end
249 295 @test_runs << run
250 296 end
251 297 end
252 298 end
253 299
@@ -61,96 +61,100
61 61 return false if (user.site!=nil) and
62 62 ((user.site.started!=true) or (user.site.finished?))
63 63 end
64 64 return true
65 65 end
66 66
67 67 def self.show_tasks_to?(user)
68 68 if time_limit_mode?
69 69 return false if not user.contest_started?
70 70 end
71 71 return true
72 72 end
73 73
74 74 def self.show_grading_result
75 75 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
76 76 end
77 77
78 78 def self.allow_test_request(user)
79 79 mode = get(SYSTEM_MODE_CONF_KEY)
80 80 early_timeout = get(TEST_REQUEST_EARLY_TIMEOUT_KEY)
81 81 if (mode=='contest')
82 82 return false if ((user.site!=nil) and
83 83 ((user.site.started!=true) or
84 84 (early_timeout and (user.site.time_left < 30.minutes))))
85 85 end
86 86 return false if mode=='analysis'
87 87 return true
88 88 end
89 89
90 90 def self.task_grading_info
91 91 if Configuration.task_grading_info==nil
92 92 read_grading_info
93 93 end
94 94 return Configuration.task_grading_info
95 95 end
96 96
97 97 def self.standard_mode?
98 98 return get(SYSTEM_MODE_CONF_KEY) == 'standard'
99 99 end
100 100
101 101 def self.contest_mode?
102 102 return get(SYSTEM_MODE_CONF_KEY) == 'contest'
103 103 end
104 104
105 105 def self.indv_contest_mode?
106 106 return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
107 107 end
108 108
109 + def self.multicontests?
110 + return get('system.multicontests') == true
111 + end
112 +
109 113 def self.time_limit_mode?
110 114 mode = get(SYSTEM_MODE_CONF_KEY)
111 115 return ((mode == 'contest') or (mode == 'indv-contest'))
112 116 end
113 117
114 118 def self.analysis_mode?
115 119 return get(SYSTEM_MODE_CONF_KEY) == 'analysis'
116 120 end
117 121
118 122 def self.contest_time_limit
119 123 contest_time_str = Configuration['contest.time_limit']
120 124
121 125 if not defined? Configuration.contest_time_str
122 126 Configuration.contest_time_str = nil
123 127 end
124 128
125 129 if Configuration.contest_time_str != contest_time_str
126 130 Configuration.contest_time_str = contest_time_str
127 131 if tmatch = /(\d+):(\d+)/.match(contest_time_str)
128 132 h = tmatch[1].to_i
129 133 m = tmatch[2].to_i
130 134
131 135 Configuration.contest_time = h.hour + m.minute
132 136 else
133 137 Configuration.contest_time = nil
134 138 end
135 139 end
136 140 return Configuration.contest_time
137 141 end
138 142
139 143 protected
140 144
141 145 def self.convert_type(val,type)
142 146 case type
143 147 when 'string'
144 148 return val
145 149
146 150 when 'integer'
147 151 return val.to_i
148 152
149 153 when 'boolean'
150 154 return (val=='true')
151 155 end
152 156 end
153 157
154 158 def self.read_config
155 159 Configuration.config_cache = {}
156 160 Configuration.find(:all).each do |conf|
@@ -1,63 +1,65
1 1 class Problem < ActiveRecord::Base
2 2
3 3 belongs_to :description
4 4 has_and_belongs_to_many :contests
5 5 has_many :test_pairs, :dependent => :delete_all
6 6
7 7 validates_presence_of :name
8 8 validates_format_of :name, :with => /^\w+$/
9 9 validates_presence_of :full_name
10 10
11 + named_scope :available, :conditions => {:available => true}
12 +
11 13 DEFAULT_TIME_LIMIT = 1
12 14 DEFAULT_MEMORY_LIMIT = 32
13 15
14 16 def self.find_available_problems
15 - find(:all, :conditions => {:available => true}, :order => "date_added DESC")
17 + Problem.available.all(:order => "date_added DESC")
16 18 end
17 19
18 20 def self.create_from_import_form_params(params, old_problem=nil)
19 21 problem = old_problem || Problem.new
20 22 import_params = Problem.extract_params_and_check(params, problem)
21 23
22 24 if not problem.valid?
23 25 return problem, 'Error importing'
24 26 end
25 27
26 28 problem.full_score = 100
27 29 problem.date_added = Time.new
28 30 problem.test_allowed = true
29 31 problem.output_only = false
30 32 problem.available = false
31 33
32 34 if not problem.save
33 35 return problem, 'Error importing'
34 36 end
35 37
36 38 import_to_db = params.has_key? :import_to_db
37 39
38 40 importer = TestdataImporter.new(problem)
39 41
40 42 if not importer.import_from_file(import_params[:file],
41 43 import_params[:time_limit],
42 44 import_params[:memory_limit],
43 45 import_to_db)
44 46 problem.errors.add_to_base('Import error.')
45 47 end
46 48
47 49 return problem, importer.log_msg
48 50 end
49 51
50 52 def self.download_file_basedir
51 53 return "#{RAILS_ROOT}/data/tasks"
52 54 end
53 55
54 56 protected
55 57
56 58 def self.to_i_or_default(st, default)
57 59 if st!=''
58 60 st.to_i
59 61 else
60 62 default
61 63 end
62 64 end
63 65
@@ -1,18 +1,18
1 1 <tr class="info-<%= (problem_counter%2==0) ? "even" : "odd" %>">
2 2 <td>
3 3 <%= "#{problem_counter+1}" %>
4 4 </td>
5 5 <td>
6 6 <%= "#{problem.full_name} (#{problem.name})" %>
7 7 <%= link_to_description_if_any "[#{t 'main.problem_desc'}]", problem %>
8 8 </td>
9 9 <td align="center">
10 - <%= @prob_submissions[problem_counter][:count] %>
10 + <%= @prob_submissions[problem.id][:count] %>
11 11 </td>
12 12 <td>
13 13 <%= render :partial => 'submission_short',
14 14 :locals => {
15 - :submission => @prob_submissions[problem_counter][:submission],
15 + :submission => @prob_submissions[problem.id][:submission],
16 16 :problem_name => problem.name }%>
17 17 </td>
18 18 </tr>
@@ -1,38 +1,51
1 1 - content_for :head do
2 2 = javascript_include_tag :defaults
3 3 = javascript_include_tag 'announcement_refresh.js'
4 4
5 5 = user_title_bar(@user)
6 6
7 7 .announcementbox{:style => (@announcements.length==0 ? "display:none" : "")}
8 8 %span{:class => 'title'}
9 9 Announcements
10 10 #announcementbox-body
11 11 = render :partial => 'announcement', :collection => @announcements
12 12
13 13 - if Configuration.show_submitbox_to?(@user)
14 14 .submitbox
15 15 = error_messages_for 'submission'
16 16 = render :partial => 'submission_box'
17 17
18 18
19 19 %hr/
20 20
21 21 - if (Configuration.contest_mode?) and (@user.site!=nil) and (@user.site.started!=true)
22 22 %p=t 'main.start_soon'
23 23
24 24 - if Configuration.show_tasks_to?(@user)
25 + - if not Configuration.multicontests?
25 26 %table.info
26 27 %tr.info-head
27 28 %th
28 29 %th Tasks
29 30 %th # of sub(s)
30 31 %th Results
31 32 = render :partial => 'problem', :collection => @problems
33 + - else
34 + - @contest_problems.each do |cp|
35 + %h2{:class =>'contest-title'}
36 + = "#{cp[:contest] ? cp[:contest].title : 'Public problems'}"
37 + %table.info
38 + %tr.info-head
39 + %th
40 + %th Tasks
41 + %th # of sub(s)
42 + %th Results
43 + = render :partial => 'problem', :collection => cp[:problems]
44 +
32 45
33 46 %hr/
34 47
35 48 %script{:type => 'text/javascript'}
36 49 = "Announcement.refreshUrl = '#{url_for :controller => 'main', :action => 'announcements'}';"
37 50 Announcement.registerRefreshEventTimer();
38 51
@@ -1,56 +1,159
1 -
2 1 require File.dirname(__FILE__) + '/../spec_helper'
3 2
4 - describe MainController do
3 + module ConfigHelperMethods
4 + def enable_multicontest
5 + c = Configuration.new(:key => 'system.multicontests',
6 + :value_type => 'boolean',
7 + :value => 'true')
8 + c.save
9 + end
10 +
11 + def disable_multicontest
12 + c = Configuration.new(:key => 'system.multicontests',
13 + :value_type => 'boolean',
14 + :value => 'false')
15 + c.save
16 + end
17 + end
18 +
19 + describe MainController, "when a user comes to list page" do
20 +
21 + it "should redirect user to login page when unlogged-in user try to access main/list" do
22 + get 'list'
23 + response.should redirect_to(:action => 'login')
24 + end
25 +
26 + end
27 +
28 + describe MainController, "when a logged in user comes to list page, with multicontests off" do
29 + integrate_views
30 +
31 + include ConfigHelperMethods
32 +
33 + fixtures :users
34 + fixtures :problems
35 + fixtures :contests
36 +
37 + before(:each) do
38 + disable_multicontest
39 + end
40 +
41 + it "should list available problems" do
42 + john = users(:john)
43 + get "list", {}, {:user_id => john.id}
44 +
45 + response.should render_template 'main/list'
46 + response.should have_text(/add/)
47 + response.should have_text(/easy_problem/)
48 + response.should have_text(/hard_problem/)
49 + end
50 +
51 + end
52 +
53 + describe MainController, "when a logged in user comes to list page, with multicontests on" do
54 + integrate_views
55 +
56 + include ConfigHelperMethods
57 +
58 + fixtures :users
59 + fixtures :problems
60 + fixtures :contests
61 +
62 + before(:each) do
63 + enable_multicontest
64 + end
65 +
66 + it "should list only available public problems to users with no contest assigned" do
67 + john = users(:john)
68 + get "list", {}, {:user_id => john.id}
69 +
70 + response.should render_template('main/list')
71 + response.should have_text(/add/)
72 + response.should_not have_text(/easy_problem/)
73 + response.should_not have_text(/hard_problem/)
74 + end
75 +
76 + it "should list available problems on a specific contest" do
77 + james = users(:james)
78 + get "list", {}, {:user_id => james.id}
79 +
80 + response.should render_template('main/list')
81 + response.should have_text(/add/)
82 + response.should have_text(/easy_problem/)
83 + response.should_not have_text(/hard_problem/)
84 + end
85 +
86 + it "should shows available problems by contests" do
87 + james = users(:james)
88 + get "list", {}, {:user_id => james.id}
89 +
90 + response.should render_template('main/list')
91 + response.should have_text(Regexp.new('Contest A.*easy_problem', Regexp::MULTILINE))
92 + end
93 +
94 + it "should shows available problems by contests; problems belonging to more the one contest should appear many times" do
95 + jack = users(:jack)
96 + get "list", {}, {:user_id => jack.id}
97 +
98 + response.should render_template('main/list')
99 + response.should have_text(Regexp.new('Contest A.*easy_problem.*Contest B.*easy_problem', Regexp::MULTILINE))
100 + response.should have_text(Regexp.new('Contest B.*hard_problem', Regexp::MULTILINE))
101 + end
102 + end
103 +
104 + describe MainController, "when a user loads sources and compiler messages" do
5 105
6 106 before(:each) do
7 107 @problem = mock(Problem, :name => 'test', :output_only => false)
8 108 @language = mock(Language, :name => 'cpp', :ext => 'cpp')
9 109 @submission = mock(Submission,
10 110 :id => 1,
11 111 :user_id => 1,
12 112 :problem => @problem,
13 113 :language => @language,
14 114 :source => 'sample source',
15 115 :compiler_message => 'none')
116 +
16 117 @user = mock(User, :id => 1, :login => 'john')
118 + @user.should_receive(:update_start_time).at_most(:once)
119 +
17 120 @another_user = mock(User, :id => 2, :login => 'mary')
18 - end
121 + @another_user.should_receive(:update_start_time).at_most(:once)
19 122
20 - it "should redirect user to login page when unlogged-in user try to access main/list" do
21 - get 'list'
22 - response.should redirect_to(:action => 'login')
123 + User.should_receive(:find).
124 + with(1).any_number_of_times.
125 + and_return(@user)
126 + User.should_receive(:find).
127 + with(2).any_number_of_times.
128 + and_return(@another_user)
129 + Submission.should_receive(:find).
130 + any_number_of_times.with(@submission.id.to_s).
131 + and_return(@submission)
23 132 end
24 133
25 134 it "should let user sees her own source" do
26 - Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
27 - User.should_receive(:find).with(1).and_return(@user)
28 - @user.should_receive(:update_start_time)
135 + @submission.should_receive(:download_filename).and_return("foo.c")
29 136 get 'source', {:id => @submission.id}, {:user_id => 1}
30 137 response.should be_success
31 138 end
32 139
33 140 it "should let user sees her own compiler message" do
34 - Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
35 - User.should_receive(:find).with(1).and_return(@user)
36 141 get 'compiler_msg', {:id => @submission.id}, {:user_id => 1}
37 142 response.should be_success
38 143 end
39 144
40 145 it "should not let user sees other user's source" do
41 - Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
42 - User.should_receive(:find).with(2).and_return(@another_user)
43 146 get 'source', {:id => @submission.id}, {:user_id => 2}
44 147 flash[:notice].should =~ /[Ee]rror/
45 148 response.should redirect_to(:action => 'list')
46 149 end
47 150
48 151 it "should not let user sees other user's compiler message" do
49 - Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
50 - User.should_receive(:find).with(2).and_return(@another_user)
51 152 get 'compiler_msg', {:id => @submission.id}, {:user_id => 2}
52 153 flash[:notice].should =~ /[Ee]rror/
53 154 response.should redirect_to(:action => 'list')
54 155 end
55 156
56 157 end
158 +
159 +
@@ -1,11 +1,24
1 1 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2 2 one:
3 3 id: 1
4 4 name: add
5 5 full_name: add_full_name
6 6 available: true
7 +
7 8 two:
8 9 id: 2
9 10 name: subtract
10 11 full_name: subtract_full_name
11 12 available: false
13 +
14 + easy:
15 + name: easy_problem
16 + full_name: Easy Problem
17 + available: true
18 + contests: contest_a, contest_b
19 +
20 + hard:
21 + name: hard_problem
22 + full_name: Hard Problem
23 + available: true
24 + contests: contest_b
@@ -1,20 +1,34
1 1 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
2 2
3 3 <%
4 4 User.public_class_method :encrypt
5 5
6 6 salt = "abc"
7 7 %>
8 8
9 9 john:
10 10 login: john
11 11 full_name: john
12 12 hashed_password: <%= User.encrypt("hello",salt) %>
13 13 salt: <%= salt %>
14 +
14 15 mary:
15 16 login: mary
16 17 full_name: mary
17 18 hashed_password: <%= User.encrypt("goodbye",salt) %>
18 19 salt: <%= salt %>
19 20 roles: admin
20 21
22 + james:
23 + login: james
24 + full_name: James
25 + hashed_password: <%= User.encrypt("morning",salt) %>
26 + salt: <%= salt %>
27 + contests: contest_a
28 +
29 + jack:
30 + login: jack
31 + full_name: Jack
32 + hashed_password: <%= User.encrypt("morning",salt) %>
33 + salt: <%= salt %>
34 + contests: contest_a, contest_b
@@ -1,20 +1,4
1 1 require File.dirname(__FILE__) + '/../test_helper'
2 2
3 3 class MainControllerTest < ActionController::TestCase
4 - fixtures :users
5 - fixtures :problems
6 -
7 - def test_should_redirect_new_user_to_login
8 - get :list
9 - assert_redirected_to :controller => 'main', :action => 'login'
10 4 end
11 -
12 - def test_should_list_available_problems_if_logged_in
13 - john = users(:john)
14 - get :list, {}, {:user_id => john.id}
15 -
16 - assert_template 'main/list'
17 - assert_select "table tr:nth-child(2)", :text => /\(add\)/
18 - end
19 -
20 - end
You need to be logged in to leave comments. Login now