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 +9 -1
- data/README.rdoc +10 -1
- data/Rakefile +1 -1
- data/bin/pool +7 -0
- data/lib/tournament.rb +1 -1
- data/lib/tournament/bracket.rb +17 -6
- data/lib/tournament/pool.rb +184 -39
- data/lib/tournament/possibility.rb +14 -0
- data/lib/tournament/webgui_installer.rb +2 -1
- data/test/test_webgui_installer.rb +3 -1
- data/webgui/app/controllers/teams_controller.rb +1 -1
- data/webgui/app/helpers/application_helper.rb +7 -0
- data/webgui/app/views/pool/index.html.erb +5 -2
- data/webgui/app/views/reports/_possibility.html.erb +2 -2
- data/webgui/config/environment.rb +1 -1
- data/webgui/config/initializers/pool.rb +5 -0
- data/webgui/lib/tasks/possibility.rake +2 -2
- data/webgui/public/stylesheets/main.css +7 -1
- metadata +58 -60
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
data/lib/tournament/bracket.rb
CHANGED
@@ -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
|
-
#
|
155
|
-
|
156
|
-
|
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
|
|
data/lib/tournament/pool.rb
CHANGED
@@ -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
|
-
|
385
|
-
|
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.
|
691
|
-
#
|
692
|
-
# and
|
693
|
-
#
|
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
|
-
|
699
|
-
|
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
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
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
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
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
|
-
|
739
|
-
return
|
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.
|
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
|
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
|
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 => "
|
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")
|
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
|
-
|
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:
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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:
|
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:
|
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.
|
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
|
378
|
-
|
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
|