diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb
--- a/app/controllers/main_controller.rb
+++ b/app/controllers/main_controller.rb
@@ -193,16 +193,62 @@
end
end
+ def problem_list_by_user_contests(user)
+ contest_problems = []
+ pin = {}
+ user.contests.each do |contest|
+ available_problems = contest.problems.available
+ contest_problems << {
+ :contest => contest,
+ :problems => available_problems
+ }
+ available_problems.each {|p| pin[p.id] = true}
+ end
+ other_avaiable_problems = Problem.available.find_all {|p| pin[p.id]==nil and p.contests.length==0}
+ contest_problems << {
+ :contest => nil,
+ :problems => other_avaiable_problems
+ }
+ return contest_problems
+ end
+
+ def problem_list_for_user(user, contest_problems=nil)
+ if not Configuration.multicontests?
+ return Problem.find_available_problems
+ else
+ if contest_problems==nil
+ contest_problems = problem_list_by_user_contests(user)
+ end
+
+ problems = []
+ collected = {}
+ contest_problems.each do |cp|
+ cp[:problems].each do |problem|
+ if not collected[problem.id]
+ problems << problem
+ collected[problem.id] = true
+ end
+ end
+ end
+ return problems
+ end
+ end
+
def prepare_list_information
- @problems = Problem.find_available_problems
- @prob_submissions = Array.new
@user = User.find(session[:user_id])
+ if not Configuration.multicontests?
+ @problems = problem_list_for_user(@user)
+ else
+ @contest_problems = problem_list_by_user_contests(@user)
+ @problems = problem_list_for_user(@user, @contest_problems)
+ end
+ @prob_submissions = {}
@problems.each do |p|
sub = Submission.find_last_by_user_and_problem(@user.id,p.id)
if sub!=nil
- @prob_submissions << { :count => sub.number, :submission => sub }
+ @prob_submissions[p.id] = { :count => sub.number, :submission => sub }
else
- @prob_submissions << { :count => 0, :submission => nil }
+ @prob_submissions[p.id] = { :count => 0, :submission => nil }
end
end
prepare_announcements
diff --git a/app/models/configuration.rb b/app/models/configuration.rb
--- a/app/models/configuration.rb
+++ b/app/models/configuration.rb
@@ -106,6 +106,10 @@
return get(SYSTEM_MODE_CONF_KEY) == 'indv-contest'
end
+ def self.multicontests?
+ return get('system.multicontests') == true
+ end
+
def self.time_limit_mode?
mode = get(SYSTEM_MODE_CONF_KEY)
return ((mode == 'contest') or (mode == 'indv-contest'))
diff --git a/app/models/problem.rb b/app/models/problem.rb
--- a/app/models/problem.rb
+++ b/app/models/problem.rb
@@ -8,11 +8,13 @@
validates_format_of :name, :with => /^\w+$/
validates_presence_of :full_name
+ named_scope :available, :conditions => {:available => true}
+
DEFAULT_TIME_LIMIT = 1
DEFAULT_MEMORY_LIMIT = 32
def self.find_available_problems
- find(:all, :conditions => {:available => true}, :order => "date_added DESC")
+ Problem.available.all(:order => "date_added DESC")
end
def self.create_from_import_form_params(params, old_problem=nil)
diff --git a/app/views/main/_problem.html.erb b/app/views/main/_problem.html.erb
--- a/app/views/main/_problem.html.erb
+++ b/app/views/main/_problem.html.erb
@@ -7,12 +7,12 @@
<%= link_to_description_if_any "[#{t 'main.problem_desc'}]", problem %>
- <%= @prob_submissions[problem_counter][:count] %>
+ <%= @prob_submissions[problem.id][:count] %>
|
<%= render :partial => 'submission_short',
:locals => {
- :submission => @prob_submissions[problem_counter][:submission],
+ :submission => @prob_submissions[problem.id][:submission],
:problem_name => problem.name }%>
|
diff --git a/app/views/main/list.html.haml b/app/views/main/list.html.haml
--- a/app/views/main/list.html.haml
+++ b/app/views/main/list.html.haml
@@ -22,13 +22,26 @@
%p=t 'main.start_soon'
- if Configuration.show_tasks_to?(@user)
- %table.info
- %tr.info-head
- %th
- %th Tasks
- %th # of sub(s)
- %th Results
- = render :partial => 'problem', :collection => @problems
+ - if not Configuration.multicontests?
+ %table.info
+ %tr.info-head
+ %th
+ %th Tasks
+ %th # of sub(s)
+ %th Results
+ = render :partial => 'problem', :collection => @problems
+ - else
+ - @contest_problems.each do |cp|
+ %h2{:class =>'contest-title'}
+ = "#{cp[:contest] ? cp[:contest].title : 'Public problems'}"
+ %table.info
+ %tr.info-head
+ %th
+ %th Tasks
+ %th # of sub(s)
+ %th Results
+ = render :partial => 'problem', :collection => cp[:problems]
+
%hr/
diff --git a/spec/controllers/main_controller_spec.rb b/spec/controllers/main_controller_spec.rb
--- a/spec/controllers/main_controller_spec.rb
+++ b/spec/controllers/main_controller_spec.rb
@@ -1,7 +1,107 @@
-
require File.dirname(__FILE__) + '/../spec_helper'
-describe MainController do
+module ConfigHelperMethods
+ def enable_multicontest
+ c = Configuration.new(:key => 'system.multicontests',
+ :value_type => 'boolean',
+ :value => 'true')
+ c.save
+ end
+
+ def disable_multicontest
+ c = Configuration.new(:key => 'system.multicontests',
+ :value_type => 'boolean',
+ :value => 'false')
+ c.save
+ end
+end
+
+describe MainController, "when a user comes to list page" do
+
+ it "should redirect user to login page when unlogged-in user try to access main/list" do
+ get 'list'
+ response.should redirect_to(:action => 'login')
+ end
+
+end
+
+describe MainController, "when a logged in user comes to list page, with multicontests off" do
+ integrate_views
+
+ include ConfigHelperMethods
+
+ fixtures :users
+ fixtures :problems
+ fixtures :contests
+
+ before(:each) do
+ disable_multicontest
+ end
+
+ it "should list available problems" do
+ john = users(:john)
+ get "list", {}, {:user_id => john.id}
+
+ response.should render_template 'main/list'
+ response.should have_text(/add/)
+ response.should have_text(/easy_problem/)
+ response.should have_text(/hard_problem/)
+ end
+
+end
+
+describe MainController, "when a logged in user comes to list page, with multicontests on" do
+ integrate_views
+
+ include ConfigHelperMethods
+
+ fixtures :users
+ fixtures :problems
+ fixtures :contests
+
+ before(:each) do
+ enable_multicontest
+ end
+
+ it "should list only available public problems to users with no contest assigned" do
+ john = users(:john)
+ get "list", {}, {:user_id => john.id}
+
+ response.should render_template('main/list')
+ response.should have_text(/add/)
+ response.should_not have_text(/easy_problem/)
+ response.should_not have_text(/hard_problem/)
+ end
+
+ it "should list available problems on a specific contest" do
+ james = users(:james)
+ get "list", {}, {:user_id => james.id}
+
+ response.should render_template('main/list')
+ response.should have_text(/add/)
+ response.should have_text(/easy_problem/)
+ response.should_not have_text(/hard_problem/)
+ end
+
+ it "should shows available problems by contests" do
+ james = users(:james)
+ get "list", {}, {:user_id => james.id}
+
+ response.should render_template('main/list')
+ response.should have_text(Regexp.new('Contest A.*easy_problem', Regexp::MULTILINE))
+ end
+
+ it "should shows available problems by contests; problems belonging to more the one contest should appear many times" do
+ jack = users(:jack)
+ get "list", {}, {:user_id => jack.id}
+
+ response.should render_template('main/list')
+ response.should have_text(Regexp.new('Contest A.*easy_problem.*Contest B.*easy_problem', Regexp::MULTILINE))
+ response.should have_text(Regexp.new('Contest B.*hard_problem', Regexp::MULTILINE))
+ end
+end
+
+describe MainController, "when a user loads sources and compiler messages" do
before(:each) do
@problem = mock(Problem, :name => 'test', :output_only => false)
@@ -13,44 +113,47 @@
:language => @language,
:source => 'sample source',
:compiler_message => 'none')
+
@user = mock(User, :id => 1, :login => 'john')
+ @user.should_receive(:update_start_time).at_most(:once)
+
@another_user = mock(User, :id => 2, :login => 'mary')
- end
+ @another_user.should_receive(:update_start_time).at_most(:once)
- it "should redirect user to login page when unlogged-in user try to access main/list" do
- get 'list'
- response.should redirect_to(:action => 'login')
+ User.should_receive(:find).
+ with(1).any_number_of_times.
+ and_return(@user)
+ User.should_receive(:find).
+ with(2).any_number_of_times.
+ and_return(@another_user)
+ Submission.should_receive(:find).
+ any_number_of_times.with(@submission.id.to_s).
+ and_return(@submission)
end
it "should let user sees her own source" do
- Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
- User.should_receive(:find).with(1).and_return(@user)
- @user.should_receive(:update_start_time)
+ @submission.should_receive(:download_filename).and_return("foo.c")
get 'source', {:id => @submission.id}, {:user_id => 1}
response.should be_success
end
it "should let user sees her own compiler message" do
- Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
- User.should_receive(:find).with(1).and_return(@user)
get 'compiler_msg', {:id => @submission.id}, {:user_id => 1}
response.should be_success
end
it "should not let user sees other user's source" do
- Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
- User.should_receive(:find).with(2).and_return(@another_user)
get 'source', {:id => @submission.id}, {:user_id => 2}
flash[:notice].should =~ /[Ee]rror/
response.should redirect_to(:action => 'list')
end
it "should not let user sees other user's compiler message" do
- Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
- User.should_receive(:find).with(2).and_return(@another_user)
get 'compiler_msg', {:id => @submission.id}, {:user_id => 2}
flash[:notice].should =~ /[Ee]rror/
response.should redirect_to(:action => 'list')
end
end
+
+
diff --git a/test/fixtures/problems.yml b/test/fixtures/problems.yml
--- a/test/fixtures/problems.yml
+++ b/test/fixtures/problems.yml
@@ -4,8 +4,21 @@
name: add
full_name: add_full_name
available: true
+
two:
id: 2
name: subtract
full_name: subtract_full_name
available: false
+
+easy:
+ name: easy_problem
+ full_name: Easy Problem
+ available: true
+ contests: contest_a, contest_b
+
+hard:
+ name: hard_problem
+ full_name: Hard Problem
+ available: true
+ contests: contest_b
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -11,6 +11,7 @@
full_name: john
hashed_password: <%= User.encrypt("hello",salt) %>
salt: <%= salt %>
+
mary:
login: mary
full_name: mary
@@ -18,3 +19,16 @@
salt: <%= salt %>
roles: admin
+james:
+ login: james
+ full_name: James
+ hashed_password: <%= User.encrypt("morning",salt) %>
+ salt: <%= salt %>
+ contests: contest_a
+
+jack:
+ login: jack
+ full_name: Jack
+ hashed_password: <%= User.encrypt("morning",salt) %>
+ salt: <%= salt %>
+ contests: contest_a, contest_b
diff --git a/test/functional/main_controller_test.rb b/test/functional/main_controller_test.rb
--- a/test/functional/main_controller_test.rb
+++ b/test/functional/main_controller_test.rb
@@ -1,20 +1,4 @@
require File.dirname(__FILE__) + '/../test_helper'
class MainControllerTest < ActionController::TestCase
- fixtures :users
- fixtures :problems
-
- def test_should_redirect_new_user_to_login
- get :list
- assert_redirected_to :controller => 'main', :action => 'login'
- end
-
- def test_should_list_available_problems_if_logged_in
- john = users(:john)
- get :list, {}, {:user_id => john.id}
-
- assert_template 'main/list'
- assert_select "table tr:nth-child(2)", :text => /\(add\)/
- end
-
end