tournament 4.0.2 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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