# HG changeset patch # User Jittat Fakcharoenphol # Date 2010-03-03 03:23:18 # Node ID a0c2d497b31a8332e94b26eb13cc415b2983bbb1 # Parent f3c97d6285dcaa74c14f3bc3bb1a2e78b0b03a29 shows problems availabe in contests 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