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 +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
|