tournament 4.0.2 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,6 +1,14 @@
1
+ == 4.2.0 / 2011-03-20
2
+ * Add ability to run statistics for the possibility report in more than one
3
+ process.
4
+ * Add link to possibility report on pool index page.
5
+ * Pretty formatting for large numbers.
6
+
7
+ == 4.1.0 / 2011-03-17
8
+ * Bug fix change team name and some cosmetic changes
9
+
1
10
  == 4.0.2 / 2011-03-14
2
11
  * Add ability to change the name of a team after tourny has started.
3
- FIXME: after team name change, admin must manually save all entries
4
12
 
5
13
  == 4.0.0 / 2011-03-14
6
14
  * 2011 bracket.
data/README.rdoc CHANGED
@@ -20,7 +20,6 @@ basketball tournament pool.
20
20
  the entries and updating the tournament results bracket. Useful as an
21
21
  adjunct to the command line script.
22
22
  * FIXME: Complete the test suite for the library and command line tool
23
- * FIXME: Can't change name of team after entries have been added
24
23
 
25
24
  == COMMAND LINE SYNOPSIS:
26
25
 
@@ -157,6 +156,16 @@ to control how the application is installed:
157
156
  modrails and Apache. Example:
158
157
 
159
158
  --relative-root=/my_pool (Default: empty, no root is set)
159
+
160
+ * Number of statistics processors. If you generate statistics for
161
+ the possibilities report, set this to the number of CPUs/cores your
162
+ machine has to get maximum performance. If you set this to 1 or
163
+ to fewer than the number of CPUs/cores, you won't max CPU consumption
164
+ when calculating the possibility stats. This might be considered
165
+ a 'good thing'. Set this to 1 if your hosting provider charges you
166
+ for high CPU use.
167
+
168
+ --stats-processors=1 (Default: 2)
160
169
 
161
170
  * Administrator email address. The web GUI will send emails
162
171
  when users register for creating entries in a pool. The following
data/Rakefile CHANGED
@@ -27,7 +27,7 @@ Bones {
27
27
 
28
28
  #spec.opts << '--color'
29
29
 
30
- exclude %w(tmp$ bak$ ~$ CVS \.svn \.git swp$ ^pkg ^doc bin/fake bin/gui_v2.rb ^tags$ ^webgui/log/.*\.log)
30
+ exclude %w(tmp$ bak$ ~$ CVS \.svn \.git swp$ ^pkg ^doc bin/fake bin/gui_v2.rb ^tags$ ^webgui/db/.*\.sqlite3 ^webgui/db/stats_.*\.yml ^webgui/log/.*\.log)
31
31
 
32
32
  rdoc.opts ["--line-numbers", "--force-update", "-W", "http://tournament.rubyforge.org/svn/trunk/%s"]
33
33
  rdoc.exclude [
data/bin/pool CHANGED
@@ -55,6 +55,13 @@ Main do
55
55
  arity 1
56
56
  description "Relative URL root if you are installing the web application as a subdirectory in an existing website."
57
57
  end
58
+ option('stats-processors', 's') do
59
+ optional
60
+ argument :required
61
+ arity 1
62
+ cast :int
63
+ description "Number of processors to spawn when calculating the possibility statistics. Set this to the number of CPUs/cores on your box."
64
+ end
58
65
  option('admin-email') do
59
66
  required
60
67
  argument :required
data/lib/tournament.rb CHANGED
@@ -7,7 +7,7 @@ unless defined? Tournament
7
7
  module Tournament
8
8
 
9
9
  # :stopdoc:
10
- VERSION = '4.0.2'
10
+ VERSION = '4.2.0'
11
11
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
12
12
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
13
13
  # :startdoc:
@@ -44,9 +44,9 @@ class Tournament::Bracket
44
44
  # For each possible outcome remaining in the pool, generates a bracket representing
45
45
  # that outcome and yields it to the caller's block. This can take a very long time
46
46
  # with more than about 22 teams left.
47
- def each_possible_bracket
47
+ def each_possible_bracket(batch = 0, num_batches = 1)
48
48
  puts "WARNING: This is likely going to take a very long time ... " if teams_left > 21
49
- each_possibility do |possibility|
49
+ each_possibility(batch, num_batches) do |possibility|
50
50
  yield(bracket_for(possibility))
51
51
  end
52
52
  end
@@ -97,7 +97,7 @@ class Tournament::Bracket
97
97
  #
98
98
  # If no games have been played, we can represent each possibility
99
99
  # by every possible 7 bit binary number.
100
- def each_possibility
100
+ def each_possibility(batch = 0, num_batches = 1)
101
101
  # bit masks of games that have been played
102
102
  # played_mask is for any game where a winner has been determined
103
103
  # first is a mask where the first of the matched teams won
@@ -151,9 +151,19 @@ class Tournament::Bracket
151
151
  #num_possibilities = 0
152
152
  #shifts.size.times { |n| num_possibilities |= (1 << n) }
153
153
 
154
- #puts "Checking #{num_possibilities} (#{number_of_outcomes}) possible outcomes."
155
- possibility = num_possibilities - 1
156
- while possibility >= 0
154
+ # Handle a batch
155
+ limit = nil
156
+ offset = 0
157
+ if batch && num_batches && num_batches > 1
158
+ limit = num_possibilities / num_batches
159
+ offset = batch * limit
160
+ end
161
+
162
+ possibility = num_possibilities - 1 - offset
163
+ puts "Checking #{limit.inspect} of #{number_of_outcomes} possible outcomes, offset: #{offset}, starting at: #{possibility}"
164
+
165
+ count_this_run = 0
166
+ while possibility >= 0 && (limit.nil? || count_this_run < limit)
157
167
  #puts " possibility: #{Tournament::Bracket.jbin(possibility, teams.size - 1)}"
158
168
  real_poss = 0
159
169
  shifts.each_with_index do |s, i|
@@ -164,6 +174,7 @@ class Tournament::Bracket
164
174
  #puts " real_poss: #{Tournament::Bracket.jbin(real_poss, teams.size - 1)}"
165
175
  yield(real_poss)
166
176
  possibility -= 1
177
+ count_this_run += 1
167
178
  end
168
179
  end
169
180
 
@@ -381,8 +381,8 @@ class Tournament::Pool
381
381
  picks = (1..num_picks).map {|n| Tournament::Bracket.random_bracket(b.teams)}
382
382
  # Play out the bracket
383
383
  32.times { |n| b.set_winner(1,n+1, b.matchup(1, n+1)[rand(2)])}
384
- 10.times { |n| b.set_winner(2,n+1, b.matchup(2, n+1)[rand(2)])}
385
- # 8.times { |n| b.set_winner(3,n+1, b.matchup(3, n+1)[rand(2)])}
384
+ 16.times { |n| b.set_winner(2,n+1, b.matchup(2, n+1)[rand(2)])}
385
+ 2.times { |n| b.set_winner(3,n+1, b.matchup(3, n+1)[rand(2)])}
386
386
  #4.times { |n| b.set_winner(4,n+1, b.matchup(4, n+1)[rand(2)])}
387
387
  #2.times { |n| b.set_winner(5,n+1, b.matchup(5, n+1)[rand(2)])}
388
388
  #1.times { |n| b.set_winner(6,n+1, b.matchup(6, n+1)[rand(2)])}
@@ -687,56 +687,91 @@ class Tournament::Pool
687
687
  # Runs through every possible outcome of the tournament and
688
688
  # calculates each entry's chance to win as a percentage of the
689
689
  # possible outcomes the entry would win if the tournment came
690
- # out that way. If a block is provided, periodically reports
691
- # progress by calling the block and passing it the percent complete
692
- # and time remaining in seconds as arguments.
693
- # Returns array of structs responding to times_champ, max_score,
694
- # min_rank, entry and champs methods. The entry method returns the
695
- # Tournament::Entry object and the champs method returns a hash
690
+ # out that way. Returns an array of Tournament::Possibility objects for
691
+ # each entry in the pool. These objects respond to :times_champ, :max_score,
692
+ # :min_rank, :entry and :champs methods. The :entry method returns the
693
+ # Tournament::Entry object and the :champs method returns a hash
696
694
  # keyed on team name and whose values are the number of times that
697
695
  # team could win that would make the entry come in on top.
698
- def possibility_stats
699
- stats = @entries.map do |e|
696
+ # Options avaliable:
697
+ # :processes => Number of Threads (default: 1) - Set this to the
698
+ # number of CPU's on your computer to really peg CPU usage ;)
699
+ # This method spins up N processes to perform the work and will periodically
700
+ # report progress if passed a block responding to three parameters:
701
+ # 1) percentage of possibilities checked, 2) estimated time remaining in seconds
702
+ # and 3) total possibilities checked so far.
703
+ def possibility_stats(options = {})
704
+ options = options || {}
705
+ global_stats = @entries.map do |e|
700
706
  Tournament::Possibility.new(e)
701
707
  end
702
- max_possible_score = @entries.map{|p| 0}
703
- min_ranking = @entries.map{|p| @entries.size + 1}
704
- times_winner = @entries.map{|p| 0 }
705
- player_champions = @entries.map{|p| Hash.new {|h,k| h[k] = 0} }
708
+
709
+ # Create a collector to hold the results
710
+ collector = StatsCollector.new
711
+
712
+ # How many workers?
713
+ num_threads = options[:threads] || 1
714
+
715
+ threads = if num_threads == 1
716
+ # With just one worker, do it in-process
717
+ [possibility_stats_thread(collector, 0, 1)]
718
+ else
719
+ # With more than one worker, create a drb
720
+ # server and multiple clients
721
+ possibility_stats_cluster(collector, num_threads)
722
+ end
723
+
724
+ puts "CREATED WORKERS: #{threads}"
725
+
726
+ # Wait for them to finish
706
727
  count = 0
707
- old_percentage = -1
708
- old_remaining = 1_000_000_000_000
709
728
  start = Time.now.to_f
710
- self.tournament_entry.picks.each_possible_bracket do |poss|
711
- poss_scores = @entries.map{|p| p.picks.score_against(poss, self.scoring_strategy)}
712
- sort_scores = poss_scores.sort.reverse
713
- @entries.each_with_index do |entry, i|
714
- score = poss_scores[i]
715
- stat = stats[i]
716
- stat.max_score = score if score > stat.max_score
717
- rank = sort_scores.index(score) + 1
718
- stat.min_rank = rank if rank < stat.min_rank
719
- stat.times_champ += 1 if rank == 1
720
- if rank == 1
721
- stat.champs[poss.champion.name] ||= 0
722
- stat.champs[poss.champion.name] += 1
723
- end
724
- end
725
- count += 1
726
- percentage = (count * 100.0 / self.tournament_entry.picks.number_of_outcomes)
729
+ old_remaining = 1_000_000_000_000_000
730
+ old_percentage = 0
731
+ last_thousand = 1000
732
+ total_outcomes = self.tournament_entry.picks.number_of_outcomes
733
+ while count < total_outcomes
734
+ sleep 1
735
+ count = collector.total_count
736
+ next if count == 0
737
+ percentage = (count * 100.0 / total_outcomes)
727
738
  elapsed = Time.now.to_f - start
728
739
  spp = elapsed / count
729
740
  remaining = ((self.tournament_entry.picks.number_of_outcomes - count) * spp).to_i
730
- if (percentage.to_i != old_percentage) || (remaining < old_remaining)
741
+ if (percentage.to_i != old_percentage) || (remaining < old_remaining) || count > last_thousand
731
742
  old_remaining = remaining
732
743
  old_percentage = percentage.to_i
733
744
  if block_given?
734
- yield(percentage.to_i, remaining)
745
+ yield(percentage.to_i, remaining, count)
735
746
  end
736
747
  end
748
+ if count > last_thousand
749
+ last_thousand = (count / 1000 + 1) * 1000
750
+ end
751
+ end
752
+
753
+ # join
754
+ puts
755
+ puts "Waiting for threads to finish"
756
+ threads.each {|t| t.join}
757
+
758
+ if @drb_server
759
+ puts "Waiting for drb services to stop"
760
+ DRb.stop_service
761
+ if DRb.thread
762
+ DRb.thread.stop
763
+ DRb.thread.join
764
+ end
765
+ end
766
+
767
+ # Collect all the stats
768
+ num_threads.times do |n|
769
+ global_stats.each_with_index do |stats, idx|
770
+ stats.merge!(collector.stats_of(n)[idx])
771
+ end
737
772
  end
738
- stats.sort!
739
- return stats
773
+ global_stats.sort!
774
+ return global_stats
740
775
  end
741
776
 
742
777
  # Runs through every possible outcome of the tournament and calculates
@@ -750,9 +785,9 @@ class Tournament::Pool
750
785
  return
751
786
  end
752
787
  out << "Checking #{self.tournament_entry.picks.number_of_outcomes} possible outcomes" << "\n"
753
- stats = possibility_stats do |percentage, remaining|
788
+ stats = possibility_stats(:threads => 2) do |percentage, remaining, num_processed|
754
789
  hashes = '#' * (percentage.to_i/5) + '>'
755
- out << "\rCalculating: %3d%% +#{hashes.ljust(20, '-')}+ %5d seconds remaining" % [percentage.to_i, remaining]
790
+ out << "\rCalculating: %3d%% +#{hashes.ljust(20, '-')}+ %5d seconds remaining, %d" % [percentage.to_i, remaining, num_processed]
756
791
  end
757
792
  out << "\n"
758
793
  #puts "SORT: #{stats.inspect}"
@@ -781,4 +816,114 @@ class Tournament::Pool
781
816
  end
782
817
  nil
783
818
  end
819
+
820
+ require 'drb/drb'
821
+ URI = "druby://localhost:38787"
822
+ class StatsCollector
823
+ include DRb::DRbUndumped
824
+ def initialize
825
+ @counts = {}
826
+ @stats = {}
827
+ end
828
+ def count(child, count)
829
+ @counts[child] = count
830
+ end
831
+ def stats(child, stat)
832
+ @stats[child] = stat
833
+ end
834
+ def stats_of(child)
835
+ @stats[child]
836
+ end
837
+ def count_of(child)
838
+ @count[child]
839
+ end
840
+ def total_count
841
+ @counts.values.inject(0) {|sum, count| sum += count}
842
+ end
843
+ end
844
+
845
+ # Make a subprocess look like a thread
846
+ class StatsProcess
847
+ def initialize(pid)
848
+ @pid = pid
849
+ end
850
+ def join
851
+ Process.waitpid @pid
852
+ end
853
+ end
854
+
855
+ protected
856
+
857
+ # Starts a drb server that spins off #total_threads child processes
858
+ # that each do a chunk of the calculation. Returns array
859
+ # of StatsProcess objects that can be joined
860
+ def possibility_stats_cluster(collector, total_threads)
861
+ # Create a drb server
862
+ @drb_server = DRb.start_service(URI, collector)
863
+ threads = []
864
+ total_threads.times do |n|
865
+ pid = fork
866
+ if pid
867
+ # Track the child process
868
+ threads << StatsProcess.new(pid)
869
+ else
870
+ # child
871
+ DRb.start_service
872
+ remote_collector = DRbObject.new_with_uri(URI)
873
+ puts "Child #{n} got remote collector: #{remote_collector.inspect}"
874
+ # Start off collection thread
875
+ t = possibility_stats_thread(remote_collector, n, total_threads)
876
+ # Wait for it to finish
877
+ t.join
878
+ exit 0
879
+ end
880
+ end
881
+ return threads
882
+ end
883
+
884
+ # Compute possibility stats for the given thread num and total threads.
885
+ # Tracks number of possibilities processed in collector and stores
886
+ # an Array of Tournament::Possibility objects in the collector when
887
+ # finished
888
+ def possibility_stats_thread(collector, thread_num, total_threads)
889
+ puts "Creating stats thread #{thread_num} of #{total_threads}"
890
+ t = Thread.new do
891
+ count = 0
892
+ collector.count(thread_num, count)
893
+ stats = @entries.map do |e|
894
+ Tournament::Possibility.new(e)
895
+ end
896
+ self.tournament_entry.picks.each_possible_bracket(thread_num, total_threads) do |poss|
897
+ poss_scores = @entries.map{|p| p.picks.score_against(poss, self.scoring_strategy)}
898
+ # precalculate ranks
899
+ sorted = poss_scores.sort_by{|s| -s}
900
+ sort_scores = sorted.inject({}) do |h, s|
901
+ h[s] = sorted.index(s) + 1
902
+ h
903
+ end
904
+ @entries.each_with_index do |entry, i|
905
+ score = poss_scores[i]
906
+ stat = stats[i]
907
+ stat.max_score = score if score > stat.max_score
908
+ rank = sort_scores[score]
909
+ stat.min_rank = rank if rank < stat.min_rank
910
+ stat.times_champ += 1 if rank == 1
911
+ if rank == 1
912
+ stat.champs[poss.champion.name] ||= 0
913
+ stat.champs[poss.champion.name] += 1
914
+ end
915
+ end
916
+ count += 1
917
+ if count % 1000 == 0
918
+ collector.count(thread_num, count)
919
+ end
920
+ end
921
+ # Final count and stats
922
+ collector.count(thread_num, count)
923
+ collector.stats(thread_num, stats)
924
+ end
925
+ t.abort_on_exception = true
926
+ t
927
+ end
928
+
784
929
  end
@@ -16,4 +16,18 @@ class Tournament::Possibility
16
16
  (self.min_rank <=> other.min_rank).nonzero? ||
17
17
  other.max_score <=> self.max_score
18
18
  end
19
+ # Combine self with another possibility
20
+ def merge!(other)
21
+ @times_champ += other.times_champ
22
+ @max_score = [@max_score, other.max_score].max
23
+ @min_rank = [@min_rank, other.min_rank].min
24
+
25
+ # Merge champs
26
+ @champs.keys.each do |name|
27
+ @champs[name] += (other.champs[name] || 0)
28
+ end
29
+ (other.champs.keys - @champs.keys).each do |name|
30
+ @champs[name] = other.champs[name]
31
+ end
32
+ end
19
33
  end
@@ -52,7 +52,8 @@ class Tournament::WebguiInstaller
52
52
  ['admin-email', 'ADMIN_EMAIL'],
53
53
  ['relative-root', 'RELATIVE_URL_ROOT'],
54
54
  ['smtp-configuration', 'SMTP_CONFIGURATION'],
55
- ['prince-path', 'PRINCE_PATH']
55
+ ['prince-path', 'PRINCE_PATH'],
56
+ ['stats-processors', 'STATS_PROCESSORS']
56
57
  ].each do |config_name, constant_name|
57
58
  if config_options[config_name]
58
59
  puts " -> Setting config option #{config_name} to #{config_options[config_name]}"
@@ -48,13 +48,15 @@ class WebguiInstallerTest < Test::Unit::TestCase
48
48
  {
49
49
  'prince-path' => 'foo',
50
50
  'admin-email' => 'admin@admin.com',
51
- 'site-name' => 'My Site'
51
+ 'site-name' => 'My Site',
52
+ 'stats-processors' => 5
52
53
  }
53
54
  )
54
55
  new_config = File.read(File.join(@installer.install_dir, 'config', 'initializers', 'pool.rb'))
55
56
  assert_match(/PRINCE_PATH = "foo"/, new_config)
56
57
  assert_match(/TOURNAMENT_TITLE = "My Site"/, new_config)
57
58
  assert_match(/ADMIN_EMAIL = "admin@admin.com"/, new_config)
59
+ assert_match(/STATS_PROCESSORS = 5/, new_config)
58
60
  end
59
61
 
60
62
  #def test_prince_install
@@ -66,7 +66,7 @@ class TeamsController < ApplicationController
66
66
  if @pool.teams_set?
67
67
  @pool.pool.tournament_entry.bracket.change_team_name(old_name, old_short_name, team.name, team.short_name)
68
68
  @pool.save!
69
- @pool.user_entries.each do |entry|
69
+ @pool.entries.each do |entry|
70
70
  entry.bracket.change_team_name(old_name, old_short_name, team.name, team.short_name)
71
71
  entry.save!
72
72
  end
@@ -1,3 +1,10 @@
1
1
  # Methods added to this helper will be available to all templates in the application.
2
2
  module ApplicationHelper
3
+ def has_stats_file(pool_id)
4
+ @has_stats_file ||= begin
5
+ path = File.expand_path(File.join(RAILS_ROOT, 'db', ReportsController::STATS_DATAFILE_NAME % pool_id))
6
+ File.exist?(path) ? :true : :false
7
+ end
8
+ @has_stats_file == :true
9
+ end
3
10
  end
@@ -7,17 +7,20 @@
7
7
  <small>
8
8
  <% if current_user && pool.user_id == current_user.id %>
9
9
  <% if pool.tournament_entry -%>
10
- <%= link_to '[Tourny Bracket]', :controller => 'admin', :action => 'bracket', :id => pool.id %>
10
+ <%= link_to '[Tourny Results]', :controller => 'admin', :action => 'bracket', :id => pool.id %>
11
11
  <% end -%>
12
12
  <%= link_to '[Recap]', :controller => 'admin', :action => 'recap', :id => pool.id%>
13
13
  <%= link_to '[Edit]', :controller => 'admin', :action => 'pool', :id => pool.id %>
14
14
  <%= link_to '[Entries]', :controller => 'admin', :action => 'entries', :id => pool.id %>
15
15
  <% else %>
16
16
  <% if pool.tournament_entry -%>
17
- <%= link_to '[Tourny Bracket]', :controller => 'entry', :action => 'show', :id => pool.tournament_entry.id %>
17
+ <%= link_to '[Tourny Results]', :controller => 'entry', :action => 'show', :id => pool.tournament_entry.id %>
18
18
  <% end %>
19
19
  <% end %>
20
20
  <%= link_to '[Leader Board]', :controller => 'reports', :action => 'show', :id => pool.id, :report => 'leader' %>
21
+ <% if has_stats_file(pool.id) -%>
22
+ <%= link_to '[Possibilities]', :controller => 'reports', :action => 'show', :id => pool.id, :report => 'possibility' %>
23
+ <% end -%>
21
24
  <%= link_to '[Reports]', :controller => 'reports', :action => 'show', :id => pool.id %>
22
25
  </small>
23
26
  <div class="poollistinfo">
@@ -5,7 +5,7 @@
5
5
  <li>Total games played: <%= pool.tournament_entry.picks.games_played %></li>
6
6
  <li>Pool Tie Break: <%=pool.tournament_entry.tie_breaker || '-' %></li>
7
7
  <li>Number of entries: <%=pool.entries.size%></li>
8
- <li>Number of remaining outcomes: <%=pool.tournament_entry.picks.number_of_outcomes%></li>
8
+ <li>Number of remaining outcomes: <%=number_with_delimiter(pool.tournament_entry.picks.number_of_outcomes)%></li>
9
9
  </ul>
10
10
  <% end %>
11
11
  <% if @message %>
@@ -38,7 +38,7 @@
38
38
  <a class="info" href="#">Detail
39
39
  <span class="flow">
40
40
  <% stat.champs.sort_by{|k,v| -v}.each do |team, occurrences| -%>
41
- <nobr><%=team%> <%=occurrences%> (<%="%5.2f" % (occurrences.to_f * 100.0 / stat.times_champ)%>%)</nobr>
41
+ <nobr><%=team%> <%=number_with_delimiter(occurrences)%> (<%="%5.2f" % (occurrences.to_f * 100.0 / stat.times_champ)%>%)</nobr>
42
42
  <br/>
43
43
  <% end -%>
44
44
  </span>
@@ -27,7 +27,7 @@ Rails::Initializer.run do |config|
27
27
  # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
28
28
  # config.gem "sqlite3-ruby", :lib => "sqlite3"
29
29
  # config.gem "aws-s3", :lib => "aws/s3"
30
- config.gem "tournament", :version => "~> 4.0.0"
30
+ config.gem "tournament", :version => ">= 4.0.0"
31
31
 
32
32
  # Only load the plugins named here, in the order given. By default, all plugins
33
33
  # in vendor/plugins are loaded in alphabetical order.
@@ -34,3 +34,8 @@ end
34
34
  # Full path to the prince xml executable if you want printable
35
35
  # styled PDF's of tournament brackets.
36
36
  PRINCE_PATH = nil unless defined?(PRINCE_PATH)
37
+
38
+ # Number of processes to use when calculating possibility
39
+ # report. Set this to the number of CPU's on the machine
40
+ # you run the report on.
41
+ STATS_PROCESSORS = 2
@@ -5,9 +5,9 @@ namespace :report do
5
5
  pool_id = args[:pool_id].to_i
6
6
  tournament_pool = Pool.find(pool_id).pool
7
7
  puts "Calculating stats for pool with #{tournament_pool.tournament_entry.picks.number_of_outcomes} possible outcomes ..."
8
- stats = tournament_pool.possibility_stats do |percentage, remaining|
8
+ stats = tournament_pool.possibility_stats(:threads => STATS_PROCESSORS) do |percentage, remaining, num_processed|
9
9
  hashes = '#' * (percentage.to_i/5) + '>'
10
- print "\rCalculating: %3d%% +#{hashes.ljust(20, '-')}+ %5d seconds remaining" % [percentage.to_i, remaining]
10
+ print "\rCalculating: %3d%% +#{hashes.ljust(20, '-')}+ %5d seconds remaining, processed %d" % [percentage.to_i, remaining, num_processed]
11
11
  end
12
12
  puts
13
13
  stats_yaml_file = File.join(RAILS_ROOT, 'db', "stats_#{pool_id}.yml")
@@ -58,7 +58,13 @@ h1 {
58
58
  }
59
59
  #main {
60
60
  float: left;
61
- width: 70%;
61
+ width: 65%;
62
+ margin-left: 10px;
63
+ }
64
+ #main_report {
65
+ float: left;
66
+ width: 100%;
67
+ margin-left: 10px;
62
68
  }
63
69
  #main h2, #main h3, #main p {
64
70
  padding: 0 10px;
metadata CHANGED
@@ -1,80 +1,79 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: tournament
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 4.2.0
4
5
  prerelease:
5
- version: 4.0.2
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Douglas A. Seifert
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-03-14 00:00:00 -07:00
12
+ date: 2011-03-20 00:00:00.000000000 -07:00
14
13
  default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
17
16
  name: main
18
- prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &19735860 !ruby/object:Gem::Requirement
20
18
  none: false
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
24
- version: "0"
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
25
23
  type: :runtime
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
24
  prerelease: false
30
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *19735860
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: &19735380 !ruby/object:Gem::Requirement
31
29
  none: false
32
- requirements:
33
- - - ">="
34
- - !ruby/object:Gem::Version
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
35
33
  version: 0.8.7
36
34
  type: :runtime
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
39
- name: rails
40
35
  prerelease: false
41
- requirement: &id003 !ruby/object:Gem::Requirement
36
+ version_requirements: *19735380
37
+ - !ruby/object:Gem::Dependency
38
+ name: rails
39
+ requirement: &19734880 !ruby/object:Gem::Requirement
42
40
  none: false
43
- requirements:
44
- - - "="
45
- - !ruby/object:Gem::Version
41
+ requirements:
42
+ - - =
43
+ - !ruby/object:Gem::Version
46
44
  version: 2.3.11
47
45
  type: :runtime
48
- version_requirements: *id003
49
- - !ruby/object:Gem::Dependency
50
- name: bones
51
46
  prerelease: false
52
- requirement: &id004 !ruby/object:Gem::Requirement
47
+ version_requirements: *19734880
48
+ - !ruby/object:Gem::Dependency
49
+ name: bones
50
+ requirement: &19734400 !ruby/object:Gem::Requirement
53
51
  none: false
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
57
55
  version: 3.6.5
58
56
  type: :development
59
- version_requirements: *id004
60
- description: |-
61
- Small library, command line program and Rails web GUI for managing a NCAA
62
- basketball tournament pool.
57
+ prerelease: false
58
+ version_requirements: *19734400
59
+ description: ! 'Small library, command line program and Rails web GUI for managing
60
+ a NCAA
61
+
62
+ basketball tournament pool.'
63
63
  email: doug@dseifert.net
64
- executables:
64
+ executables:
65
65
  - benchmark_pool
66
66
  - gui.rb
67
67
  - picker
68
68
  - pool
69
69
  extensions: []
70
-
71
- extra_rdoc_files:
70
+ extra_rdoc_files:
72
71
  - History.txt
73
72
  - README.rdoc
74
73
  - bin/benchmark_pool
75
74
  - bin/picker
76
75
  - bin/pool
77
- files:
76
+ files:
78
77
  - History.txt
79
78
  - README.rdoc
80
79
  - Rakefile
@@ -345,36 +344,35 @@ files:
345
344
  has_rdoc: true
346
345
  homepage: http://www.dseifert.net/code/tournament
347
346
  licenses: []
348
-
349
347
  post_install_message:
350
- rdoc_options:
348
+ rdoc_options:
351
349
  - --line-numbers
352
350
  - --force-update
353
351
  - -W
354
352
  - http://tournament.rubyforge.org/svn/trunk/%s
355
353
  - --main
356
354
  - README.rdoc
357
- require_paths:
355
+ require_paths:
358
356
  - lib
359
- required_ruby_version: !ruby/object:Gem::Requirement
357
+ required_ruby_version: !ruby/object:Gem::Requirement
360
358
  none: false
361
- requirements:
362
- - - ">="
363
- - !ruby/object:Gem::Version
364
- version: "0"
365
- required_rubygems_version: !ruby/object:Gem::Requirement
359
+ requirements:
360
+ - - ! '>='
361
+ - !ruby/object:Gem::Version
362
+ version: '0'
363
+ required_rubygems_version: !ruby/object:Gem::Requirement
366
364
  none: false
367
- requirements:
368
- - - ">="
369
- - !ruby/object:Gem::Version
370
- version: "0"
365
+ requirements:
366
+ - - ! '>='
367
+ - !ruby/object:Gem::Version
368
+ version: '0'
371
369
  requirements: []
372
-
373
370
  rubyforge_project: tournament
374
- rubygems_version: 1.6.1
371
+ rubygems_version: 1.6.2
375
372
  signing_key:
376
373
  specification_version: 3
377
- summary: Small library, command line program and Rails web GUI for managing a NCAA basketball tournament pool.
378
- test_files:
374
+ summary: Small library, command line program and Rails web GUI for managing a NCAA
375
+ basketball tournament pool.
376
+ test_files:
379
377
  - test/test_webgui_installer.rb
380
378
  - test/test_tournament.rb