mirror of
https://github.com/jcwimer/wrestlingApp
synced 2026-03-25 01:14:43 +00:00
Compare commits
32 Commits
8c2ddf55ed
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 7526148ba5 | |||
| e8e0fa291b | |||
| 679fc2fcb9 | |||
| 18d39c6c8f | |||
| ca4d5ce0db | |||
| 654cb84827 | |||
| dc50efe8fc | |||
| 8670ce38c3 | |||
| d359be3ea1 | |||
| e97aa0d680 | |||
| ae8d995b2c | |||
| d57aaac09d | |||
| fcc8a9b9a9 | |||
| b51866e9d8 | |||
| 07d43e7720 | |||
| d8b6cfa8ac | |||
| 5d674f894f | |||
| 25df2a7280 | |||
| 2767274066 | |||
| a2f8c7bced | |||
| 9c2a9d62ad | |||
| 556090c16b | |||
| 86f9c03991 | |||
| c8764c149b | |||
| fe9a9c628c | |||
| 7e4b6d8fc8 | |||
| 940f7b1d00 | |||
| 52df73d14f | |||
| 8b03a74b1e | |||
| b4bca8f10a | |||
| af1f8df4b6 | |||
| 3576445a1c |
@@ -1 +1 @@
|
||||
wrestlingdev
|
||||
wrestlingdev
|
||||
@@ -1 +1 @@
|
||||
ruby-3.2.0
|
||||
ruby-4.0.1
|
||||
6
Gemfile
6
Gemfile
@@ -1,8 +1,8 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby '3.2.0'
|
||||
ruby '4.0.1'
|
||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||
gem 'rails', '8.0.3'
|
||||
gem 'rails', '8.1.2'
|
||||
|
||||
# Added in rails 7.1
|
||||
gem 'rails-html-sanitizer'
|
||||
@@ -67,6 +67,7 @@ gem 'influxdb-rails'
|
||||
gem 'cancancan'
|
||||
gem 'round_robin_tournament'
|
||||
gem 'rb-readline'
|
||||
gem 'rqrcode'
|
||||
# Replacing Delayed Job with Solid Queue
|
||||
# gem 'delayed_job_active_record'
|
||||
gem 'solid_queue'
|
||||
@@ -91,4 +92,3 @@ group :development, :test do
|
||||
# rails-controller-testing is needed for assert_template
|
||||
gem 'rails-controller-testing'
|
||||
end
|
||||
|
||||
|
||||
261
Gemfile.lock
261
Gemfile.lock
@@ -1,29 +1,31 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (8.0.3)
|
||||
actionpack (= 8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
action_text-trix (2.1.16)
|
||||
railties
|
||||
actioncable (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
zeitwerk (~> 2.6)
|
||||
actionmailbox (8.0.3)
|
||||
actionpack (= 8.0.3)
|
||||
activejob (= 8.0.3)
|
||||
activerecord (= 8.0.3)
|
||||
activestorage (= 8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
actionmailbox (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
activejob (= 8.1.2)
|
||||
activerecord (= 8.1.2)
|
||||
activestorage (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
mail (>= 2.8.0)
|
||||
actionmailer (8.0.3)
|
||||
actionpack (= 8.0.3)
|
||||
actionview (= 8.0.3)
|
||||
activejob (= 8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
actionmailer (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
actionview (= 8.1.2)
|
||||
activejob (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
mail (>= 2.8.0)
|
||||
rails-dom-testing (~> 2.2)
|
||||
actionpack (8.0.3)
|
||||
actionview (= 8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
actionpack (8.1.2)
|
||||
actionview (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
nokogiri (>= 1.8.5)
|
||||
rack (>= 2.2.4)
|
||||
rack-session (>= 1.0.1)
|
||||
@@ -31,42 +33,43 @@ GEM
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
useragent (~> 0.16)
|
||||
actiontext (8.0.3)
|
||||
actionpack (= 8.0.3)
|
||||
activerecord (= 8.0.3)
|
||||
activestorage (= 8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
actiontext (8.1.2)
|
||||
action_text-trix (~> 2.1.15)
|
||||
actionpack (= 8.1.2)
|
||||
activerecord (= 8.1.2)
|
||||
activestorage (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
actionview (8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
activejob (8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
activejob (8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
activerecord (8.0.3)
|
||||
activemodel (= 8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
activemodel (8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
activerecord (8.1.2)
|
||||
activemodel (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
timeout (>= 0.4.0)
|
||||
activestorage (8.0.3)
|
||||
actionpack (= 8.0.3)
|
||||
activejob (= 8.0.3)
|
||||
activerecord (= 8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
activestorage (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
activejob (= 8.1.2)
|
||||
activerecord (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
marcel (~> 1.0)
|
||||
activesupport (8.0.3)
|
||||
activesupport (8.1.2)
|
||||
base64
|
||||
benchmark (>= 0.3)
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||
connection_pool (>= 2.2.5)
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
json
|
||||
logger (>= 1.4.2)
|
||||
minitest (>= 5.1)
|
||||
securerandom (>= 0.3)
|
||||
@@ -74,39 +77,39 @@ GEM
|
||||
uri (>= 0.13.1)
|
||||
ast (2.4.3)
|
||||
base64 (0.3.0)
|
||||
bcrypt (3.1.20)
|
||||
benchmark (0.4.1)
|
||||
bigdecimal (3.3.0)
|
||||
bootsnap (1.18.6)
|
||||
bcrypt (3.1.21)
|
||||
bigdecimal (4.0.1)
|
||||
bootsnap (1.23.0)
|
||||
msgpack (~> 1.2)
|
||||
brakeman (7.1.0)
|
||||
brakeman (8.0.2)
|
||||
racc
|
||||
builder (3.3.0)
|
||||
bullet (8.0.8)
|
||||
bullet (8.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
bundler-audit (0.9.2)
|
||||
bundler (>= 1.2.0, < 3)
|
||||
bundler-audit (0.9.3)
|
||||
bundler (>= 1.2.0)
|
||||
thor (~> 1.0)
|
||||
cancancan (3.6.1)
|
||||
concurrent-ruby (1.3.5)
|
||||
connection_pool (2.5.4)
|
||||
chunky_png (1.4.0)
|
||||
concurrent-ruby (1.3.6)
|
||||
connection_pool (3.0.2)
|
||||
crass (1.0.6)
|
||||
daemons (1.4.1)
|
||||
date (3.4.1)
|
||||
date (3.5.1)
|
||||
drb (2.2.3)
|
||||
erb (5.0.3)
|
||||
erb (6.0.1)
|
||||
erubi (1.13.1)
|
||||
et-orbi (1.4.0)
|
||||
tzinfo
|
||||
fugit (1.11.2)
|
||||
et-orbi (~> 1, >= 1.2.11)
|
||||
fugit (1.12.1)
|
||||
et-orbi (~> 1.4)
|
||||
raabro (~> 1.4)
|
||||
globalid (1.3.0)
|
||||
activesupport (>= 6.1)
|
||||
i18n (1.14.7)
|
||||
i18n (1.14.8)
|
||||
concurrent-ruby (~> 1.0)
|
||||
importmap-rails (2.2.2)
|
||||
importmap-rails (2.2.3)
|
||||
actionpack (>= 6.0.0)
|
||||
activesupport (>= 6.0.0)
|
||||
railties (>= 6.0.0)
|
||||
@@ -114,33 +117,36 @@ GEM
|
||||
influxdb-rails (1.0.3)
|
||||
influxdb (~> 0.6, >= 0.6.4)
|
||||
railties (>= 5.0)
|
||||
io-console (0.8.1)
|
||||
irb (1.15.2)
|
||||
io-console (0.8.2)
|
||||
irb (1.17.0)
|
||||
pp (>= 0.6.0)
|
||||
prism (>= 1.3.0)
|
||||
rdoc (>= 4.0.0)
|
||||
reline (>= 0.4.2)
|
||||
jbuilder (2.14.1)
|
||||
actionview (>= 7.0.0)
|
||||
activesupport (>= 7.0.0)
|
||||
jquery-rails (4.6.0)
|
||||
jquery-rails (4.6.1)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (2.15.1)
|
||||
json (2.18.1)
|
||||
language_server-protocol (3.17.0.5)
|
||||
lint_roller (1.1.0)
|
||||
logger (1.7.0)
|
||||
loofah (2.24.1)
|
||||
loofah (2.25.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
mail (2.8.1)
|
||||
mail (2.9.0)
|
||||
logger
|
||||
mini_mime (>= 0.1.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
marcel (1.1.0)
|
||||
mini_mime (1.1.5)
|
||||
minitest (5.25.5)
|
||||
minitest (6.0.1)
|
||||
prism (~> 1.5)
|
||||
mission_control-jobs (1.1.0)
|
||||
actioncable (>= 7.1)
|
||||
actionpack (>= 7.1)
|
||||
@@ -151,12 +157,12 @@ GEM
|
||||
railties (>= 7.1)
|
||||
stimulus-rails
|
||||
turbo-rails
|
||||
mocha (2.7.1)
|
||||
mocha (3.0.2)
|
||||
ruby2_keywords (>= 0.0.5)
|
||||
msgpack (1.8.0)
|
||||
mysql2 (0.5.7)
|
||||
bigdecimal
|
||||
net-imap (0.5.12)
|
||||
net-imap (0.6.3)
|
||||
date
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
@@ -165,64 +171,64 @@ GEM
|
||||
timeout
|
||||
net-smtp (0.5.1)
|
||||
net-protocol
|
||||
nio4r (2.7.4)
|
||||
nokogiri (1.18.10-aarch64-linux-gnu)
|
||||
nio4r (2.7.5)
|
||||
nokogiri (1.19.0-aarch64-linux-gnu)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.18.10-aarch64-linux-musl)
|
||||
nokogiri (1.19.0-aarch64-linux-musl)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.18.10-arm-linux-gnu)
|
||||
nokogiri (1.19.0-arm-linux-gnu)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.18.10-arm-linux-musl)
|
||||
nokogiri (1.19.0-arm-linux-musl)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.18.10-arm64-darwin)
|
||||
nokogiri (1.19.0-arm64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.18.10-x86_64-darwin)
|
||||
nokogiri (1.19.0-x86_64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.18.10-x86_64-linux-gnu)
|
||||
nokogiri (1.19.0-x86_64-linux-gnu)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.18.10-x86_64-linux-musl)
|
||||
nokogiri (1.19.0-x86_64-linux-musl)
|
||||
racc (~> 1.4)
|
||||
parallel (1.27.0)
|
||||
parser (3.3.9.0)
|
||||
parser (3.3.10.1)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
pp (0.6.3)
|
||||
prettyprint
|
||||
prettyprint (0.2.0)
|
||||
prism (1.5.1)
|
||||
prism (1.9.0)
|
||||
propshaft (1.3.1)
|
||||
actionpack (>= 7.0.0)
|
||||
activesupport (>= 7.0.0)
|
||||
rack
|
||||
psych (5.2.6)
|
||||
psych (5.3.1)
|
||||
date
|
||||
stringio
|
||||
puma (7.0.4)
|
||||
puma (7.2.0)
|
||||
nio4r (~> 2.0)
|
||||
raabro (1.4.0)
|
||||
racc (1.8.1)
|
||||
rack (3.2.2)
|
||||
rack (3.2.4)
|
||||
rack-session (2.1.1)
|
||||
base64 (>= 0.1.0)
|
||||
rack (>= 3.0.0)
|
||||
rack-test (2.2.0)
|
||||
rack (>= 1.3)
|
||||
rackup (2.2.1)
|
||||
rackup (2.3.1)
|
||||
rack (>= 3)
|
||||
rails (8.0.3)
|
||||
actioncable (= 8.0.3)
|
||||
actionmailbox (= 8.0.3)
|
||||
actionmailer (= 8.0.3)
|
||||
actionpack (= 8.0.3)
|
||||
actiontext (= 8.0.3)
|
||||
actionview (= 8.0.3)
|
||||
activejob (= 8.0.3)
|
||||
activemodel (= 8.0.3)
|
||||
activerecord (= 8.0.3)
|
||||
activestorage (= 8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
rails (8.1.2)
|
||||
actioncable (= 8.1.2)
|
||||
actionmailbox (= 8.1.2)
|
||||
actionmailer (= 8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
actiontext (= 8.1.2)
|
||||
actionview (= 8.1.2)
|
||||
activejob (= 8.1.2)
|
||||
activemodel (= 8.1.2)
|
||||
activerecord (= 8.1.2)
|
||||
activestorage (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 8.0.3)
|
||||
railties (= 8.1.2)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
@@ -239,9 +245,9 @@ GEM
|
||||
rails_stdout_logging
|
||||
rails_serve_static_assets (0.0.5)
|
||||
rails_stdout_logging (0.0.5)
|
||||
railties (8.0.3)
|
||||
actionpack (= 8.0.3)
|
||||
activesupport (= 8.0.3)
|
||||
railties (8.1.2)
|
||||
actionpack (= 8.1.2)
|
||||
activesupport (= 8.1.2)
|
||||
irb (~> 1.13)
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
@@ -249,17 +255,21 @@ GEM
|
||||
tsort (>= 0.2)
|
||||
zeitwerk (~> 2.6)
|
||||
rainbow (3.1.1)
|
||||
rake (13.3.0)
|
||||
rake (13.3.1)
|
||||
rb-readline (0.5.5)
|
||||
rdoc (6.15.0)
|
||||
rdoc (7.2.0)
|
||||
erb
|
||||
psych (>= 4.0.0)
|
||||
tsort
|
||||
regexp_parser (2.11.3)
|
||||
reline (0.6.2)
|
||||
reline (0.6.3)
|
||||
io-console (~> 0.5)
|
||||
round_robin_tournament (0.1.2)
|
||||
rubocop (1.81.1)
|
||||
rqrcode (3.2.0)
|
||||
chunky_png (~> 1.0)
|
||||
rqrcode_core (~> 2.0)
|
||||
rqrcode_core (2.1.0)
|
||||
rubocop (1.84.2)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
lint_roller (~> 1.1.0)
|
||||
@@ -267,15 +277,15 @@ GEM
|
||||
parser (>= 3.3.0.2)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 2.9.3, < 3.0)
|
||||
rubocop-ast (>= 1.47.1, < 2.0)
|
||||
rubocop-ast (>= 1.49.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 4.0)
|
||||
rubocop-ast (1.47.1)
|
||||
rubocop-ast (1.49.0)
|
||||
parser (>= 3.3.7.2)
|
||||
prism (~> 1.4)
|
||||
prism (~> 1.7)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
sdoc (2.6.4)
|
||||
sdoc (2.6.5)
|
||||
rdoc (>= 5.0)
|
||||
securerandom (0.4.1)
|
||||
solid_cable (3.0.12)
|
||||
@@ -283,50 +293,50 @@ GEM
|
||||
activejob (>= 7.2)
|
||||
activerecord (>= 7.2)
|
||||
railties (>= 7.2)
|
||||
solid_cache (1.0.7)
|
||||
solid_cache (1.0.10)
|
||||
activejob (>= 7.2)
|
||||
activerecord (>= 7.2)
|
||||
railties (>= 7.2)
|
||||
solid_queue (1.2.1)
|
||||
solid_queue (1.3.1)
|
||||
activejob (>= 7.1)
|
||||
activerecord (>= 7.1)
|
||||
concurrent-ruby (>= 1.3.1)
|
||||
fugit (~> 1.11.0)
|
||||
fugit (~> 1.11)
|
||||
railties (>= 7.1)
|
||||
thor (>= 1.3.1)
|
||||
spring (4.4.0)
|
||||
sqlite3 (2.7.4-aarch64-linux-gnu)
|
||||
sqlite3 (2.7.4-aarch64-linux-musl)
|
||||
sqlite3 (2.7.4-arm-linux-gnu)
|
||||
sqlite3 (2.7.4-arm-linux-musl)
|
||||
sqlite3 (2.7.4-arm64-darwin)
|
||||
sqlite3 (2.7.4-x86_64-darwin)
|
||||
sqlite3 (2.7.4-x86_64-linux-gnu)
|
||||
sqlite3 (2.7.4-x86_64-linux-musl)
|
||||
spring (4.4.2)
|
||||
sqlite3 (2.9.0-aarch64-linux-gnu)
|
||||
sqlite3 (2.9.0-aarch64-linux-musl)
|
||||
sqlite3 (2.9.0-arm-linux-gnu)
|
||||
sqlite3 (2.9.0-arm-linux-musl)
|
||||
sqlite3 (2.9.0-arm64-darwin)
|
||||
sqlite3 (2.9.0-x86_64-darwin)
|
||||
sqlite3 (2.9.0-x86_64-linux-gnu)
|
||||
sqlite3 (2.9.0-x86_64-linux-musl)
|
||||
stimulus-rails (1.3.4)
|
||||
railties (>= 6.0.0)
|
||||
stringio (3.1.7)
|
||||
thor (1.4.0)
|
||||
timeout (0.4.3)
|
||||
stringio (3.2.0)
|
||||
thor (1.5.0)
|
||||
timeout (0.6.0)
|
||||
tsort (0.2.0)
|
||||
turbo-rails (2.0.17)
|
||||
turbo-rails (2.0.23)
|
||||
actionpack (>= 7.1.0)
|
||||
railties (>= 7.1.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
tzinfo-data (1.2025.2)
|
||||
tzinfo-data (1.2025.3)
|
||||
tzinfo (>= 1.0.0)
|
||||
unicode-display_width (3.2.0)
|
||||
unicode-emoji (~> 4.1)
|
||||
unicode-emoji (4.1.0)
|
||||
unicode-emoji (4.2.0)
|
||||
uniform_notifier (1.18.0)
|
||||
uri (1.0.4)
|
||||
uri (1.1.1)
|
||||
useragent (0.16.11)
|
||||
websocket-driver (0.8.0)
|
||||
base64
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
zeitwerk (2.7.3)
|
||||
zeitwerk (2.7.4)
|
||||
|
||||
PLATFORMS
|
||||
aarch64-linux-gnu
|
||||
@@ -355,12 +365,13 @@ DEPENDENCIES
|
||||
mysql2
|
||||
propshaft
|
||||
puma
|
||||
rails (= 8.0.3)
|
||||
rails (= 8.1.2)
|
||||
rails-controller-testing
|
||||
rails-html-sanitizer
|
||||
rails_12factor
|
||||
rb-readline
|
||||
round_robin_tournament
|
||||
rqrcode
|
||||
rubocop
|
||||
sdoc
|
||||
solid_cable
|
||||
@@ -373,7 +384,7 @@ DEPENDENCIES
|
||||
tzinfo-data
|
||||
|
||||
RUBY VERSION
|
||||
ruby 3.2.0p0
|
||||
ruby 4.0.1p0
|
||||
|
||||
BUNDLED WITH
|
||||
2.6.9
|
||||
4.0.3
|
||||
|
||||
13
README.md
13
README.md
@@ -7,8 +7,8 @@ This application is being created to run a wrestling tournament.
|
||||
**Public Production Url:** [https://wrestlingdev.com](https://wrestlingdev.com)
|
||||
|
||||
**App Info**
|
||||
* Ruby 3.2.0
|
||||
* Rails 8.0.2
|
||||
* Ruby 4.0.1
|
||||
* Rails 8.1.2
|
||||
* DB MySQL/MariaDB
|
||||
* Solid Cache -> MySQL/MariaDB for html partial caching
|
||||
* Solid Queue -> MySQL/MariaDB for background job processing
|
||||
@@ -34,11 +34,14 @@ In development environments, background jobs run inline (synchronously) by defau
|
||||
|
||||
To run a single test file:
|
||||
1. Get a shell with ruby and rails: `bash bin/rails-dev-run.sh wrestlingdev-development`
|
||||
2. `rake test TEST=test/models/match_test.rb`
|
||||
2. `rake test TEST=test/models/match_test.rb` OR `rails test test/models/match_test.rb`
|
||||
|
||||
To run a single test inside a file:
|
||||
1. Get a shell with ruby and rails: `bash bin/rails-dev-run.sh wrestlingdev-development`
|
||||
2. `rake test TEST=test/models/match_test.rb TESTOPTS="--name='/test_Match_should_not_be_valid_if_an_incorrect_win_type_is_given/'"`
|
||||
2. `rake test TEST=test/models/match_test.rb TESTOPTS="--name='/test_Match_should_not_be_valid_if_an_incorrect_win_type_is_given/'"` OR `rails test test/models/match_test.rb --name=/test_Match_should_not_be_valid_if_an_incorrect_win_type_is_given/`
|
||||
|
||||
To run tests in verbose mode (outputs the time for each test file and the test file name)
|
||||
`rails test -v`
|
||||
|
||||
## Develop with rvm
|
||||
With rvm installed, run `rvm install ruby-3.2.0`
|
||||
@@ -149,7 +152,7 @@ SolidQueue plugin enabled in Puma
|
||||
```
|
||||
|
||||
I have deployed Mission Control as a UI for SolidQueue. The uri for mission control is `/jobs`.
|
||||
For the development environment, the user/password is dev/secret. For the production environment, it is defined by environment variables.
|
||||
For the development environment, the user/password is dev/secret. For the production environment, it is defined by environment variables WRESTLINGDEV_MISSION_CONTROL_USER/WRESTLINGDEV_MISSION_CONTROL_PASSWORD. You can see this in `config/environments/production.rb` and `config/environments/development.rb`.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import "@hotwired/turbo-rails";
|
||||
import { createConsumer } from "@rails/actioncable"; // Import createConsumer directly
|
||||
import "jquery";
|
||||
import "bootstrap";
|
||||
import "datatables.net";
|
||||
|
||||
// Stimulus setup
|
||||
import { Application } from "@hotwired/stimulus";
|
||||
@@ -20,12 +19,14 @@ import WrestlerColorController from "controllers/wrestler_color_controller";
|
||||
import MatchScoreController from "controllers/match_score_controller";
|
||||
import MatchDataController from "controllers/match_data_controller";
|
||||
import MatchSpectateController from "controllers/match_spectate_controller";
|
||||
import UpMatchesConnectionController from "controllers/up_matches_connection_controller";
|
||||
|
||||
// Register controllers
|
||||
application.register("wrestler-color", WrestlerColorController);
|
||||
application.register("match-score", MatchScoreController);
|
||||
application.register("match-data", MatchDataController);
|
||||
application.register("match-spectate", MatchSpectateController);
|
||||
application.register("up-matches-connection", UpMatchesConnectionController);
|
||||
|
||||
// Your existing Action Cable consumer setup
|
||||
(function() {
|
||||
@@ -39,9 +40,9 @@ application.register("match-spectate", MatchSpectateController);
|
||||
}
|
||||
}).call(this);
|
||||
|
||||
console.log("Propshaft/Importmap application.js initialized with jQuery, Bootstrap, Stimulus, and DataTables.");
|
||||
console.log("Propshaft/Importmap application.js initialized with jQuery, Bootstrap, and Stimulus.");
|
||||
|
||||
// If you have custom JavaScript files in app/javascript/ that were previously
|
||||
// handled by Sprockets `require_tree`, you'll need to import them here explicitly.
|
||||
// For example:
|
||||
// import "./my_custom_logic";
|
||||
// import "./my_custom_logic";
|
||||
|
||||
@@ -13,6 +13,8 @@ export default class extends Controller {
|
||||
|
||||
connect() {
|
||||
console.log("Match data controller connected")
|
||||
this.isConnected = false
|
||||
this.pendingLocalSync = { w1: false, w2: false }
|
||||
|
||||
this.w1 = {
|
||||
name: "w1",
|
||||
@@ -69,6 +71,7 @@ export default class extends Controller {
|
||||
wrestler.updated_at = new Date().toISOString()
|
||||
this.updateHtmlValues()
|
||||
this.saveToLocalStorage(wrestler)
|
||||
if (!this.isConnected) this.pendingLocalSync[wrestler.name] = true
|
||||
|
||||
// Send the update via Action Cable if subscribed
|
||||
if (this.matchSubscription) {
|
||||
@@ -109,6 +112,7 @@ export default class extends Controller {
|
||||
// Update the internal JS object
|
||||
wrestler.stats = newValue
|
||||
wrestler.updated_at = new Date().toISOString()
|
||||
if (!this.isConnected) this.pendingLocalSync[wrestler.name] = true
|
||||
|
||||
// Save to localStorage
|
||||
this.saveToLocalStorage(wrestler)
|
||||
@@ -334,15 +338,18 @@ export default class extends Controller {
|
||||
{
|
||||
connected: () => {
|
||||
console.log(`[Stats AC] Connected to MatchStatsChannel for match ID: ${matchId}`)
|
||||
this.isConnected = true
|
||||
if (this.statusIndicatorTarget) {
|
||||
this.statusIndicatorTarget.innerText = "Connected: Stats will update in real-time."
|
||||
this.statusIndicatorTarget.classList.remove('alert-info', 'alert-warning', 'alert-danger')
|
||||
this.statusIndicatorTarget.classList.add('alert-success')
|
||||
}
|
||||
this.sendCurrentStatsOnReconnect()
|
||||
},
|
||||
|
||||
disconnected: () => {
|
||||
console.log(`[Stats AC] Disconnected from MatchStatsChannel`)
|
||||
this.isConnected = false
|
||||
if (this.statusIndicatorTarget) {
|
||||
this.statusIndicatorTarget.innerText = "Disconnected: Stats updates paused."
|
||||
this.statusIndicatorTarget.classList.remove('alert-info', 'alert-success', 'alert-danger')
|
||||
@@ -356,15 +363,25 @@ export default class extends Controller {
|
||||
// Update w1 stats
|
||||
if (data.w1_stat !== undefined && this.w1StatTarget) {
|
||||
console.log(`[Stats AC] Updating w1_stat: ${data.w1_stat.substring(0, 30)}...`)
|
||||
this.w1.stats = data.w1_stat
|
||||
this.w1StatTarget.value = data.w1_stat
|
||||
if (!this.pendingLocalSync.w1 || data.w1_stat === this.w1.stats) {
|
||||
this.w1.stats = data.w1_stat
|
||||
this.w1StatTarget.value = data.w1_stat
|
||||
this.pendingLocalSync.w1 = false
|
||||
} else {
|
||||
console.log('[Stats AC] Skipping w1_stat overwrite due to pending local changes.')
|
||||
}
|
||||
}
|
||||
|
||||
// Update w2 stats
|
||||
if (data.w2_stat !== undefined && this.w2StatTarget) {
|
||||
console.log(`[Stats AC] Updating w2_stat: ${data.w2_stat.substring(0, 30)}...`)
|
||||
this.w2.stats = data.w2_stat
|
||||
this.w2StatTarget.value = data.w2_stat
|
||||
if (!this.pendingLocalSync.w2 || data.w2_stat === this.w2.stats) {
|
||||
this.w2.stats = data.w2_stat
|
||||
this.w2StatTarget.value = data.w2_stat
|
||||
this.pendingLocalSync.w2 = false
|
||||
} else {
|
||||
console.log('[Stats AC] Skipping w2_stat overwrite due to pending local changes.')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -381,4 +398,23 @@ export default class extends Controller {
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sendCurrentStatsOnReconnect() {
|
||||
if (!this.matchSubscription) return
|
||||
const payload = {}
|
||||
if (typeof this.w1?.stats === 'string' && this.w1.stats.length > 0) {
|
||||
payload.new_w1_stat = this.w1.stats
|
||||
this.pendingLocalSync.w1 = true
|
||||
}
|
||||
if (typeof this.w2?.stats === 'string' && this.w2.stats.length > 0) {
|
||||
payload.new_w2_stat = this.w2.stats
|
||||
this.pendingLocalSync.w2 = true
|
||||
}
|
||||
if (Object.keys(payload).length > 0) {
|
||||
console.log('[ActionCable] Reconnect sync: sending current stats payload:', payload)
|
||||
this.matchSubscription.perform('send_stat', payload)
|
||||
} else {
|
||||
console.log('[ActionCable] Reconnect sync: no local stats to send.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,11 @@ export default class extends Controller {
|
||||
this.statusIndicatorTarget.classList.remove('alert-danger', 'alert-secondary', 'text-danger', 'text-dark')
|
||||
this.statusIndicatorTarget.classList.add('alert-success')
|
||||
}
|
||||
try {
|
||||
this.matchSubscription.perform('request_sync')
|
||||
} catch (e) {
|
||||
console.error('[Spectator AC] request_sync perform failed:', e)
|
||||
}
|
||||
},
|
||||
disconnected: () => {
|
||||
console.log(`[Spectator AC Callback] Disconnected: ${matchId}`)
|
||||
@@ -131,4 +136,4 @@ export default class extends Controller {
|
||||
this.finishedTarget.textContent = data.finished ? 'Yes' : 'No'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Controller } from "@hotwired/stimulus"
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["stream", "statusIndicator"]
|
||||
|
||||
connect() {
|
||||
this.setupSubscription()
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.cleanupSubscription()
|
||||
}
|
||||
|
||||
setupSubscription() {
|
||||
this.cleanupSubscription()
|
||||
this.setStatus("Connecting to server for real-time bout board updates...", "info")
|
||||
|
||||
if (!this.hasStreamTarget) {
|
||||
this.setStatus("Error: Stream source not found.", "danger")
|
||||
return
|
||||
}
|
||||
|
||||
const signedStreamName = this.streamTarget.getAttribute("signed-stream-name")
|
||||
if (!signedStreamName) {
|
||||
this.setStatus("Error: Invalid stream source.", "danger")
|
||||
return
|
||||
}
|
||||
|
||||
if (!window.App || !window.App.cable) {
|
||||
this.setStatus("Error: WebSockets unavailable. Bout board won't update in real-time. Refresh the page to update.", "danger")
|
||||
return
|
||||
}
|
||||
|
||||
this.subscription = App.cable.subscriptions.create(
|
||||
{
|
||||
channel: "Turbo::StreamsChannel",
|
||||
signed_stream_name: signedStreamName
|
||||
},
|
||||
{
|
||||
connected: () => {
|
||||
this.setStatus("Connected: Bout board updating in real-time.", "success")
|
||||
},
|
||||
disconnected: () => {
|
||||
this.setStatus("Disconnected: Live bout board updates paused.", "warning")
|
||||
},
|
||||
rejected: () => {
|
||||
this.setStatus("Error: Live bout board connection rejected.", "danger")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
cleanupSubscription() {
|
||||
if (!this.subscription) return
|
||||
this.subscription.unsubscribe()
|
||||
this.subscription = null
|
||||
}
|
||||
|
||||
setStatus(message, type) {
|
||||
if (!this.hasStatusIndicatorTarget) return
|
||||
|
||||
this.statusIndicatorTarget.innerText = message
|
||||
this.statusIndicatorTarget.classList.remove("alert-secondary", "alert-info", "alert-success", "alert-warning", "alert-danger")
|
||||
|
||||
if (type === "success") this.statusIndicatorTarget.classList.add("alert-success")
|
||||
else if (type === "warning") this.statusIndicatorTarget.classList.add("alert-warning")
|
||||
else if (type === "danger") this.statusIndicatorTarget.classList.add("alert-danger")
|
||||
else this.statusIndicatorTarget.classList.add("alert-info")
|
||||
}
|
||||
}
|
||||
@@ -60,4 +60,29 @@ class MatchChannel < ApplicationCable::Channel
|
||||
Rails.logger.info "[MatchChannel] No new stat data provided in send_stat for match #{@match.id}, not updating DB or broadcasting."
|
||||
end
|
||||
end
|
||||
|
||||
# Called when client wants the latest stats immediately after reconnect
|
||||
def request_sync
|
||||
unless @match
|
||||
Rails.logger.error "[MatchChannel] Error: request_sync called but @match is nil. Client params on sub: #{params[:match_id]}"
|
||||
return
|
||||
end
|
||||
|
||||
payload = {
|
||||
w1_stat: @match.w1_stat,
|
||||
w2_stat: @match.w2_stat,
|
||||
score: @match.score,
|
||||
win_type: @match.win_type,
|
||||
winner_name: @match.winner&.name,
|
||||
winner_id: @match.winner_id,
|
||||
finished: @match.finished
|
||||
}.compact
|
||||
|
||||
if payload.present?
|
||||
Rails.logger.info "[MatchChannel] request_sync transmit for match #{@match.id} with payload: #{payload.inspect}"
|
||||
transmit(payload)
|
||||
else
|
||||
Rails.logger.info "[MatchChannel] request_sync payload empty for match #{@match.id}, not transmitting."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ class MatAssignmentRulesController < ApplicationController
|
||||
before_action :set_mat_assignment_rule, only: [:edit, :update, :destroy]
|
||||
|
||||
def index
|
||||
@mat_assignment_rules = @tournament.mat_assignment_rules
|
||||
@mat_assignment_rules = @tournament.mat_assignment_rules.includes(:mat)
|
||||
@weights_by_id = @tournament.weights.index_by(&:id) # For quick lookup
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class MatchesController < ApplicationController
|
||||
before_action :set_match, only: [:show, :edit, :update, :stat, :spectate]
|
||||
before_action :check_access, only: [:edit,:update, :stat]
|
||||
before_action :set_match, only: [:show, :edit, :update, :stat, :spectate, :edit_assignment, :update_assignment]
|
||||
before_action :check_access, only: [:edit, :update, :stat, :edit_assignment, :update_assignment]
|
||||
|
||||
# GET /matches/1
|
||||
# GET /matches/1.json
|
||||
@@ -21,7 +21,7 @@ class MatchesController < ApplicationController
|
||||
session[:return_path] = "/tournaments/#{@match.tournament.id}/matches"
|
||||
end
|
||||
|
||||
def stat
|
||||
def stat
|
||||
# @show_next_bout_button = false
|
||||
if params[:match]
|
||||
@match = Match.where(:id => params[:match]).includes(:wrestlers).first
|
||||
@@ -50,8 +50,21 @@ class MatchesController < ApplicationController
|
||||
end
|
||||
@tournament = @match.tournament
|
||||
end
|
||||
session[:return_path] = "/tournaments/#{@tournament.id}/matches"
|
||||
session[:error_return_path] = "/matches/#{@match.id}/stat"
|
||||
if @match&.mat
|
||||
@mat = @match.mat
|
||||
queue_position = @mat.queue_position_for_match(@match)
|
||||
@next_match = queue_position == 1 ? @mat.queue2_match : nil
|
||||
@show_next_bout_button = queue_position == 1
|
||||
if request.referer&.include?("/tournaments/#{@tournament.id}/matches")
|
||||
session[:return_path] = "/tournaments/#{@tournament.id}/matches"
|
||||
else
|
||||
session[:return_path] = mat_path(@mat)
|
||||
end
|
||||
session[:error_return_path] = "/matches/#{@match.id}/stat"
|
||||
else
|
||||
session[:return_path] = "/tournaments/#{@tournament.id}/matches"
|
||||
session[:error_return_path] = "/matches/#{@match.id}/stat"
|
||||
end
|
||||
end
|
||||
|
||||
# GET /matches/:id/spectate
|
||||
@@ -71,6 +84,49 @@ class MatchesController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
# GET /matches/1/edit_assignment
|
||||
def edit_assignment
|
||||
@tournament = @match.tournament
|
||||
@mats = @tournament.mats.sort_by(&:name)
|
||||
@current_mat = @match.mat
|
||||
@current_queue_position = @current_mat&.queue_position_for_match(@match)
|
||||
session[:return_path] = "/tournaments/#{@tournament.id}/matches"
|
||||
end
|
||||
|
||||
# PATCH /matches/1/update_assignment
|
||||
def update_assignment
|
||||
@tournament = @match.tournament
|
||||
mat_id = params.dig(:match, :mat_id)
|
||||
queue_position = params.dig(:match, :queue_position)
|
||||
|
||||
if mat_id.blank?
|
||||
Mat.where("queue1 = :match_id OR queue2 = :match_id OR queue3 = :match_id OR queue4 = :match_id", match_id: @match.id)
|
||||
.find_each { |mat| mat.remove_match_from_queue_and_collapse!(@match.id) }
|
||||
@match.update(mat_id: nil)
|
||||
redirect_to session.delete(:return_path) || "/tournaments/#{@tournament.id}/matches", notice: "Match assignment cleared."
|
||||
return
|
||||
end
|
||||
|
||||
if queue_position.blank?
|
||||
redirect_to edit_assignment_match_path(@match), alert: "Queue position is required when selecting a mat."
|
||||
return
|
||||
end
|
||||
|
||||
unless %w[1 2 3 4].include?(queue_position.to_s)
|
||||
redirect_to edit_assignment_match_path(@match), alert: "Queue position must be between 1 and 4."
|
||||
return
|
||||
end
|
||||
|
||||
mat = @tournament.mats.find_by(id: mat_id)
|
||||
unless mat
|
||||
redirect_to edit_assignment_match_path(@match), alert: "Selected mat was not found."
|
||||
return
|
||||
end
|
||||
|
||||
mat.assign_match_to_queue!(@match, queue_position)
|
||||
redirect_to session.delete(:return_path) || "/tournaments/#{@tournament.id}/matches", notice: "Match assignment updated."
|
||||
end
|
||||
|
||||
# PATCH/PUT /matches/1
|
||||
# PATCH/PUT /matches/1.json
|
||||
def update
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
class MatsController < ApplicationController
|
||||
before_action :set_mat, only: [:show, :edit, :update, :destroy, :assign_next_match]
|
||||
before_action :check_access, only: [:new,:create,:update,:destroy,:edit,:show, :assign_next_match]
|
||||
before_action :check_for_matches, only: [:show]
|
||||
|
||||
# GET /mats/1
|
||||
# GET /mats/1.json
|
||||
def show
|
||||
bout_number_param = params[:bout_number] # Read the bout_number from the URL params
|
||||
|
||||
if bout_number_param
|
||||
@show_next_bout_button = false
|
||||
@match = @mat.unfinished_matches.find { |m| m.bout_number == bout_number_param.to_i }
|
||||
bout_number_param = params[:bout_number]
|
||||
@queue_matches = @mat.queue_matches
|
||||
@match = if bout_number_param
|
||||
@queue_matches.compact.find { |m| m.bout_number == bout_number_param.to_i }
|
||||
else
|
||||
@show_next_bout_button = true
|
||||
@match = @mat.unfinished_matches.first
|
||||
@queue_matches[0]
|
||||
end
|
||||
|
||||
@next_match = @mat.unfinished_matches.second # Second unfinished match on the mat
|
||||
# If a requested bout is no longer queued, fall back to queue1.
|
||||
@match ||= @queue_matches[0]
|
||||
@next_match = @queue_matches[1]
|
||||
@show_next_bout_button = false
|
||||
|
||||
@wrestlers = []
|
||||
if @match
|
||||
@@ -82,8 +81,8 @@ class MatsController < ApplicationController
|
||||
def assign_next_match
|
||||
@tournament = @mat.tournament_id
|
||||
respond_to do |format|
|
||||
if @mat.assign_next_match
|
||||
format.html { redirect_to "/tournaments/#{@mat.tournament.id}", notice: "Next Match on Mat #{@mat.name} successfully completed." }
|
||||
if @mat.advance_queue!
|
||||
format.html { redirect_to "/tournaments/#{@mat.tournament.id}", notice: "Mat #{@mat.name} queue advanced." }
|
||||
format.json { head :no_content }
|
||||
else
|
||||
format.html { redirect_to "/tournaments/#{@mat.tournament.id}", alert: "There was an error." }
|
||||
@@ -142,11 +141,4 @@ class MatsController < ApplicationController
|
||||
end
|
||||
|
||||
|
||||
def check_for_matches
|
||||
if @mat
|
||||
if @mat.tournament.matches.empty?
|
||||
redirect_to "/tournaments/#{@tournament.id}/no_matches"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
class StaticPagesController < ApplicationController
|
||||
|
||||
def my_tournaments
|
||||
tournaments_created = current_user.tournaments
|
||||
tournaments_delegated = current_user.delegated_tournaments
|
||||
tournaments_created = current_user.tournaments.to_a
|
||||
tournaments_delegated = current_user.delegated_tournaments.to_a
|
||||
all_tournaments = tournaments_created + tournaments_delegated
|
||||
@tournaments = all_tournaments.sort_by{|t| t.days_until_start}
|
||||
@schools = current_user.delegated_schools
|
||||
@schools = current_user.delegated_schools.includes(:tournament)
|
||||
end
|
||||
|
||||
def not_allowed
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
class TournamentsController < ApplicationController
|
||||
before_action :set_tournament, only: [:all_results, :delete_school_keys, :generate_school_keys,:reset_bout_board,:calculate_team_scores,:bout_sheets,:swap,:weigh_in_sheet,:error,:teampointadjust,:remove_teampointadjust,:remove_school_delegate,:remove_delegate,:school_delegate,:delegate,:matches,:weigh_in,:weigh_in_weight,:create_custom_weights,:show,:edit,:update,:destroy,:up_matches,:no_matches,:team_scores,:generate_matches,:bracket,:all_brackets]
|
||||
before_action :check_access_manage, only: [:delete_school_keys, :generate_school_keys,:reset_bout_board,:calculate_team_scores,:swap,:weigh_in_sheet,:teampointadjust,:remove_teampointadjust,:remove_school_delegate,:school_delegate,:weigh_in,:weigh_in_weight,:create_custom_weights,:update,:edit,:generate_matches,:matches]
|
||||
before_action :set_tournament, only: [:all_results, :delete_school_keys, :generate_school_keys,:reset_bout_board,:calculate_team_scores,:bout_sheets,:swap,:weigh_in_sheet,:error,:teampointadjust,:remove_teampointadjust,:remove_school_delegate,:remove_delegate,:school_delegate,:delegate,:matches,:weigh_in,:weigh_in_weight,:create_custom_weights,:show,:edit,:update,:destroy,:up_matches,:no_matches,:team_scores,:generate_matches,:bracket,:all_brackets,:qrcode]
|
||||
before_action :check_access_manage, only: [:delete_school_keys, :generate_school_keys,:reset_bout_board,:calculate_team_scores,:swap,:weigh_in_sheet,:teampointadjust,:remove_teampointadjust,:remove_school_delegate,:school_delegate,:weigh_in,:weigh_in_weight,:create_custom_weights,:update,:edit,:generate_matches,:matches,:qrcode]
|
||||
before_action :check_access_destroy, only: [:destroy,:delegate,:remove_delegate]
|
||||
before_action :check_tournament_errors, only: [:generate_matches]
|
||||
before_action :check_for_matches, only: [:all_results,:up_matches,:bracket,:all_brackets]
|
||||
before_action :check_for_matches, only: [:all_results,:bracket,:all_brackets]
|
||||
before_action :check_access_read, only: [:all_results,:up_matches,:bracket,:all_brackets]
|
||||
|
||||
def weigh_in_sheet
|
||||
|
||||
@schools = @tournament.schools.includes(wrestlers: :weight)
|
||||
end
|
||||
|
||||
def calculate_team_scores
|
||||
@@ -92,12 +92,9 @@ class TournamentsController < ApplicationController
|
||||
end
|
||||
end
|
||||
end
|
||||
@users_delegates = []
|
||||
@tournament.schools.each do |s|
|
||||
s.delegates.each do |d|
|
||||
@users_delegates << d
|
||||
end
|
||||
end
|
||||
@users_delegates = SchoolDelegate.includes(:user, :school)
|
||||
.joins(:school)
|
||||
.where(schools: { tournament_id: @tournament.id })
|
||||
end
|
||||
|
||||
def delegate
|
||||
@@ -115,11 +112,63 @@ class TournamentsController < ApplicationController
|
||||
end
|
||||
end
|
||||
end
|
||||
@users_delegates = @tournament.delegates
|
||||
@users_delegates = @tournament.delegates.includes(:user)
|
||||
end
|
||||
|
||||
def matches
|
||||
@matches = @tournament.matches.includes(:wrestlers,:schools).sort_by{|m| m.bout_number}
|
||||
per_page = 50
|
||||
@page = params[:page].to_i > 0 ? params[:page].to_i : 1
|
||||
offset = (@page - 1) * per_page
|
||||
matches_table = Match.arel_table
|
||||
|
||||
matches_scope = @tournament.matches.order(:bout_number)
|
||||
|
||||
if params[:search].present?
|
||||
wrestlers_table = Wrestler.arel_table
|
||||
schools_table = School.arel_table
|
||||
search_terms = params[:search].downcase.split
|
||||
|
||||
search_terms.each do |term|
|
||||
escaped_term = ActiveRecord::Base.sanitize_sql_like(term)
|
||||
pattern = "%#{escaped_term}%"
|
||||
|
||||
matching_wrestler_ids = Wrestler
|
||||
.joins(:weight)
|
||||
.left_outer_joins(:school)
|
||||
.where(weights: { tournament_id: @tournament.id })
|
||||
.where(
|
||||
wrestlers_table[:name].matches(pattern)
|
||||
.or(schools_table[:name].matches(pattern))
|
||||
)
|
||||
.distinct
|
||||
.select(:id)
|
||||
|
||||
term_scope = @tournament.matches.where(w1: matching_wrestler_ids)
|
||||
.or(@tournament.matches.where(w2: matching_wrestler_ids))
|
||||
|
||||
if term.match?(/\A\d+\z/)
|
||||
term_scope = term_scope.or(@tournament.matches.where(bout_number: term.to_i))
|
||||
end
|
||||
|
||||
matches_scope = matches_scope.where(id: term_scope.select(:id))
|
||||
end
|
||||
end
|
||||
|
||||
@total_count = matches_scope.count
|
||||
@total_pages = (@total_count / per_page.to_f).ceil
|
||||
@per_page = per_page
|
||||
|
||||
loser1_not_bye = matches_table[:loser1_name].not_eq("BYE").or(matches_table[:loser1_name].eq(nil))
|
||||
loser2_not_bye = matches_table[:loser2_name].not_eq("BYE").or(matches_table[:loser2_name].eq(nil))
|
||||
|
||||
non_bye_scope = matches_scope.where(loser1_not_bye).where(loser2_not_bye)
|
||||
@matches_without_byes_count = non_bye_scope.count
|
||||
@unfinished_matches_without_byes_count = non_bye_scope.where(finished: [nil, 0]).count
|
||||
|
||||
@matches = matches_scope
|
||||
.includes({ wrestler1: :school }, { wrestler2: :school }, { weight: :matches })
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
if @match
|
||||
@w1 = @match.wrestler1
|
||||
@w2 = @match.wrestler2
|
||||
@@ -129,10 +178,18 @@ class TournamentsController < ApplicationController
|
||||
|
||||
def weigh_in_weight
|
||||
if params[:wrestler]
|
||||
Wrestler.update(params[:wrestler].keys, params[:wrestler].values)
|
||||
sanitized_wrestlers = params.require(:wrestler).to_unsafe_h.each_with_object({}) do |(wrestler_id, attributes), result|
|
||||
permitted = ActionController::Parameters.new(attributes).permit(:offical_weight)
|
||||
result[wrestler_id] = permitted
|
||||
end
|
||||
Wrestler.update(sanitized_wrestlers.keys, sanitized_wrestlers.values) if sanitized_wrestlers.present?
|
||||
redirect_to "/tournaments/#{@tournament.id}/weigh_in/#{params[:weight]}", notice: "Weights were successfully recorded."
|
||||
return
|
||||
end
|
||||
if params[:weight]
|
||||
@weight = Weight.where(:id => params[:weight]).includes(:wrestlers).first
|
||||
@weight = Weight.where(id: params[:weight])
|
||||
.includes(wrestlers: [:school, :weight])
|
||||
.first
|
||||
@tournament_id = @tournament.id
|
||||
@tournament_name = @tournament.name
|
||||
@weights = @tournament.weights
|
||||
@@ -159,8 +216,11 @@ class TournamentsController < ApplicationController
|
||||
def all_brackets
|
||||
@schools = @tournament.schools
|
||||
@schools = @schools.sort_by{|s| s.page_score_string}.reverse!
|
||||
@matches = @tournament.matches.includes(:wrestlers,:schools)
|
||||
@weights = @tournament.weights.includes(:matches,:wrestlers)
|
||||
@weights = @tournament.weights.includes(:matches, wrestlers: :school)
|
||||
all_matches = @tournament.matches.includes(:weight, { wrestler1: :school }, { wrestler2: :school })
|
||||
all_wrestlers = @tournament.wrestlers.includes(:school, :weight)
|
||||
@matches_by_weight_id = all_matches.group_by(&:weight_id)
|
||||
@wrestlers_by_weight_id = all_wrestlers.group_by(&:weight_id)
|
||||
end
|
||||
|
||||
def bracket
|
||||
@@ -196,27 +256,37 @@ class TournamentsController < ApplicationController
|
||||
|
||||
end
|
||||
|
||||
def qrcode
|
||||
@tournament_url = tournament_url(@tournament)
|
||||
@qrcode = RQRCode::QRCode.new(@tournament_url)
|
||||
end
|
||||
|
||||
|
||||
def up_matches
|
||||
# .where.not(loser1_name: 'BYE') won't return matches with NULL loser1_name
|
||||
# so I was only getting back matches with Loser of BOUT_NUMBER
|
||||
@matches = @tournament.matches
|
||||
.where("mat_id is NULL and (finished != 1 or finished is NULL)")
|
||||
.where("loser1_name != ? OR loser1_name IS NULL", "BYE")
|
||||
.where("loser2_name != ? OR loser2_name IS NULL", "BYE")
|
||||
.order('bout_number ASC')
|
||||
.limit(10).includes(:wrestlers)
|
||||
@mats = @tournament.mats.includes(:matches)
|
||||
@matches = @tournament.up_matches_unassigned_matches
|
||||
@mats = @tournament.up_matches_mats
|
||||
end
|
||||
|
||||
def bout_sheets
|
||||
matches_scope = @tournament.matches
|
||||
.where("loser1_name != ? OR loser1_name IS NULL", "BYE")
|
||||
.where("loser2_name != ? OR loser2_name IS NULL", "BYE")
|
||||
|
||||
if params[:round]
|
||||
round = params[:round]
|
||||
if round != "All"
|
||||
@matches = @tournament.matches.where("round = ?",round).sort_by{|match| match.bout_number}
|
||||
@matches = matches_scope
|
||||
.where(round: round)
|
||||
.includes(:weight)
|
||||
.order(:bout_number)
|
||||
else
|
||||
@matches = @tournament.matches.sort_by{|match| match.bout_number}
|
||||
@matches = matches_scope
|
||||
.includes(:weight)
|
||||
.order(:bout_number)
|
||||
end
|
||||
|
||||
wrestler_ids = @matches.flat_map { |match| [match.w1, match.w2] }.compact.uniq
|
||||
@wrestlers_by_id = Wrestler.includes(:school).where(id: wrestler_ids).index_by(&:id)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
module ApplicationHelper
|
||||
|
||||
def hide_ads?
|
||||
case controller_name
|
||||
when "schools"
|
||||
action_name == "show" && (user_signed_in? || school_permission_key_present?)
|
||||
when "wrestlers"
|
||||
%w[new edit].include?(action_name) && (user_signed_in? || school_permission_key_present?)
|
||||
when "mats"
|
||||
action_name == "show" && user_signed_in?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def school_permission_key_present?
|
||||
@school_permission_key.present? ||
|
||||
params[:school_permission_key].present? ||
|
||||
params.dig(:school, :school_permission_key).present?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
class AdvanceWrestlerJob < ApplicationJob
|
||||
queue_as :default
|
||||
# associations are not available here so we had to pass tournament_id when creating the job
|
||||
limits_concurrency to: 1, key: ->(_wrestler, _match, tournament_id) { "tournament:#{tournament_id}" }
|
||||
|
||||
def perform(wrestler, match)
|
||||
def perform(wrestler, match, tournament_id)
|
||||
# Get tournament from wrestler
|
||||
tournament = wrestler.tournament
|
||||
|
||||
@@ -29,4 +31,4 @@ class AdvanceWrestlerJob < ApplicationJob
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
class CalculateSchoolScoreJob < ApplicationJob
|
||||
queue_as :default
|
||||
limits_concurrency to: 1, key: ->(school) { "tournament:#{school.tournament_id}" }
|
||||
|
||||
# Need for TournamentJobStatusIntegrationTest
|
||||
def self.perform_sync(school)
|
||||
@@ -35,4 +36,4 @@ class CalculateSchoolScoreJob < ApplicationJob
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
class GenerateTournamentMatchesJob < ApplicationJob
|
||||
queue_as :default
|
||||
limits_concurrency to: 1, key: ->(tournament) { "tournament:#{tournament.id}" }
|
||||
|
||||
def perform(tournament)
|
||||
# Log information about the job
|
||||
@@ -17,4 +18,4 @@ class GenerateTournamentMatchesJob < ApplicationJob
|
||||
raise # Re-raise the error so it's properly recorded
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
class TournamentBackupJob < ApplicationJob
|
||||
queue_as :default
|
||||
limits_concurrency to: 1, key: ->(tournament, *) { "tournament:#{tournament.id}" }
|
||||
|
||||
def perform(tournament, reason = nil)
|
||||
# Log information about the job
|
||||
@@ -29,4 +30,4 @@ class TournamentBackupJob < ApplicationJob
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,7 +17,8 @@ class TournamentCleanupJob < ApplicationJob
|
||||
has_real_matches = tournament.matches.where(finished: 1).where.not(win_type: 'BYE').exists?
|
||||
|
||||
if has_real_matches
|
||||
|
||||
tournament.tournament_backups.destroy_all
|
||||
|
||||
# 1. Remove all school delegates
|
||||
tournament.schools.each do |school|
|
||||
school.delegates.destroy_all
|
||||
@@ -33,4 +34,4 @@ class TournamentCleanupJob < ApplicationJob
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
class WrestlingdevImportJob < ApplicationJob
|
||||
queue_as :default
|
||||
limits_concurrency to: 1, key: ->(tournament, *) { "tournament:#{tournament.id}" }
|
||||
|
||||
def perform(tournament, import_data = nil)
|
||||
# Log information about the job
|
||||
@@ -30,4 +31,4 @@ class WrestlingdevImportJob < ApplicationJob
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
class Mat < ApplicationRecord
|
||||
include ActionView::RecordIdentifier
|
||||
belongs_to :tournament
|
||||
has_many :matches, dependent: :destroy
|
||||
has_many :matches, dependent: :nullify
|
||||
has_many :mat_assignment_rules, dependent: :destroy
|
||||
|
||||
validates :name, presence: true
|
||||
|
||||
before_destroy do
|
||||
if tournament.matches.size > 0
|
||||
tournament.reset_mats
|
||||
matsToAssign = tournament.mats.select{|m| m.id != self.id}
|
||||
tournament.assign_mats(matsToAssign)
|
||||
end
|
||||
end
|
||||
QUEUE_SLOTS = %w[queue1 queue2 queue3 queue4].freeze
|
||||
|
||||
after_create do
|
||||
if tournament.matches.size > 0
|
||||
tournament.reset_mats
|
||||
matsToAssign = tournament.mats
|
||||
tournament.assign_mats(matsToAssign)
|
||||
end
|
||||
end
|
||||
after_save :clear_queue_matches_cache
|
||||
after_commit :broadcast_up_matches_board, on: :update, if: :up_matches_queue_changed?
|
||||
|
||||
def assign_next_match
|
||||
slot = first_empty_queue_slot
|
||||
return true unless slot
|
||||
|
||||
match = next_eligible_match
|
||||
self.matches.reload
|
||||
if match and self.unfinished_matches.size < 4
|
||||
match.mat_id = self.id
|
||||
if match.save
|
||||
# Invalidate any wrestler caches
|
||||
if match.w1
|
||||
match.wrestler1.touch
|
||||
match.wrestler1.school.touch
|
||||
return false unless match
|
||||
|
||||
place_match_in_empty_slot!(match, slot)
|
||||
true
|
||||
end
|
||||
|
||||
def advance_queue!(finished_match = nil)
|
||||
self.class.transaction do
|
||||
if finished_match
|
||||
position = queue_position_for_match(finished_match)
|
||||
if position == 1
|
||||
shift_queue_forward!
|
||||
fill_queue_slots!
|
||||
elsif position
|
||||
remove_match_from_queue_and_collapse!(finished_match.id)
|
||||
else
|
||||
fill_queue_slots!
|
||||
end
|
||||
if match.w2
|
||||
match.wrestler2.touch
|
||||
match.wrestler2.school.touch
|
||||
end
|
||||
return true
|
||||
else
|
||||
return false
|
||||
if queue1_match&.finished == 1
|
||||
shift_queue_forward!
|
||||
end
|
||||
fill_queue_slots!
|
||||
end
|
||||
else
|
||||
return true
|
||||
end
|
||||
broadcast_current_match
|
||||
true
|
||||
end
|
||||
|
||||
def next_eligible_match
|
||||
# Start with all matches that are either unfinished (nil or 0), have a bout number, and are ordered by bout_number
|
||||
filtered_matches = tournament.matches
|
||||
filtered_matches = Match.where(tournament_id: tournament_id)
|
||||
.where(finished: [nil, 0]) # finished is nil or 0
|
||||
.where(mat_id: nil) # mat_id is nil
|
||||
.where.not(bout_number: nil) # bout_number is not nil
|
||||
@@ -57,6 +57,11 @@ class Mat < ApplicationRecord
|
||||
filtered_matches = filtered_matches
|
||||
.where("loser1_name != ? OR loser1_name IS NULL", "BYE")
|
||||
.where("loser2_name != ? OR loser2_name IS NULL", "BYE")
|
||||
|
||||
# Filter out matches without a wrestlers
|
||||
filtered_matches = filtered_matches
|
||||
.where("w1 IS NOT NULL")
|
||||
.where("w2 IS NOT NULL")
|
||||
|
||||
# Apply mat assignment rules
|
||||
mat_assignment_rules.each do |rule|
|
||||
@@ -80,9 +85,205 @@ class Mat < ApplicationRecord
|
||||
filtered_matches.first
|
||||
end
|
||||
|
||||
def queue_match_ids
|
||||
QUEUE_SLOTS.map { |slot| public_send(slot) }
|
||||
end
|
||||
|
||||
# used to prevent N+1 query on each mat
|
||||
def queue_matches
|
||||
slot_ids = queue_match_ids
|
||||
if @queue_matches.nil? || @queue_match_slot_ids != slot_ids
|
||||
ids = slot_ids.compact
|
||||
@queue_matches = if ids.empty?
|
||||
[nil, nil, nil, nil]
|
||||
else
|
||||
matches_by_id = Match.where(id: ids)
|
||||
.includes({ wrestler1: :school }, { wrestler2: :school }, { weight: :matches })
|
||||
.index_by(&:id)
|
||||
slot_ids.map { |match_id| match_id ? matches_by_id[match_id] : nil }
|
||||
end
|
||||
@queue_match_slot_ids = slot_ids
|
||||
end
|
||||
@queue_matches
|
||||
end
|
||||
|
||||
def queue1_match
|
||||
queue_match_at(1)
|
||||
end
|
||||
|
||||
def queue2_match
|
||||
queue_match_at(2)
|
||||
end
|
||||
|
||||
def queue3_match
|
||||
queue_match_at(3)
|
||||
end
|
||||
|
||||
def queue4_match
|
||||
queue_match_at(4)
|
||||
end
|
||||
|
||||
def queue_position_for_match(match)
|
||||
return nil unless match
|
||||
return 1 if queue1 == match.id
|
||||
return 2 if queue2 == match.id
|
||||
return 3 if queue3 == match.id
|
||||
return 4 if queue4 == match.id
|
||||
nil
|
||||
end
|
||||
|
||||
def remove_match_from_queue_and_collapse!(match_id)
|
||||
queue_ids = queue_match_ids
|
||||
return if queue_ids.none? { |id| id == match_id }
|
||||
|
||||
queue_ids.map! { |id| id == match_id ? nil : id }
|
||||
queue_ids = queue_ids.compact
|
||||
queue_ids += [nil] * (4 - queue_ids.size)
|
||||
|
||||
update!(
|
||||
queue1: queue_ids[0],
|
||||
queue2: queue_ids[1],
|
||||
queue3: queue_ids[2],
|
||||
queue4: queue_ids[3]
|
||||
)
|
||||
|
||||
fill_queue_slots!
|
||||
broadcast_current_match
|
||||
end
|
||||
|
||||
def assign_match_to_queue!(match, position)
|
||||
position = position.to_i
|
||||
raise ArgumentError, "Queue position must be 1-4" unless (1..4).cover?(position)
|
||||
|
||||
self.class.transaction do
|
||||
match.update!(mat_id: id)
|
||||
remove_match_from_other_mats!(match.id)
|
||||
|
||||
queue_ids = queue_match_ids.map { |id| id == match.id ? nil : id }
|
||||
queue_ids = queue_ids.compact
|
||||
|
||||
queue_ids.insert(position - 1, match.id)
|
||||
bumped_match_id = queue_ids.length > 4 ? queue_ids.pop : nil
|
||||
|
||||
queue_ids += [nil] * (4 - queue_ids.length)
|
||||
|
||||
update!(
|
||||
queue1: queue_ids[0],
|
||||
queue2: queue_ids[1],
|
||||
queue3: queue_ids[2],
|
||||
queue4: queue_ids[3]
|
||||
)
|
||||
|
||||
bumped_match = Match.find_by(id: bumped_match_id)
|
||||
if bumped_match && bumped_match.finished != 1
|
||||
bumped_match.update!(mat_id: nil)
|
||||
end
|
||||
end
|
||||
broadcast_current_match
|
||||
end
|
||||
|
||||
def clear_queue!
|
||||
update!(queue1: nil, queue2: nil, queue3: nil, queue4: nil)
|
||||
broadcast_current_match
|
||||
end
|
||||
|
||||
def unfinished_matches
|
||||
matches.select{|m| m.finished != 1}.sort_by{|m| m.bout_number}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clear_queue_matches_cache
|
||||
@queue_matches = nil
|
||||
@queue_match_slot_ids = nil
|
||||
end
|
||||
|
||||
def queue_match_at(position)
|
||||
queue_matches[position - 1]
|
||||
end
|
||||
|
||||
def first_empty_queue_slot
|
||||
QUEUE_SLOTS.each_with_index do |slot, index|
|
||||
return index + 1 if public_send(slot).nil?
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def shift_queue_forward!
|
||||
update!(
|
||||
queue1: queue2,
|
||||
queue2: queue3,
|
||||
queue3: queue4,
|
||||
queue4: nil
|
||||
)
|
||||
end
|
||||
|
||||
def fill_queue_slots!
|
||||
queue_ids = queue_match_ids
|
||||
updated = false
|
||||
|
||||
QUEUE_SLOTS.each_with_index do |_slot, index|
|
||||
next if queue_ids[index].present?
|
||||
|
||||
match = next_eligible_match
|
||||
break unless match
|
||||
|
||||
queue_ids[index] = match.id
|
||||
match.update!(mat_id: id)
|
||||
updated = true
|
||||
end
|
||||
|
||||
if updated
|
||||
update!(
|
||||
queue1: queue_ids[0],
|
||||
queue2: queue_ids[1],
|
||||
queue3: queue_ids[2],
|
||||
queue4: queue_ids[3]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_match_from_other_mats!(match_id)
|
||||
self.class.where.not(id: id)
|
||||
.where("queue1 = :match_id OR queue2 = :match_id OR queue3 = :match_id OR queue4 = :match_id", match_id: match_id)
|
||||
.find_each do |mat|
|
||||
mat.remove_match_from_queue_and_collapse!(match_id)
|
||||
end
|
||||
end
|
||||
|
||||
def place_match_in_empty_slot!(match, slot)
|
||||
self.class.transaction do
|
||||
match.update!(mat_id: id)
|
||||
remove_match_from_other_mats!(match.id)
|
||||
update!(slot_key(slot) => match.id)
|
||||
end
|
||||
broadcast_current_match
|
||||
end
|
||||
|
||||
def slot_key(slot)
|
||||
"queue#{slot}"
|
||||
end
|
||||
|
||||
def broadcast_current_match
|
||||
Turbo::StreamsChannel.broadcast_update_to(
|
||||
self,
|
||||
target: dom_id(self, :current_match),
|
||||
partial: "mats/current_match",
|
||||
locals: {
|
||||
mat: self,
|
||||
match: queue1_match,
|
||||
next_match: queue2_match,
|
||||
show_next_bout_button: true
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def broadcast_up_matches_board
|
||||
Tournament.broadcast_up_matches_board(tournament_id)
|
||||
end
|
||||
|
||||
def up_matches_queue_changed?
|
||||
saved_change_to_queue1? || saved_change_to_queue2? || saved_change_to_queue3? || saved_change_to_queue4?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
class Match < ApplicationRecord
|
||||
include ActionView::RecordIdentifier
|
||||
|
||||
belongs_to :tournament, touch: true
|
||||
belongs_to :weight, touch: true
|
||||
belongs_to :mat, touch: true, optional: true
|
||||
belongs_to :winner, class_name: 'Wrestler', foreign_key: 'winner_id', optional: true
|
||||
belongs_to :wrestler1, class_name: 'Wrestler', foreign_key: 'w1', optional: true
|
||||
belongs_to :wrestler2, class_name: 'Wrestler', foreign_key: 'w2', optional: true
|
||||
has_many :wrestlers, :through => :weight
|
||||
has_many :schools, :through => :wrestlers
|
||||
validate :score_validation, :win_type_validation, :bracket_position_validation, :overtime_type_validation
|
||||
@@ -10,6 +14,11 @@ class Match < ApplicationRecord
|
||||
# Callback to update finished_at when a match is finished
|
||||
before_save :update_finished_at
|
||||
|
||||
# update mat show with correct match if bout board is reset
|
||||
# this is done with a turbo stream
|
||||
after_commit :broadcast_mat_assignment_change, if: :saved_change_to_mat_id?, on: [:create, :update]
|
||||
after_commit :broadcast_up_matches_board, on: :update, if: :saved_change_to_mat_id?
|
||||
|
||||
# Enqueue advancement and related actions after the DB transaction has committed.
|
||||
# Using after_commit ensures any background jobs enqueued inside these callbacks
|
||||
# will see the committed state of the match (e.g. finished == 1). Enqueuing
|
||||
@@ -31,12 +40,14 @@ class Match < ApplicationRecord
|
||||
wrestler2.touch
|
||||
end
|
||||
if self.finished == 1 && self.winner_id != nil
|
||||
if self.mat
|
||||
self.mat.assign_next_match
|
||||
end
|
||||
advance_wrestlers
|
||||
if self.mat
|
||||
self.mat.advance_queue!(self)
|
||||
end
|
||||
self.tournament.refill_open_bout_board_queues
|
||||
# School point calculation has move to the end of advance wrestler
|
||||
# calculate_school_points
|
||||
self.update(mat_id: nil)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,7 +61,7 @@ class Match < ApplicationRecord
|
||||
errors.add(:winner_id, "cannot be blank")
|
||||
end
|
||||
if win_type == "Pin" and ! score.match(/^[0-5]?[0-9]:[0-5][0-9]/)
|
||||
errors.add(:score, "needs to be in time format MM:SS when win type is Pin example: 1:23 or 10:03")
|
||||
errors.add(:score, "needs to be in time format MM:SS when win type is Pin example: 2:23, 0:25, 10:03")
|
||||
end
|
||||
if win_type == "Decision" or win_type == "Tech Fall" or win_type == "Major" and ! score.match(/^[0-9]?[0-9]-[0-9]?[0-9]/)
|
||||
errors.add(:score, "needs to be in Number-Number format when win type is Decision, Tech Fall, and Major example: 10-2")
|
||||
@@ -170,14 +181,6 @@ class Match < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def wrestler1
|
||||
wrestlers.select{|w| w.id == self.w1}.first
|
||||
end
|
||||
|
||||
def wrestler2
|
||||
wrestlers.select{|w| w.id == self.w2}.first
|
||||
end
|
||||
|
||||
def w1_name
|
||||
if self.w1 != nil
|
||||
wrestler1.name
|
||||
@@ -195,6 +198,7 @@ class Match < ApplicationRecord
|
||||
end
|
||||
|
||||
def w1_bracket_name
|
||||
first_round = first_round_for_weight
|
||||
return_string = ""
|
||||
return_string_ending = ""
|
||||
if self.w1 and self.winner_id == self.w1
|
||||
@@ -202,7 +206,7 @@ class Match < ApplicationRecord
|
||||
return_string_ending = return_string_ending + "</strong>"
|
||||
end
|
||||
if self.w1 != nil
|
||||
if self.round == 1
|
||||
if self.round == first_round
|
||||
return_string = return_string + "#{wrestler1.long_bracket_name}"
|
||||
else
|
||||
return_string = return_string + "#{wrestler1.short_bracket_name}"
|
||||
@@ -214,6 +218,7 @@ class Match < ApplicationRecord
|
||||
end
|
||||
|
||||
def w2_bracket_name
|
||||
first_round = first_round_for_weight
|
||||
return_string = ""
|
||||
return_string_ending = ""
|
||||
if self.w2 and self.winner_id == self.w2
|
||||
@@ -221,7 +226,7 @@ class Match < ApplicationRecord
|
||||
return_string_ending = return_string_ending + "</strong>"
|
||||
end
|
||||
if self.w2 != nil
|
||||
if self.round == 1
|
||||
if self.round == first_round
|
||||
return_string = return_string + "#{wrestler2.long_bracket_name}"
|
||||
else
|
||||
return_string = return_string + "#{wrestler2.short_bracket_name}"
|
||||
@@ -279,6 +284,17 @@ class Match < ApplicationRecord
|
||||
self.weight.max
|
||||
end
|
||||
|
||||
def first_round_for_weight
|
||||
return @first_round_for_weight if defined?(@first_round_for_weight)
|
||||
|
||||
@first_round_for_weight =
|
||||
if association(:weight).loaded? && self.weight&.association(:matches)&.loaded?
|
||||
self.weight.matches.map(&:round).compact.min
|
||||
else
|
||||
Match.where(weight_id: self.weight_id).minimum(:round)
|
||||
end
|
||||
end
|
||||
|
||||
def replace_loser_name_with_wrestler(w,loser_name)
|
||||
if self.loser1_name == loser_name
|
||||
self.w1 = w.id
|
||||
@@ -334,4 +350,30 @@ class Match < ApplicationRecord
|
||||
self.finished_at = Time.current.utc
|
||||
end
|
||||
end
|
||||
|
||||
def broadcast_mat_assignment_change
|
||||
old_mat_id, new_mat_id = saved_change_to_mat_id || previous_changes["mat_id"]
|
||||
return unless old_mat_id || new_mat_id
|
||||
|
||||
[old_mat_id, new_mat_id].compact.uniq.each do |mat_id|
|
||||
mat = Mat.find_by(id: mat_id)
|
||||
next unless mat
|
||||
|
||||
Turbo::StreamsChannel.broadcast_update_to(
|
||||
mat,
|
||||
target: dom_id(mat, :current_match),
|
||||
partial: "mats/current_match",
|
||||
locals: {
|
||||
mat: mat,
|
||||
match: mat.queue1_match,
|
||||
next_match: mat.queue2_match,
|
||||
show_next_bout_button: true
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def broadcast_up_matches_board
|
||||
Tournament.broadcast_up_matches_board(tournament_id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -69,8 +69,35 @@ class Tournament < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def up_matches_unassigned_matches
|
||||
matches
|
||||
.where("mat_id is NULL and (finished != 1 or finished is NULL)")
|
||||
.where("loser1_name != ? OR loser1_name IS NULL", "BYE")
|
||||
.where("loser2_name != ? OR loser2_name IS NULL", "BYE")
|
||||
.order("bout_number ASC")
|
||||
.limit(10)
|
||||
.includes({ wrestler1: :school }, { wrestler2: :school }, { weight: :matches })
|
||||
end
|
||||
|
||||
def up_matches_mats
|
||||
mats.includes(:matches)
|
||||
end
|
||||
|
||||
def self.broadcast_up_matches_board(tournament_id)
|
||||
tournament = find_by(id: tournament_id)
|
||||
return unless tournament
|
||||
|
||||
Turbo::StreamsChannel.broadcast_replace_to(
|
||||
tournament,
|
||||
target: "up_matches_board",
|
||||
partial: "tournaments/up_matches_board",
|
||||
locals: { tournament: tournament }
|
||||
)
|
||||
end
|
||||
|
||||
def destroy_all_matches
|
||||
matches.destroy_all
|
||||
mats.each(&:clear_queue!)
|
||||
end
|
||||
|
||||
def matches_by_round(round)
|
||||
@@ -82,38 +109,26 @@ class Tournament < ApplicationRecord
|
||||
matches.maximum(:round) || 0 # Return 0 if no matches or max round is nil
|
||||
end
|
||||
|
||||
def assign_mats(mats_to_assign)
|
||||
if mats_to_assign.count > 0
|
||||
until mats_to_assign.sort_by{|m| m.id}.last.matches.count == 4
|
||||
mats_to_assign.sort_by{|m| m.id}.each do |m|
|
||||
m.assign_next_match
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def reset_mats
|
||||
matches.reload
|
||||
mats.reload
|
||||
matches_to_reset = matches.select{|m| m.mat_id != nil}
|
||||
# matches_to_reset.update_all( {:mat_id => nil } )
|
||||
matches_to_reset.each do |m|
|
||||
m.mat_id = nil
|
||||
m.save
|
||||
end
|
||||
mats.each do |mat|
|
||||
mat.clear_queue!
|
||||
end
|
||||
end
|
||||
|
||||
def pointAdjustments
|
||||
point_adjustments = []
|
||||
self.schools.each do |s|
|
||||
s.deductedPoints.each do |d|
|
||||
point_adjustments << d
|
||||
end
|
||||
end
|
||||
self.wrestlers.each do |w|
|
||||
w.deductedPoints.each do |d|
|
||||
point_adjustments << d
|
||||
end
|
||||
end
|
||||
point_adjustments
|
||||
school_scope = Teampointadjust.where(school_id: schools.select(:id))
|
||||
wrestler_scope = Teampointadjust.where(wrestler_id: wrestlers.select(:id))
|
||||
|
||||
Teampointadjust.includes(:school, :wrestler)
|
||||
.merge(school_scope.or(wrestler_scope))
|
||||
end
|
||||
|
||||
def remove_school_delegations
|
||||
@@ -156,14 +171,14 @@ class Tournament < ApplicationRecord
|
||||
|
||||
def double_elim_number_of_wrestlers_error
|
||||
error_string = ""
|
||||
if self.tournament_type == "Double Elimination 1-6" or self.tournament_type == "Double Elimination 1-8"
|
||||
if self.tournament_type == "Regular Double Elimination 1-6" or self.tournament_type == "Regular Double Elimination 1-8"
|
||||
weights_with_too_many_wrestlers = weights.select{|w| w.wrestlers.size > 64}
|
||||
weight_with_too_few_wrestlers = weights.select{|w| w.wrestlers.size < 4}
|
||||
weight_with_too_few_wrestlers = weights.select{|w| w.wrestlers.size < 2}
|
||||
weights_with_too_many_wrestlers.each do |weight|
|
||||
error_string = error_string + " The weight class #{weight.max} has more than 64 wrestlers."
|
||||
end
|
||||
weight_with_too_few_wrestlers.each do |weight|
|
||||
error_string = error_string + " The weight class #{weight.max} has less than 4 wrestlers."
|
||||
error_string = error_string + " The weight class #{weight.max} has less than 2 wrestlers."
|
||||
end
|
||||
end
|
||||
return error_string
|
||||
@@ -228,19 +243,24 @@ class Tournament < ApplicationRecord
|
||||
|
||||
def reset_and_fill_bout_board
|
||||
reset_mats
|
||||
|
||||
if mats.any?
|
||||
4.times do
|
||||
# Iterate over each mat and assign the next available match
|
||||
mats.each do |mat|
|
||||
match_assigned = mat.assign_next_match
|
||||
# If no more matches are available, exit early
|
||||
unless match_assigned
|
||||
puts "No more eligible matches to assign."
|
||||
return
|
||||
end
|
||||
matches.reload
|
||||
refill_open_bout_board_queues
|
||||
end
|
||||
|
||||
def refill_open_bout_board_queues
|
||||
return unless mats.any?
|
||||
|
||||
loop do
|
||||
assigned_any = false
|
||||
# Fill in round-robin order by queue depth:
|
||||
# all mats queue1 first, then queue2, then queue3, then queue4.
|
||||
(1..4).each do |slot|
|
||||
mats.reload.each do |mat|
|
||||
next unless mat.public_send("queue#{slot}").nil?
|
||||
assigned_any ||= mat.assign_next_match
|
||||
end
|
||||
end
|
||||
end
|
||||
break unless assigned_any
|
||||
end
|
||||
end
|
||||
|
||||
@@ -279,4 +299,4 @@ class Tournament < ApplicationRecord
|
||||
def connection_adapter
|
||||
ActiveRecord::Base.connection.adapter_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -53,19 +53,16 @@ class User < ApplicationRecord
|
||||
end
|
||||
|
||||
def delegated_tournaments
|
||||
tournaments_delegated = []
|
||||
delegated_tournament_permissions.each do |t|
|
||||
tournaments_delegated << t.tournament
|
||||
end
|
||||
tournaments_delegated
|
||||
Tournament.joins(:delegates)
|
||||
.where(tournament_delegates: { user_id: id })
|
||||
.distinct
|
||||
end
|
||||
|
||||
def delegated_schools
|
||||
schools_delegated = []
|
||||
delegated_school_permissions.each do |t|
|
||||
schools_delegated << t.school
|
||||
end
|
||||
schools_delegated
|
||||
School.joins(:delegates)
|
||||
.where(school_delegates: { user_id: id })
|
||||
.includes(:tournament)
|
||||
.distinct
|
||||
end
|
||||
|
||||
def self.search(search)
|
||||
|
||||
@@ -156,7 +156,7 @@ class Weight < ApplicationRecord
|
||||
end
|
||||
|
||||
def calculate_bracket_size
|
||||
num_wrestlers = wrestlers.reload.size
|
||||
num_wrestlers = wrestlers.size
|
||||
return nil if num_wrestlers <= 0 # Handle invalid input
|
||||
|
||||
# Find the smallest power of 2 greater than or equal to num_wrestlers
|
||||
|
||||
@@ -8,25 +8,101 @@ class AdvanceWrestler
|
||||
def advance
|
||||
# Use perform_later which will execute based on centralized adapter config
|
||||
# This will be converted to inline execution in test environment by ActiveJob
|
||||
AdvanceWrestlerJob.perform_later(@wrestler, @last_match)
|
||||
AdvanceWrestlerJob.perform_later(@wrestler, @last_match, @tournament.id)
|
||||
end
|
||||
|
||||
def advance_raw
|
||||
@last_match.reload
|
||||
@wrestler.reload
|
||||
if @last_match && @last_match.finished?
|
||||
pool_to_bracket_advancement if @tournament.tournament_type == "Pool to bracket"
|
||||
ModifiedDoubleEliminationAdvance.new(@wrestler, @last_match).bracket_advancement if @tournament.tournament_type.include? "Modified 16 Man Double Elimination"
|
||||
DoubleEliminationAdvance.new(@wrestler, @last_match).bracket_advancement if @tournament.tournament_type.include? "Regular Double Elimination"
|
||||
@last_match = Match.find_by(id: @last_match&.id)
|
||||
@wrestler = Wrestler.includes(:school, :weight).find_by(id: @wrestler.id)
|
||||
return unless @last_match && @wrestler && @last_match.finished?
|
||||
|
||||
context = preload_advancement_context
|
||||
matches_to_advance = []
|
||||
|
||||
if @tournament.tournament_type == "Pool to bracket"
|
||||
matches_to_advance.concat(pool_to_bracket_advancement(context))
|
||||
elsif @tournament.tournament_type.include?("Modified 16 Man Double Elimination")
|
||||
service = ModifiedDoubleEliminationAdvance.new(@wrestler, @last_match, matches: context[:matches])
|
||||
service.bracket_advancement
|
||||
matches_to_advance.concat(service.matches_to_advance)
|
||||
elsif @tournament.tournament_type.include?("Regular Double Elimination")
|
||||
service = DoubleEliminationAdvance.new(@wrestler, @last_match, matches: context[:matches])
|
||||
service.bracket_advancement
|
||||
matches_to_advance.concat(service.matches_to_advance)
|
||||
end
|
||||
|
||||
persist_advancement_changes(context)
|
||||
advance_pending_matches(matches_to_advance)
|
||||
@wrestler.school.calculate_score
|
||||
end
|
||||
|
||||
def pool_to_bracket_advancement
|
||||
if @wrestler.weight.all_pool_matches_finished(@wrestler.pool) and (@wrestler.finished_bracket_matches.size < 1)
|
||||
PoolOrder.new(@wrestler.weight.wrestlers_in_pool(@wrestler.pool)).getPoolOrder
|
||||
end
|
||||
PoolAdvance.new(@wrestler).advanceWrestler
|
||||
def preload_advancement_context
|
||||
weight = Weight.includes(:matches, :wrestlers).find(@wrestler.weight_id)
|
||||
{
|
||||
weight: weight,
|
||||
matches: weight.matches.to_a,
|
||||
wrestlers: weight.wrestlers.to_a
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
def persist_advancement_changes(context)
|
||||
persist_matches(context[:matches])
|
||||
persist_wrestlers(context[:wrestlers])
|
||||
end
|
||||
|
||||
def persist_matches(matches)
|
||||
timestamp = Time.current
|
||||
updates = matches.filter_map do |m|
|
||||
next unless m.changed?
|
||||
|
||||
{
|
||||
id: m.id,
|
||||
w1: m.w1,
|
||||
w2: m.w2,
|
||||
winner_id: m.winner_id,
|
||||
win_type: m.win_type,
|
||||
score: m.score,
|
||||
finished: m.finished,
|
||||
loser1_name: m.loser1_name,
|
||||
loser2_name: m.loser2_name,
|
||||
finished_at: m.finished_at,
|
||||
updated_at: timestamp
|
||||
}
|
||||
end
|
||||
Match.upsert_all(updates) if updates.any?
|
||||
end
|
||||
|
||||
def persist_wrestlers(wrestlers)
|
||||
timestamp = Time.current
|
||||
updates = wrestlers.filter_map do |w|
|
||||
next unless w.changed?
|
||||
|
||||
{
|
||||
id: w.id,
|
||||
pool_placement: w.pool_placement,
|
||||
pool_placement_tiebreaker: w.pool_placement_tiebreaker,
|
||||
updated_at: timestamp
|
||||
}
|
||||
end
|
||||
Wrestler.upsert_all(updates) if updates.any?
|
||||
end
|
||||
|
||||
def advance_pending_matches(matches_to_advance)
|
||||
matches_to_advance.uniq(&:id).each do |match|
|
||||
match.advance_wrestlers
|
||||
end
|
||||
end
|
||||
|
||||
def pool_to_bracket_advancement(context)
|
||||
matches_to_advance = []
|
||||
wrestlers_in_pool = context[:wrestlers].select { |w| w.pool == @wrestler.pool }
|
||||
if @wrestler.weight.all_pool_matches_finished(@wrestler.pool) && (@wrestler.finished_bracket_matches.size < 1)
|
||||
PoolOrder.new(wrestlers_in_pool).getPoolOrder
|
||||
end
|
||||
service = PoolAdvance.new(@wrestler, @last_match, matches: context[:matches], wrestlers: context[:wrestlers])
|
||||
service.advanceWrestler
|
||||
matches_to_advance.concat(service.matches_to_advance)
|
||||
matches_to_advance
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
class DoubleEliminationAdvance
|
||||
|
||||
def initialize(wrestler,last_match)
|
||||
attr_reader :matches_to_advance
|
||||
|
||||
def initialize(wrestler,last_match, matches: nil)
|
||||
@wrestler = wrestler
|
||||
@last_match = last_match
|
||||
@matches = matches || @wrestler.weight.matches.to_a
|
||||
@matches_to_advance = []
|
||||
@next_match_position_number = (@last_match.bracket_position_number / 2.0)
|
||||
end
|
||||
|
||||
@@ -48,7 +52,7 @@ class DoubleEliminationAdvance
|
||||
end
|
||||
|
||||
if next_match_bracket_position
|
||||
next_match = Match.where("bracket_position = ? AND bracket_position_number = ? AND weight_id = ?",next_match_bracket_position,next_match_position_number.ceil,@wrestler.weight_id).first
|
||||
next_match = @matches.find { |m| m.bracket_position == next_match_bracket_position && m.bracket_position_number == next_match_position_number.ceil && m.weight_id == @wrestler.weight_id }
|
||||
end
|
||||
|
||||
if next_match
|
||||
@@ -59,18 +63,16 @@ class DoubleEliminationAdvance
|
||||
def update_new_match(match, wrestler_number)
|
||||
if wrestler_number == 2 or (match.loser1_name and match.loser1_name.include? "Loser of")
|
||||
match.w2 = @wrestler.id
|
||||
match.save
|
||||
elsif wrestler_number == 1
|
||||
match.w1 = @wrestler.id
|
||||
match.save
|
||||
end
|
||||
end
|
||||
|
||||
def update_consolation_bye
|
||||
bout = @wrestler.last_match.bout_number
|
||||
next_match = Match.where("(loser1_name = ? OR loser2_name = ?) AND weight_id = ?","Loser of #{bout}","Loser of #{bout}",@wrestler.weight_id)
|
||||
if next_match.size > 0
|
||||
next_match.first.replace_loser_name_with_bye("Loser of #{bout}")
|
||||
next_match = @matches.find { |m| m.weight_id == @wrestler.weight_id && (m.loser1_name == "Loser of #{bout}" || m.loser2_name == "Loser of #{bout}") }
|
||||
if next_match
|
||||
replace_loser_name_with_bye(next_match, "Loser of #{bout}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -84,27 +86,18 @@ class DoubleEliminationAdvance
|
||||
|
||||
def losers_bracket_advancement
|
||||
bout = @last_match.bout_number
|
||||
next_match = Match.where("(loser1_name = ? OR loser2_name = ?) AND weight_id = ?", "Loser of #{bout}", "Loser of #{bout}", @wrestler.weight_id).first
|
||||
next_match = @matches.find { |m| m.weight_id == @wrestler.weight_id && (m.loser1_name == "Loser of #{bout}" || m.loser2_name == "Loser of #{bout}") }
|
||||
|
||||
if next_match.present?
|
||||
next_match.replace_loser_name_with_wrestler(@wrestler, "Loser of #{bout}")
|
||||
next_match.reload
|
||||
replace_loser_name_with_wrestler(next_match, @wrestler, "Loser of #{bout}")
|
||||
|
||||
if next_match.loser1_name == "BYE" || next_match.loser2_name == "BYE"
|
||||
next_match.winner_id = @wrestler.id
|
||||
next_match.win_type = "BYE"
|
||||
next_match.score = ""
|
||||
next_match.finished = 1
|
||||
# puts "Before save: winner_id=#{next_match.winner_id}"
|
||||
|
||||
# if next_match.save
|
||||
# puts "Save successful: winner_id=#{next_match.reload.winner_id}"
|
||||
# else
|
||||
# puts "Save failed: #{next_match.errors.full_messages}"
|
||||
# end
|
||||
next_match.save
|
||||
|
||||
next_match.advance_wrestlers
|
||||
next_match.finished_at = Time.current
|
||||
@matches_to_advance << next_match
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -112,51 +105,69 @@ class DoubleEliminationAdvance
|
||||
|
||||
def advance_double_byes
|
||||
weight = @wrestler.weight
|
||||
weight.matches.select{|m| m.loser1_name == "BYE" and m.loser2_name == "BYE" and m.finished != 1}.each do |match|
|
||||
@matches.select{|m| m.weight_id == weight.id && m.loser1_name == "BYE" and m.loser2_name == "BYE" and m.finished != 1}.each do |match|
|
||||
match.finished = 1
|
||||
match.finished_at = Time.current
|
||||
match.score = ""
|
||||
match.win_type = "BYE"
|
||||
next_match_position_number = (match.bracket_position_number / 2.0).ceil
|
||||
after_matches = weight.matches.select{|m| m.round > match.round and m.is_consolation_match == match.is_consolation_match }.sort_by{|m| m.round}
|
||||
next_matches = weight.matches.select{|m| m.round == after_matches.first.round and m.is_consolation_match == match.is_consolation_match }
|
||||
this_round_matches = weight.matches.select{|m| m.round == match.round and m.is_consolation_match == match.is_consolation_match }
|
||||
after_matches = @matches.select{|m| m.weight_id == weight.id && m.round > match.round and m.is_consolation_match == match.is_consolation_match }.sort_by{|m| m.round}
|
||||
next if after_matches.empty?
|
||||
next_matches = @matches.select{|m| m.weight_id == weight.id && m.round == after_matches.first.round and m.is_consolation_match == match.is_consolation_match }
|
||||
this_round_matches = @matches.select{|m| m.weight_id == weight.id && m.round == match.round and m.is_consolation_match == match.is_consolation_match }
|
||||
next_match = nil
|
||||
|
||||
if next_matches.size == this_round_matches.size
|
||||
next_match = next_matches.select{|m| m.bracket_position_number == match.bracket_position_number}.first
|
||||
next_match.loser2_name = "BYE"
|
||||
next_match.save
|
||||
next_match.loser2_name = "BYE" if next_match
|
||||
elsif next_matches.size < this_round_matches.size and next_matches.size > 0
|
||||
next_match = next_matches.select{|m| m.bracket_position_number == next_match_position_number}.first
|
||||
if next_match.bracket_position_number == next_match_position_number
|
||||
if next_match && next_match.bracket_position_number == next_match_position_number
|
||||
next_match.loser2_name = "BYE"
|
||||
else
|
||||
elsif next_match
|
||||
next_match.loser1_name = "BYE"
|
||||
end
|
||||
end
|
||||
next_match.save
|
||||
match.save
|
||||
end
|
||||
end
|
||||
|
||||
def set_bye_for_placement
|
||||
weight = @wrestler.weight
|
||||
fifth_finals = weight.matches.select{|match| match.bracket_position == '5/6'}.first
|
||||
seventh_finals = weight.matches.select{|match| match.bracket_position == '7/8'}.first
|
||||
fifth_finals = @matches.select{|match| match.weight_id == weight.id && match.bracket_position == '5/6'}.first
|
||||
seventh_finals = @matches.select{|match| match.weight_id == weight.id && match.bracket_position == '7/8'}.first
|
||||
if seventh_finals
|
||||
conso_quarter = weight.matches.select{|match| match.bracket_position == 'Conso Quarter'}
|
||||
conso_quarter = @matches.select{|match| match.weight_id == weight.id && match.bracket_position == 'Conso Quarter'}
|
||||
conso_quarter.each do |match|
|
||||
if match.loser1_name == "BYE" or match.loser2_name == "BYE"
|
||||
seventh_finals.replace_loser_name_with_bye("Loser of #{match.bout_number}")
|
||||
replace_loser_name_with_bye(seventh_finals, "Loser of #{match.bout_number}")
|
||||
end
|
||||
end
|
||||
end
|
||||
if fifth_finals
|
||||
conso_semis = weight.matches.select{|match| match.bracket_position == 'Conso Semis'}
|
||||
conso_semis = @matches.select{|match| match.weight_id == weight.id && match.bracket_position == 'Conso Semis'}
|
||||
conso_semis.each do |match|
|
||||
if match.loser1_name == "BYE" or match.loser2_name == "BYE"
|
||||
fifth_finals.replace_loser_name_with_bye("Loser of #{match.bout_number}")
|
||||
replace_loser_name_with_bye(fifth_finals, "Loser of #{match.bout_number}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def replace_loser_name_with_wrestler(match, wrestler, loser_name)
|
||||
if match.loser1_name == loser_name
|
||||
match.w1 = wrestler.id
|
||||
end
|
||||
if match.loser2_name == loser_name
|
||||
match.w2 = wrestler.id
|
||||
end
|
||||
end
|
||||
|
||||
def replace_loser_name_with_bye(match, loser_name)
|
||||
if match.loser1_name == loser_name
|
||||
match.loser1_name = "BYE"
|
||||
end
|
||||
if match.loser2_name == loser_name
|
||||
match.loser2_name = "BYE"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
class ModifiedDoubleEliminationAdvance
|
||||
|
||||
def initialize(wrestler,last_match)
|
||||
attr_reader :matches_to_advance
|
||||
|
||||
def initialize(wrestler,last_match, matches: nil)
|
||||
@wrestler = wrestler
|
||||
@last_match = last_match
|
||||
@matches = matches || @wrestler.weight.matches.to_a
|
||||
@matches_to_advance = []
|
||||
@next_match_position_number = (@last_match.bracket_position_number / 2.0)
|
||||
end
|
||||
|
||||
@@ -25,42 +29,41 @@ class ModifiedDoubleEliminationAdvance
|
||||
update_consolation_bye
|
||||
end
|
||||
if @last_match.bracket_position == "Quarter"
|
||||
new_match = Match.where("bracket_position = ? AND bracket_position_number = ? AND weight_id = ?","Semis",@next_match_position_number.ceil,@wrestler.weight_id).first
|
||||
new_match = @matches.find { |m| m.bracket_position == "Semis" && m.bracket_position_number == @next_match_position_number.ceil && m.weight_id == @wrestler.weight_id }
|
||||
update_new_match(new_match, get_wrestler_number)
|
||||
elsif @last_match.bracket_position == "Semis"
|
||||
new_match = Match.where("bracket_position = ? AND bracket_position_number = ? AND weight_id = ?","1/2",@next_match_position_number.ceil,@wrestler.weight_id).first
|
||||
new_match = @matches.find { |m| m.bracket_position == "1/2" && m.bracket_position_number == @next_match_position_number.ceil && m.weight_id == @wrestler.weight_id }
|
||||
update_new_match(new_match, get_wrestler_number)
|
||||
elsif @last_match.bracket_position == "Conso Semis"
|
||||
new_match = Match.where("bracket_position = ? AND bracket_position_number = ? AND weight_id = ?","5/6",@next_match_position_number.ceil,@wrestler.weight_id).first
|
||||
new_match = @matches.find { |m| m.bracket_position == "5/6" && m.bracket_position_number == @next_match_position_number.ceil && m.weight_id == @wrestler.weight_id }
|
||||
update_new_match(new_match, get_wrestler_number)
|
||||
elsif @last_match.bracket_position == "Conso Quarter"
|
||||
# it's a special bracket where a semi loser is not dropping down
|
||||
new_match = Match.where("bracket_position = ? AND bracket_position_number = ? AND weight_id = ?","Conso Semis",@next_match_position_number.ceil,@wrestler.weight_id).first
|
||||
new_match = @matches.find { |m| m.bracket_position == "Conso Semis" && m.bracket_position_number == @next_match_position_number.ceil && m.weight_id == @wrestler.weight_id }
|
||||
update_new_match(new_match, get_wrestler_number)
|
||||
elsif @last_match.bracket_position == "Bracket Round of 16"
|
||||
new_match = Match.where("bracket_position_number = ? and weight_id = ? and round > ? and bracket_position = ?", @next_match_position_number.ceil,@wrestler.weight_id, @last_match.round , "Quarter").sort_by{|m| m.round}.first
|
||||
new_match = @matches.select { |m| m.bracket_position_number == @next_match_position_number.ceil && m.weight_id == @wrestler.weight_id && m.round > @last_match.round && m.bracket_position == "Quarter" }.sort_by(&:round).first
|
||||
update_new_match(new_match, get_wrestler_number)
|
||||
elsif @last_match.bracket_position == "Conso Round of 8"
|
||||
new_match = Match.where("bracket_position_number = ? and weight_id = ? and round > ? and bracket_position = ?", @last_match.bracket_position_number,@wrestler.weight_id, @last_match.round, "Conso Quarter").sort_by{|m| m.round}.first
|
||||
new_match = @matches.select { |m| m.bracket_position_number == @last_match.bracket_position_number && m.weight_id == @wrestler.weight_id && m.round > @last_match.round && m.bracket_position == "Conso Quarter" }.sort_by(&:round).first
|
||||
update_new_match(new_match, get_wrestler_number)
|
||||
end
|
||||
end
|
||||
|
||||
def update_new_match(match, wrestler_number)
|
||||
return unless match
|
||||
if wrestler_number == 2 or (match.loser1_name and match.loser1_name.include? "Loser of")
|
||||
match.w2 = @wrestler.id
|
||||
match.save
|
||||
elsif wrestler_number == 1
|
||||
match.w1 = @wrestler.id
|
||||
match.save
|
||||
end
|
||||
end
|
||||
|
||||
def update_consolation_bye
|
||||
bout = @wrestler.last_match.bout_number
|
||||
next_match = Match.where("(loser1_name = ? OR loser2_name = ?) AND weight_id = ?","Loser of #{bout}","Loser of #{bout}",@wrestler.weight_id)
|
||||
if next_match.size > 0
|
||||
next_match.first.replace_loser_name_with_bye("Loser of #{bout}")
|
||||
next_match = @matches.find { |m| m.weight_id == @wrestler.weight_id && (m.loser1_name == "Loser of #{bout}" || m.loser2_name == "Loser of #{bout}") }
|
||||
if next_match
|
||||
replace_loser_name_with_bye(next_match, "Loser of #{bout}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -74,27 +77,18 @@ class ModifiedDoubleEliminationAdvance
|
||||
|
||||
def losers_bracket_advancement
|
||||
bout = @last_match.bout_number
|
||||
next_match = Match.where("(loser1_name = ? OR loser2_name = ?) AND weight_id = ?", "Loser of #{bout}", "Loser of #{bout}", @wrestler.weight_id).first
|
||||
next_match = @matches.find { |m| m.weight_id == @wrestler.weight_id && (m.loser1_name == "Loser of #{bout}" || m.loser2_name == "Loser of #{bout}") }
|
||||
|
||||
if next_match.present?
|
||||
next_match.replace_loser_name_with_wrestler(@wrestler, "Loser of #{bout}")
|
||||
next_match.reload
|
||||
replace_loser_name_with_wrestler(next_match, @wrestler, "Loser of #{bout}")
|
||||
|
||||
if next_match.loser1_name == "BYE" || next_match.loser2_name == "BYE"
|
||||
next_match.winner_id = @wrestler.id
|
||||
next_match.win_type = "BYE"
|
||||
next_match.score = ""
|
||||
next_match.finished = 1
|
||||
# puts "Before save: winner_id=#{next_match.winner_id}"
|
||||
|
||||
# if next_match.save
|
||||
# puts "Save successful: winner_id=#{next_match.reload.winner_id}"
|
||||
# else
|
||||
# puts "Save failed: #{next_match.errors.full_messages}"
|
||||
# end
|
||||
next_match.save
|
||||
|
||||
next_match.advance_wrestlers
|
||||
next_match.finished_at = Time.current
|
||||
@matches_to_advance << next_match
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -102,43 +96,53 @@ class ModifiedDoubleEliminationAdvance
|
||||
|
||||
def advance_double_byes
|
||||
weight = @wrestler.weight
|
||||
weight.matches.select{|m| m.loser1_name == "BYE" and m.loser2_name == "BYE" and m.finished != 1}.each do |match|
|
||||
@matches.select{|m| m.weight_id == weight.id && m.loser1_name == "BYE" and m.loser2_name == "BYE" and m.finished != 1}.each do |match|
|
||||
match.finished = 1
|
||||
match.finished_at = Time.current
|
||||
match.score = ""
|
||||
match.win_type = "BYE"
|
||||
next_match_position_number = (match.bracket_position_number / 2.0).ceil
|
||||
after_matches = weight.matches.select{|m| m.round > match.round and m.is_consolation_match == match.is_consolation_match }.sort_by{|m| m.round}
|
||||
next_matches = weight.matches.select{|m| m.round == after_matches.first.round and m.is_consolation_match == match.is_consolation_match }
|
||||
this_round_matches = weight.matches.select{|m| m.round == match.round and m.is_consolation_match == match.is_consolation_match }
|
||||
after_matches = @matches.select{|m| m.weight_id == weight.id && m.round > match.round and m.is_consolation_match == match.is_consolation_match }.sort_by{|m| m.round}
|
||||
next if after_matches.empty?
|
||||
next_matches = @matches.select{|m| m.weight_id == weight.id && m.round == after_matches.first.round and m.is_consolation_match == match.is_consolation_match }
|
||||
this_round_matches = @matches.select{|m| m.weight_id == weight.id && m.round == match.round and m.is_consolation_match == match.is_consolation_match }
|
||||
next_match = nil
|
||||
|
||||
if next_matches.size == this_round_matches.size
|
||||
next_match = next_matches.select{|m| m.bracket_position_number == match.bracket_position_number}.first
|
||||
next_match.loser2_name = "BYE"
|
||||
next_match.save
|
||||
next_match.loser2_name = "BYE" if next_match
|
||||
elsif next_matches.size < this_round_matches.size and next_matches.size > 0
|
||||
next_match = next_matches.select{|m| m.bracket_position_number == next_match_position_number}.first
|
||||
if next_match.bracket_position_number == next_match_position_number
|
||||
if next_match && next_match.bracket_position_number == next_match_position_number
|
||||
next_match.loser2_name = "BYE"
|
||||
else
|
||||
elsif next_match
|
||||
next_match.loser1_name = "BYE"
|
||||
end
|
||||
end
|
||||
next_match.save
|
||||
match.save
|
||||
end
|
||||
end
|
||||
|
||||
def set_bye_for_placement
|
||||
weight = @wrestler.weight
|
||||
seventh_finals = weight.matches.select{|match| match.bracket_position == '7/8'}.first
|
||||
seventh_finals = @matches.select{|match| match.weight_id == weight.id && match.bracket_position == '7/8'}.first
|
||||
if seventh_finals
|
||||
conso_quarter = weight.matches.select{|match| match.bracket_position == 'Conso Semis'}
|
||||
conso_quarter = @matches.select{|match| match.weight_id == weight.id && match.bracket_position == 'Conso Semis'}
|
||||
conso_quarter.each do |match|
|
||||
if match.loser1_name == "BYE" or match.loser2_name == "BYE"
|
||||
seventh_finals.replace_loser_name_with_bye("Loser of #{match.bout_number}")
|
||||
replace_loser_name_with_bye(seventh_finals, "Loser of #{match.bout_number}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def replace_loser_name_with_wrestler(match, wrestler, loser_name)
|
||||
match.w1 = wrestler.id if match.loser1_name == loser_name
|
||||
match.w2 = wrestler.id if match.loser2_name == loser_name
|
||||
end
|
||||
|
||||
def replace_loser_name_with_bye(match, loser_name)
|
||||
match.loser1_name = "BYE" if match.loser1_name == loser_name
|
||||
match.loser2_name = "BYE" if match.loser2_name == loser_name
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
class PoolAdvance
|
||||
|
||||
def initialize(wrestler)
|
||||
attr_reader :matches_to_advance
|
||||
|
||||
def initialize(wrestler, last_match, matches: nil, wrestlers: nil)
|
||||
@wrestler = wrestler
|
||||
@last_match = @wrestler.last_match
|
||||
@last_match = last_match
|
||||
@matches = matches || @wrestler.weight.matches.to_a
|
||||
@wrestlers = wrestlers || @wrestler.weight.wrestlers.to_a
|
||||
@matches_to_advance = []
|
||||
end
|
||||
|
||||
def advanceWrestler
|
||||
@@ -17,15 +22,15 @@ class PoolAdvance
|
||||
def poolToBracketAdvancment
|
||||
pool = @wrestler.pool
|
||||
# This has to always run because the last match in a pool might not be a pool winner or runner up
|
||||
winner = Wrestler.where("weight_id = ? and pool_placement = 1 and pool = ?",@wrestler.weight.id, pool).first
|
||||
runner_up = Wrestler.where("weight_id = ? and pool_placement = 2 and pool = ?",@wrestler.weight.id, pool).first
|
||||
winner = @wrestlers.find { |w| w.weight_id == @wrestler.weight.id && w.pool_placement == 1 && w.pool == pool }
|
||||
runner_up = @wrestlers.find { |w| w.weight_id == @wrestler.weight.id && w.pool_placement == 2 && w.pool == pool }
|
||||
if runner_up
|
||||
runner_up_match = Match.where("weight_id = ? and (loser1_name = ? or loser2_name = ?)",@wrestler.weight.id, "Runner Up Pool #{pool}", "Runner Up Pool #{pool}").first
|
||||
runner_up_match.replace_loser_name_with_wrestler(runner_up,"Runner Up Pool #{pool}")
|
||||
runner_up_match = @matches.find { |m| m.weight_id == @wrestler.weight.id && (m.loser1_name == "Runner Up Pool #{pool}" || m.loser2_name == "Runner Up Pool #{pool}") }
|
||||
replace_loser_name_with_wrestler(runner_up_match, runner_up, "Runner Up Pool #{pool}") if runner_up_match
|
||||
end
|
||||
if winner
|
||||
winner_match = Match.where("weight_id = ? and (loser1_name = ? or loser2_name = ?)",@wrestler.weight.id, "Winner Pool #{pool}", "Winner Pool #{pool}").first
|
||||
winner_match.replace_loser_name_with_wrestler(winner,"Winner Pool #{pool}")
|
||||
winner_match = @matches.find { |m| m.weight_id == @wrestler.weight.id && (m.loser1_name == "Winner Pool #{pool}" || m.loser2_name == "Winner Pool #{pool}") }
|
||||
replace_loser_name_with_wrestler(winner_match, winner, "Winner Pool #{pool}") if winner_match
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,36 +50,40 @@ class PoolAdvance
|
||||
|
||||
def winner_advance
|
||||
if @wrestler.last_match.bracket_position == "Quarter"
|
||||
new_match = Match.where("bracket_position = ? AND bracket_position_number = ? AND weight_id = ?","Semis",@wrestler.next_match_position_number.ceil,@wrestler.weight_id).first
|
||||
new_match = @matches.find { |m| m.bracket_position == "Semis" && m.bracket_position_number == @wrestler.next_match_position_number.ceil && m.weight_id == @wrestler.weight_id }
|
||||
updateNewMatch(new_match)
|
||||
end
|
||||
if @wrestler.last_match.bracket_position == "Semis"
|
||||
new_match = Match.where("bracket_position = ? AND bracket_position_number = ? AND weight_id = ?","1/2",@wrestler.next_match_position_number.ceil,@wrestler.weight_id).first
|
||||
new_match = @matches.find { |m| m.bracket_position == "1/2" && m.bracket_position_number == @wrestler.next_match_position_number.ceil && m.weight_id == @wrestler.weight_id }
|
||||
updateNewMatch(new_match)
|
||||
end
|
||||
if @wrestler.last_match.bracket_position == "Conso Semis"
|
||||
new_match = Match.where("bracket_position = ? AND bracket_position_number = ? AND weight_id = ?","5/6",@wrestler.next_match_position_number.ceil,@wrestler.weight_id).first
|
||||
new_match = @matches.find { |m| m.bracket_position == "5/6" && m.bracket_position_number == @wrestler.next_match_position_number.ceil && m.weight_id == @wrestler.weight_id }
|
||||
updateNewMatch(new_match)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def updateNewMatch(match)
|
||||
return unless match
|
||||
if @wrestler.next_match_position_number == @wrestler.next_match_position_number.ceil
|
||||
match.w2 = @wrestler.id
|
||||
match.save
|
||||
end
|
||||
if @wrestler.next_match_position_number != @wrestler.next_match_position_number.ceil
|
||||
match.w1 = @wrestler.id
|
||||
match.save
|
||||
end
|
||||
end
|
||||
|
||||
def loser_advance
|
||||
bout = @wrestler.last_match.bout_number
|
||||
next_match = Match.where("(loser1_name = ? OR loser2_name = ?) AND weight_id = ?","Loser of #{bout}","Loser of #{bout}",@wrestler.weight_id)
|
||||
if next_match.size > 0
|
||||
next_match.first.replace_loser_name_with_wrestler(@wrestler,"Loser of #{bout}")
|
||||
next_match = @matches.find { |m| m.weight_id == @wrestler.weight_id && (m.loser1_name == "Loser of #{bout}" || m.loser2_name == "Loser of #{bout}") }
|
||||
if next_match
|
||||
replace_loser_name_with_wrestler(next_match, @wrestler, "Loser of #{bout}")
|
||||
end
|
||||
end
|
||||
|
||||
def replace_loser_name_with_wrestler(match, wrestler, loser_name)
|
||||
match.w1 = wrestler.id if match.loser1_name == loser_name
|
||||
match.w2 = wrestler.id if match.loser2_name == loser_name
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,8 +4,6 @@ class PoolOrder
|
||||
end
|
||||
|
||||
def getPoolOrder
|
||||
# clear caching for weight for bracket page
|
||||
@wrestlers.first.weight.touch
|
||||
setOriginalPoints
|
||||
while checkForTies(@wrestlers) == true
|
||||
getWrestlersOrderByPoolAdvancePoints.each do |wrestler|
|
||||
@@ -18,7 +16,6 @@ class PoolOrder
|
||||
getWrestlersOrderByPoolAdvancePoints.each_with_index do |wrestler, index|
|
||||
placement = index + 1
|
||||
wrestler.pool_placement = placement
|
||||
wrestler.save
|
||||
end
|
||||
@wrestlers.sort_by{|w| w.poolAdvancePoints}.reverse!
|
||||
end
|
||||
@@ -29,7 +26,6 @@ class PoolOrder
|
||||
|
||||
def setOriginalPoints
|
||||
@wrestlers.each do |w|
|
||||
matches = w.reload.all_matches
|
||||
w.pool_placement_tiebreaker = nil
|
||||
w.pool_placement = nil
|
||||
w.poolAdvancePoints = w.pool_wins.size
|
||||
|
||||
55
app/services/school_services/calculate_school_score.rb
Normal file
55
app/services/school_services/calculate_school_score.rb
Normal file
@@ -0,0 +1,55 @@
|
||||
class CalculateSchoolScore
|
||||
def initialize(school)
|
||||
@school = school
|
||||
end
|
||||
|
||||
def calculate
|
||||
school = preload_school_context
|
||||
score = calculate_score_value(school)
|
||||
persist_school_score(school.id, score)
|
||||
score
|
||||
end
|
||||
|
||||
def calculate_value
|
||||
school = preload_school_context
|
||||
calculate_score_value(school)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def preload_school_context
|
||||
School
|
||||
.includes(
|
||||
:deductedPoints,
|
||||
wrestlers: [
|
||||
:deductedPoints,
|
||||
{ matches_as_w1: :winner },
|
||||
{ matches_as_w2: :winner },
|
||||
{ weight: [:matches, { tournament: { weights: :wrestlers } }] }
|
||||
]
|
||||
)
|
||||
.find(@school.id)
|
||||
end
|
||||
|
||||
def calculate_score_value(school)
|
||||
total_points_scored_by_wrestlers(school) - total_points_deducted(school)
|
||||
end
|
||||
|
||||
def total_points_scored_by_wrestlers(school)
|
||||
school.wrestlers.sum { |wrestler| CalculateWrestlerTeamScore.new(wrestler).totalScore }
|
||||
end
|
||||
|
||||
def total_points_deducted(school)
|
||||
school.deductedPoints.sum(&:points)
|
||||
end
|
||||
|
||||
def persist_school_score(school_id, score)
|
||||
School.upsert_all([
|
||||
{
|
||||
id: school_id,
|
||||
score: score,
|
||||
updated_at: Time.current
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
@@ -3,30 +3,30 @@ class DoubleEliminationGenerateLoserNames
|
||||
@tournament = tournament
|
||||
end
|
||||
|
||||
# Entry point: assign loser placeholders and advance any byes
|
||||
def assign_loser_names
|
||||
# Compatibility wrapper. Returns transformed rows and does not persist.
|
||||
def assign_loser_names(match_rows = nil)
|
||||
rows = match_rows || @tournament.matches.where(tournament_id: @tournament.id).map { |m| m.attributes.symbolize_keys }
|
||||
@tournament.weights.each do |weight|
|
||||
assign_loser_names_for_weight(weight)
|
||||
advance_bye_matches_championship(weight)
|
||||
advance_bye_matches_consolation(weight)
|
||||
next unless weight.calculate_bracket_size > 2
|
||||
|
||||
assign_loser_names_in_memory(weight, rows)
|
||||
assign_bye_outcomes_in_memory(weight, rows)
|
||||
end
|
||||
rows
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Assign loser names for a single weight bracket
|
||||
def assign_loser_names_for_weight(weight)
|
||||
def assign_loser_names_in_memory(weight, match_rows)
|
||||
bracket_size = weight.calculate_bracket_size
|
||||
matches = weight.matches.reload
|
||||
num_placers = @tournament.number_of_placers
|
||||
return if bracket_size <= 2
|
||||
|
||||
rows = match_rows.select { |row| row[:weight_id] == weight.id }
|
||||
num_placers = @tournament.number_of_placers
|
||||
|
||||
# Build dynamic round definitions
|
||||
champ_rounds = dynamic_championship_rounds(bracket_size)
|
||||
conso_rounds = dynamic_consolation_rounds(bracket_size)
|
||||
first_round = { bracket_position: first_round_label(bracket_size) }
|
||||
champ_full = [first_round] + champ_rounds
|
||||
first_round = { bracket_position: first_round_label(bracket_size) }
|
||||
champ_full = [first_round] + champ_rounds
|
||||
|
||||
# Map championship losers into consolation slots
|
||||
mappings = []
|
||||
champ_full[0...-1].each_with_index do |champ_info, i|
|
||||
map_idx = i.zero? ? 0 : (2 * i - 1)
|
||||
@@ -34,128 +34,109 @@ class DoubleEliminationGenerateLoserNames
|
||||
|
||||
mappings << {
|
||||
championship_bracket_position: champ_info[:bracket_position],
|
||||
consolation_bracket_position: conso_rounds[map_idx][:bracket_position],
|
||||
both_wrestlers: i.zero?,
|
||||
champ_round_index: i
|
||||
consolation_bracket_position: conso_rounds[map_idx][:bracket_position],
|
||||
both_wrestlers: i.zero?,
|
||||
champ_round_index: i
|
||||
}
|
||||
end
|
||||
|
||||
# Apply loser-name mappings
|
||||
mappings.each do |map|
|
||||
champ = matches.select { |m| m.bracket_position == map[:championship_bracket_position] }
|
||||
.sort_by(&:bracket_position_number)
|
||||
conso = matches.select { |m| m.bracket_position == map[:consolation_bracket_position] }
|
||||
.sort_by(&:bracket_position_number)
|
||||
|
||||
current_champ_round_index = map[:champ_round_index]
|
||||
if current_champ_round_index.odd?
|
||||
conso.reverse!
|
||||
end
|
||||
champ = rows.select { |r| r[:bracket_position] == map[:championship_bracket_position] }
|
||||
.sort_by { |r| r[:bracket_position_number] }
|
||||
conso = rows.select { |r| r[:bracket_position] == map[:consolation_bracket_position] }
|
||||
.sort_by { |r| r[:bracket_position_number] }
|
||||
conso.reverse! if map[:champ_round_index].odd?
|
||||
|
||||
idx = 0
|
||||
# Determine if this mapping is for losers from the first championship round
|
||||
is_first_champ_round_feed = map[:champ_round_index].zero?
|
||||
|
||||
is_first_feed = map[:champ_round_index].zero?
|
||||
conso.each do |cm|
|
||||
champ_match1 = champ[idx]
|
||||
if champ_match1
|
||||
if is_first_champ_round_feed && ((champ_match1.w1 && champ_match1.w2.nil?) || (champ_match1.w1.nil? && champ_match1.w2))
|
||||
cm.loser1_name = "BYE"
|
||||
if is_first_feed && single_competitor_match_row?(champ_match1)
|
||||
cm[:loser1_name] = "BYE"
|
||||
else
|
||||
cm.loser1_name = "Loser of #{champ_match1.bout_number}"
|
||||
cm[:loser1_name] = "Loser of #{champ_match1[:bout_number]}"
|
||||
end
|
||||
else
|
||||
cm.loser1_name = nil # Should not happen if bracket generation is correct
|
||||
cm[:loser1_name] = nil
|
||||
end
|
||||
|
||||
if map[:both_wrestlers] # This is true only if is_first_champ_round_feed
|
||||
idx += 1 # Increment for the second championship match
|
||||
if map[:both_wrestlers]
|
||||
idx += 1
|
||||
champ_match2 = champ[idx]
|
||||
if champ_match2
|
||||
# BYE check is only relevant for the first championship round feed
|
||||
if is_first_champ_round_feed && ((champ_match2.w1 && champ_match2.w2.nil?) || (champ_match2.w1.nil? && champ_match2.w2))
|
||||
cm.loser2_name = "BYE"
|
||||
if is_first_feed && single_competitor_match_row?(champ_match2)
|
||||
cm[:loser2_name] = "BYE"
|
||||
else
|
||||
cm.loser2_name = "Loser of #{champ_match2.bout_number}"
|
||||
cm[:loser2_name] = "Loser of #{champ_match2[:bout_number]}"
|
||||
end
|
||||
else
|
||||
cm.loser2_name = nil # Should not happen
|
||||
cm[:loser2_name] = nil
|
||||
end
|
||||
end
|
||||
idx += 1 # Increment for the next consolation match or next pair from championship
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
|
||||
# 5th/6th place
|
||||
if bracket_size >= 5 && num_placers >= 6
|
||||
conso_semis = matches.select { |m| m.bracket_position == "Conso Semis" }
|
||||
.sort_by(&:bracket_position_number)
|
||||
if conso_semis.size >= 2
|
||||
m56 = matches.find { |m| m.bracket_position == "5/6" }
|
||||
m56.loser1_name = "Loser of #{conso_semis[0].bout_number}"
|
||||
m56.loser2_name = "Loser of #{conso_semis[1].bout_number}" if m56
|
||||
if bracket_size >= 5 && num_placers >= 6 && weight.wrestlers.size > 4
|
||||
conso_semis = rows.select { |r| r[:bracket_position] == "Conso Semis" }.sort_by { |r| r[:bracket_position_number] }
|
||||
m56 = rows.find { |r| r[:bracket_position] == "5/6" }
|
||||
if conso_semis.size >= 2 && m56
|
||||
m56[:loser1_name] = "Loser of #{conso_semis[0][:bout_number]}"
|
||||
m56[:loser2_name] = "Loser of #{conso_semis[1][:bout_number]}"
|
||||
end
|
||||
end
|
||||
|
||||
# 7th/8th place
|
||||
if bracket_size >= 7 && num_placers >= 8
|
||||
conso_quarters = matches.select { |m| m.bracket_position == "Conso Quarter" }
|
||||
.sort_by(&:bracket_position_number)
|
||||
if conso_quarters.size >= 2
|
||||
m78 = matches.find { |m| m.bracket_position == "7/8" }
|
||||
m78.loser1_name = "Loser of #{conso_quarters[0].bout_number}"
|
||||
m78.loser2_name = "Loser of #{conso_quarters[1].bout_number}" if m78
|
||||
if bracket_size >= 7 && num_placers >= 8 && weight.wrestlers.size > 6
|
||||
conso_quarters = rows.select { |r| r[:bracket_position] == "Conso Quarter" }.sort_by { |r| r[:bracket_position_number] }
|
||||
m78 = rows.find { |r| r[:bracket_position] == "7/8" }
|
||||
if conso_quarters.size >= 2 && m78
|
||||
m78[:loser1_name] = "Loser of #{conso_quarters[0][:bout_number]}"
|
||||
m78[:loser2_name] = "Loser of #{conso_quarters[1][:bout_number]}"
|
||||
end
|
||||
end
|
||||
|
||||
matches.each(&:save!)
|
||||
end
|
||||
|
||||
# Advance first-round byes in championship bracket
|
||||
def advance_bye_matches_championship(weight)
|
||||
matches = weight.matches.reload
|
||||
first_round = matches.map(&:round).min
|
||||
matches.select { |m| m.round == first_round }
|
||||
.sort_by(&:bracket_position_number)
|
||||
.each { |m| handle_bye(m) }
|
||||
end
|
||||
|
||||
# Advance first-round byes in consolation bracket
|
||||
def advance_bye_matches_consolation(weight)
|
||||
matches = weight.matches.reload
|
||||
def assign_bye_outcomes_in_memory(weight, match_rows)
|
||||
bracket_size = weight.calculate_bracket_size
|
||||
first_conso = dynamic_consolation_rounds(bracket_size).first
|
||||
return if bracket_size <= 2
|
||||
|
||||
matches.select { |m| m.round == first_conso[:round] && m.bracket_position == first_conso[:bracket_position] }
|
||||
.sort_by(&:bracket_position_number)
|
||||
.each { |m| handle_bye(m) }
|
||||
end
|
||||
rows = match_rows.select { |r| r[:weight_id] == weight.id }
|
||||
first_round = rows.map { |r| r[:round] }.compact.min
|
||||
rows.select { |r| r[:round] == first_round }.each { |row| apply_bye_to_row(row) }
|
||||
|
||||
# Mark bye match, set finished, and advance
|
||||
def handle_bye(match)
|
||||
if [match.w1, match.w2].compact.size == 1
|
||||
match.finished = 1
|
||||
match.win_type = 'BYE'
|
||||
if match.w1
|
||||
match.winner_id = match.w1
|
||||
match.loser2_name = 'BYE'
|
||||
else
|
||||
match.winner_id = match.w2
|
||||
match.loser1_name = 'BYE'
|
||||
end
|
||||
match.score = ''
|
||||
match.save!
|
||||
match.advance_wrestlers
|
||||
first_conso = dynamic_consolation_rounds(bracket_size).first
|
||||
if first_conso
|
||||
rows.select { |r| r[:round] == first_conso[:round] && r[:bracket_position] == first_conso[:bracket_position] }
|
||||
.each { |row| apply_bye_to_row(row) }
|
||||
end
|
||||
end
|
||||
|
||||
# Helpers for dynamic bracket labels
|
||||
def apply_bye_to_row(row)
|
||||
return unless single_competitor_match_row?(row)
|
||||
|
||||
row[:finished] = 1
|
||||
row[:win_type] = "BYE"
|
||||
if row[:w1]
|
||||
row[:winner_id] = row[:w1]
|
||||
row[:loser2_name] = "BYE"
|
||||
else
|
||||
row[:winner_id] = row[:w2]
|
||||
row[:loser1_name] = "BYE"
|
||||
end
|
||||
row[:score] = ""
|
||||
end
|
||||
|
||||
def single_competitor_match_row?(row)
|
||||
[row[:w1], row[:w2]].compact.size == 1
|
||||
end
|
||||
|
||||
def first_round_label(size)
|
||||
case size
|
||||
when 2 then 'Final'
|
||||
when 4 then 'Semis'
|
||||
when 8 then 'Quarter'
|
||||
else "Bracket Round of #{size}"
|
||||
when 2 then "Final"
|
||||
when 4 then "Semis"
|
||||
when 8 then "Quarter"
|
||||
else "Bracket Round of #{size}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -170,36 +151,36 @@ class DoubleEliminationGenerateLoserNames
|
||||
def dynamic_consolation_rounds(size)
|
||||
total_log2 = Math.log2(size).to_i
|
||||
return [] if total_log2 <= 1
|
||||
|
||||
|
||||
max_j_val = (2 * (total_log2 - 1) - 1)
|
||||
(1..max_j_val).map do |j|
|
||||
current_participants = size / (2**((j.to_f / 2).ceil))
|
||||
{
|
||||
{
|
||||
bracket_position: consolation_label(current_participants, j, size),
|
||||
round: j
|
||||
round: j
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def bracket_label(participants)
|
||||
case participants
|
||||
when 2 then '1/2'
|
||||
when 4 then 'Semis'
|
||||
when 8 then 'Quarter'
|
||||
else "Bracket Round of #{participants}"
|
||||
when 2 then "1/2"
|
||||
when 4 then "Semis"
|
||||
when 8 then "Quarter"
|
||||
else "Bracket Round of #{participants}"
|
||||
end
|
||||
end
|
||||
|
||||
def consolation_label(participants, j, bracket_size)
|
||||
max_j_for_bracket = (2 * (Math.log2(bracket_size).to_i - 1) - 1)
|
||||
|
||||
|
||||
if participants == 2 && j == max_j_for_bracket
|
||||
return '3/4'
|
||||
"3/4"
|
||||
elsif participants == 4
|
||||
return j.odd? ? 'Conso Quarter' : 'Conso Semis'
|
||||
j.odd? ? "Conso Quarter" : "Conso Semis"
|
||||
else
|
||||
suffix = j.odd? ? ".1" : ".2"
|
||||
return "Conso Round of #{participants}#{suffix}"
|
||||
"Conso Round of #{participants}#{suffix}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
class DoubleEliminationMatchGeneration
|
||||
def initialize(tournament)
|
||||
def initialize(tournament, weights: nil)
|
||||
@tournament = tournament
|
||||
@weights = weights
|
||||
end
|
||||
|
||||
def generate_matches
|
||||
#
|
||||
# PHASE 1: Generate matches (with local round definitions).
|
||||
#
|
||||
@tournament.weights.each do |weight|
|
||||
generate_matches_for_weight(weight)
|
||||
build_match_rows
|
||||
end
|
||||
|
||||
def build_match_rows
|
||||
rows_by_weight_id = {}
|
||||
|
||||
generation_weights.each do |weight|
|
||||
rows_by_weight_id[weight.id] = generate_match_rows_for_weight(weight)
|
||||
end
|
||||
|
||||
#
|
||||
# PHASE 2: Align all rounds to match the largest bracket’s definitions.
|
||||
#
|
||||
align_all_rounds_to_largest_bracket
|
||||
align_rows_to_largest_bracket(rows_by_weight_id)
|
||||
rows_by_weight_id.values.flatten
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# PHASE 1: Generate all matches for each bracket, using a single definition.
|
||||
###########################################################################
|
||||
def generate_matches_for_weight(weight)
|
||||
def generate_match_rows_for_weight(weight)
|
||||
bracket_size = weight.calculate_bracket_size
|
||||
bracket_info = define_bracket_matches(bracket_size)
|
||||
return unless bracket_info
|
||||
return [] unless bracket_info
|
||||
|
||||
rows = []
|
||||
|
||||
# 1) Round one matchups
|
||||
bracket_info[:round_one_matchups].each_with_index do |matchup, idx|
|
||||
@@ -32,7 +36,7 @@ class DoubleEliminationMatchGeneration
|
||||
bracket_pos_number = idx + 1
|
||||
round_number = matchup[:round]
|
||||
|
||||
create_matchup_from_seed(
|
||||
rows << create_matchup_from_seed(
|
||||
seed1,
|
||||
seed2,
|
||||
bracket_position,
|
||||
@@ -49,7 +53,7 @@ class DoubleEliminationMatchGeneration
|
||||
round_number = round_info[:round]
|
||||
|
||||
matches_this_round.times do |i|
|
||||
create_matchup(
|
||||
rows << create_matchup(
|
||||
nil,
|
||||
nil,
|
||||
bracket_position,
|
||||
@@ -67,7 +71,7 @@ class DoubleEliminationMatchGeneration
|
||||
round_number = round_info[:round]
|
||||
|
||||
matches_this_round.times do |i|
|
||||
create_matchup(
|
||||
rows << create_matchup(
|
||||
nil,
|
||||
nil,
|
||||
bracket_position,
|
||||
@@ -79,12 +83,14 @@ class DoubleEliminationMatchGeneration
|
||||
|
||||
# 5/6, 7/8 placing logic
|
||||
if weight.wrestlers.size >= 5 && @tournament.number_of_placers >= 6 && matches_this_round == 1
|
||||
create_matchup(nil, nil, "5/6", 1, round_number, weight)
|
||||
rows << create_matchup(nil, nil, "5/6", 1, round_number, weight)
|
||||
end
|
||||
if weight.wrestlers.size >= 7 && @tournament.number_of_placers >= 8 && matches_this_round == 1
|
||||
create_matchup(nil, nil, "7/8", 1, round_number, weight)
|
||||
rows << create_matchup(nil, nil, "7/8", 1, round_number, weight)
|
||||
end
|
||||
end
|
||||
|
||||
rows
|
||||
end
|
||||
|
||||
# Single bracket definition dynamically generated for any power-of-two bracket size.
|
||||
@@ -173,18 +179,18 @@ class DoubleEliminationMatchGeneration
|
||||
###########################################################################
|
||||
# PHASE 2: Overwrite rounds in all smaller brackets to match the largest one.
|
||||
###########################################################################
|
||||
def align_all_rounds_to_largest_bracket
|
||||
largest_weight = @tournament.weights.max_by { |w| w.calculate_bracket_size }
|
||||
def align_rows_to_largest_bracket(rows_by_weight_id)
|
||||
largest_weight = generation_weights.max_by { |w| w.calculate_bracket_size }
|
||||
return unless largest_weight
|
||||
|
||||
position_to_round = {}
|
||||
largest_weight.tournament.matches.where(weight_id: largest_weight.id).each do |m|
|
||||
position_to_round[m.bracket_position] ||= m.round
|
||||
rows_by_weight_id.fetch(largest_weight.id, []).each do |row|
|
||||
position_to_round[row[:bracket_position]] ||= row[:round]
|
||||
end
|
||||
|
||||
@tournament.matches.find_each do |match|
|
||||
if position_to_round.key?(match.bracket_position)
|
||||
match.update(round: position_to_round[match.bracket_position])
|
||||
rows_by_weight_id.each_value do |rows|
|
||||
rows.each do |row|
|
||||
row[:round] = position_to_round[row[:bracket_position]] if position_to_round.key?(row[:bracket_position])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -192,8 +198,12 @@ class DoubleEliminationMatchGeneration
|
||||
###########################################################################
|
||||
# Helper methods
|
||||
###########################################################################
|
||||
def generation_weights
|
||||
@weights || @tournament.weights.to_a
|
||||
end
|
||||
|
||||
def wrestler_with_seed(seed, weight)
|
||||
Wrestler.where("weight_id = ? AND bracket_line = ?", weight.id, seed).first&.id
|
||||
weight.wrestlers.find { |w| w.bracket_line == seed }&.id
|
||||
end
|
||||
|
||||
def create_matchup_from_seed(w1_seed, w2_seed, bracket_position, bracket_position_number, round, weight)
|
||||
@@ -208,14 +218,15 @@ class DoubleEliminationMatchGeneration
|
||||
end
|
||||
|
||||
def create_matchup(w1, w2, bracket_position, bracket_position_number, round, weight)
|
||||
weight.tournament.matches.create!(
|
||||
{
|
||||
w1: w1,
|
||||
w2: w2,
|
||||
tournament_id: weight.tournament_id,
|
||||
weight_id: weight.id,
|
||||
round: round,
|
||||
bracket_position: bracket_position,
|
||||
bracket_position_number: bracket_position_number
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
# Calculates the sequence of seeds for the first round of a power-of-two bracket.
|
||||
|
||||
@@ -10,62 +10,183 @@ class GenerateTournamentMatches
|
||||
|
||||
def generate_raw
|
||||
standardStartingActions
|
||||
PoolToBracketMatchGeneration.new(@tournament).generatePoolToBracketMatches if @tournament.tournament_type == "Pool to bracket"
|
||||
ModifiedSixteenManMatchGeneration.new(@tournament).generate_matches if @tournament.tournament_type.include? "Modified 16 Man Double Elimination"
|
||||
DoubleEliminationMatchGeneration.new(@tournament).generate_matches if @tournament.tournament_type.include? "Regular Double Elimination"
|
||||
generation_context = preload_generation_context
|
||||
seed_wrestlers_in_memory(generation_context)
|
||||
match_rows = build_match_rows(generation_context)
|
||||
post_process_match_rows_in_memory(generation_context, match_rows)
|
||||
persist_generation_rows(generation_context, match_rows)
|
||||
postMatchCreationActions
|
||||
PoolToBracketMatchGeneration.new(@tournament).assignLoserNames if @tournament.tournament_type == "Pool to bracket"
|
||||
ModifiedSixteenManGenerateLoserNames.new(@tournament).assign_loser_names if @tournament.tournament_type.include? "Modified 16 Man Double Elimination"
|
||||
DoubleEliminationGenerateLoserNames.new(@tournament).assign_loser_names if @tournament.tournament_type.include? "Regular Double Elimination"
|
||||
advance_bye_matches_after_insert
|
||||
end
|
||||
|
||||
def standardStartingActions
|
||||
@tournament.curently_generating_matches = 1
|
||||
@tournament.save
|
||||
WipeTournamentMatches.new(@tournament).setUpMatchGeneration
|
||||
TournamentSeeding.new(@tournament).set_seeds
|
||||
end
|
||||
|
||||
def preload_generation_context
|
||||
weights = @tournament.weights.includes(:wrestlers).order(:max).to_a
|
||||
wrestlers = weights.flat_map(&:wrestlers)
|
||||
{
|
||||
weights: weights,
|
||||
wrestlers: wrestlers,
|
||||
wrestlers_by_weight_id: wrestlers.group_by(&:weight_id)
|
||||
}
|
||||
end
|
||||
|
||||
def seed_wrestlers_in_memory(generation_context)
|
||||
TournamentSeeding.new(@tournament).set_seeds(weights: generation_context[:weights], persist: false)
|
||||
end
|
||||
|
||||
def build_match_rows(generation_context)
|
||||
return PoolToBracketMatchGeneration.new(
|
||||
@tournament,
|
||||
weights: generation_context[:weights],
|
||||
wrestlers_by_weight_id: generation_context[:wrestlers_by_weight_id]
|
||||
).generatePoolToBracketMatches if @tournament.tournament_type == "Pool to bracket"
|
||||
|
||||
return ModifiedSixteenManMatchGeneration.new(@tournament, weights: generation_context[:weights]).generate_matches if @tournament.tournament_type.include? "Modified 16 Man Double Elimination"
|
||||
|
||||
return DoubleEliminationMatchGeneration.new(@tournament, weights: generation_context[:weights]).generate_matches if @tournament.tournament_type.include? "Regular Double Elimination"
|
||||
|
||||
[]
|
||||
end
|
||||
|
||||
def persist_generation_rows(generation_context, match_rows)
|
||||
persist_wrestlers(generation_context[:wrestlers])
|
||||
persist_matches(match_rows)
|
||||
end
|
||||
|
||||
def post_process_match_rows_in_memory(generation_context, match_rows)
|
||||
move_finals_rows_to_last_round(match_rows) unless @tournament.tournament_type.include?("Regular Double Elimination")
|
||||
assign_bouts_in_memory(match_rows, generation_context[:weights])
|
||||
assign_loser_names_in_memory(generation_context, match_rows)
|
||||
assign_bye_outcomes_in_memory(generation_context, match_rows)
|
||||
end
|
||||
|
||||
def persist_wrestlers(wrestlers)
|
||||
return if wrestlers.blank?
|
||||
|
||||
timestamp = Time.current
|
||||
rows = wrestlers.map do |w|
|
||||
{
|
||||
id: w.id,
|
||||
bracket_line: w.bracket_line,
|
||||
pool: w.pool,
|
||||
updated_at: timestamp
|
||||
}
|
||||
end
|
||||
Wrestler.upsert_all(rows)
|
||||
end
|
||||
|
||||
def persist_matches(match_rows)
|
||||
return if match_rows.blank?
|
||||
|
||||
timestamp = Time.current
|
||||
rows_with_timestamps = match_rows.map do |row|
|
||||
row.to_h.symbolize_keys.merge(created_at: timestamp, updated_at: timestamp)
|
||||
end
|
||||
|
||||
all_keys = rows_with_timestamps.flat_map(&:keys).uniq
|
||||
normalized_rows = rows_with_timestamps.map do |row|
|
||||
all_keys.each_with_object({}) { |key, normalized| normalized[key] = row[key] }
|
||||
end
|
||||
|
||||
Match.insert_all!(normalized_rows)
|
||||
end
|
||||
|
||||
def postMatchCreationActions
|
||||
moveFinalsMatchesToLastRound if ! @tournament.tournament_type.include? "Regular Double Elimination"
|
||||
assignBouts
|
||||
@tournament.reset_and_fill_bout_board
|
||||
@tournament.curently_generating_matches = nil
|
||||
@tournament.save!
|
||||
Tournament.broadcast_up_matches_board(@tournament.id)
|
||||
end
|
||||
|
||||
def move_finals_rows_to_last_round(match_rows)
|
||||
finals_round = match_rows.map { |row| row[:round] }.compact.max
|
||||
return unless finals_round
|
||||
|
||||
match_rows.each do |row|
|
||||
row[:round] = finals_round if ["1/2", "3/4", "5/6", "7/8"].include?(row[:bracket_position])
|
||||
end
|
||||
end
|
||||
|
||||
def assign_bouts_in_memory(match_rows, weights)
|
||||
bout_counts = Hash.new(0)
|
||||
weight_max_by_id = weights.each_with_object({}) { |w, memo| memo[w.id] = w.max }
|
||||
|
||||
match_rows
|
||||
.sort_by { |row| [row[:round].to_i, weight_max_by_id[row[:weight_id]].to_f, row[:weight_id].to_i, row[:bracket_position_number].to_i] }
|
||||
.each do |row|
|
||||
round = row[:round].to_i
|
||||
row[:bout_number] = round * 1000 + bout_counts[round]
|
||||
bout_counts[round] += 1
|
||||
end
|
||||
end
|
||||
|
||||
def assign_loser_names_in_memory(generation_context, match_rows)
|
||||
if @tournament.tournament_type == "Pool to bracket"
|
||||
service = PoolToBracketGenerateLoserNames.new(@tournament)
|
||||
generation_context[:weights].each { |weight| service.assign_loser_names_in_memory(weight, match_rows) }
|
||||
elsif @tournament.tournament_type.include?("Modified 16 Man Double Elimination")
|
||||
service = ModifiedSixteenManGenerateLoserNames.new(@tournament)
|
||||
generation_context[:weights].each { |weight| service.assign_loser_names_in_memory(weight, match_rows) }
|
||||
elsif @tournament.tournament_type.include?("Regular Double Elimination")
|
||||
service = DoubleEliminationGenerateLoserNames.new(@tournament)
|
||||
generation_context[:weights].each { |weight| service.assign_loser_names_in_memory(weight, match_rows) }
|
||||
end
|
||||
end
|
||||
|
||||
def assign_bye_outcomes_in_memory(generation_context, match_rows)
|
||||
if @tournament.tournament_type.include?("Modified 16 Man Double Elimination")
|
||||
service = ModifiedSixteenManGenerateLoserNames.new(@tournament)
|
||||
generation_context[:weights].each { |weight| service.assign_bye_outcomes_in_memory(weight, match_rows) }
|
||||
elsif @tournament.tournament_type.include?("Regular Double Elimination")
|
||||
service = DoubleEliminationGenerateLoserNames.new(@tournament)
|
||||
generation_context[:weights].each { |weight| service.assign_bye_outcomes_in_memory(weight, match_rows) }
|
||||
end
|
||||
end
|
||||
|
||||
def advance_bye_matches_after_insert
|
||||
Match.where(tournament_id: @tournament.id, finished: 1, win_type: "BYE")
|
||||
.where.not(winner_id: nil)
|
||||
.find_each(&:advance_wrestlers)
|
||||
end
|
||||
|
||||
def assignBouts
|
||||
bout_counts = Hash.new(0)
|
||||
@tournament.matches.reload
|
||||
@tournament.matches.sort_by{|m| [m.round, m.weight_max]}.each do |m|
|
||||
m.bout_number = m.round * 1000 + bout_counts[m.round]
|
||||
bout_counts[m.round] += 1
|
||||
m.save!
|
||||
timestamp = Time.current
|
||||
ordered_matches = Match.joins(:weight)
|
||||
.where(tournament_id: @tournament.id)
|
||||
.order("matches.round ASC, weights.max ASC, matches.id ASC")
|
||||
.pluck("matches.id", "matches.round")
|
||||
|
||||
updates = []
|
||||
ordered_matches.each do |match_id, round|
|
||||
updates << {
|
||||
id: match_id,
|
||||
bout_number: round * 1000 + bout_counts[round],
|
||||
updated_at: timestamp
|
||||
}
|
||||
bout_counts[round] += 1
|
||||
end
|
||||
|
||||
Match.upsert_all(updates) if updates.any?
|
||||
end
|
||||
|
||||
def moveFinalsMatchesToLastRound
|
||||
finalsRound = @tournament.reload.total_rounds
|
||||
finalsMatches = @tournament.matches.reload.select{|m| m.bracket_position == "1/2" || m.bracket_position == "3/4" || m.bracket_position == "5/6" || m.bracket_position == "7/8"}
|
||||
finalsMatches. each do |m|
|
||||
m.round = finalsRound
|
||||
m.save
|
||||
end
|
||||
@tournament.matches
|
||||
.where(bracket_position: ["1/2", "3/4", "5/6", "7/8"])
|
||||
.update_all(round: finalsRound, updated_at: Time.current)
|
||||
end
|
||||
|
||||
def unAssignMats
|
||||
matches = @tournament.matches.reload
|
||||
matches.each do |m|
|
||||
m.mat_id = nil
|
||||
m.save!
|
||||
end
|
||||
@tournament.matches.update_all(mat_id: nil, updated_at: Time.current)
|
||||
end
|
||||
|
||||
def unAssignBouts
|
||||
bout_counts = Hash.new(0)
|
||||
@tournament.matches.each do |m|
|
||||
m.bout_number = nil
|
||||
m.save!
|
||||
end
|
||||
@tournament.matches.update_all(bout_number: nil, updated_at: Time.current)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,95 +1,91 @@
|
||||
class ModifiedSixteenManGenerateLoserNames
|
||||
def initialize( tournament )
|
||||
@tournament = tournament
|
||||
def initialize(tournament)
|
||||
@tournament = tournament
|
||||
end
|
||||
|
||||
# Compatibility wrapper. Returns transformed rows and does not persist.
|
||||
def assign_loser_names(match_rows = nil)
|
||||
rows = match_rows || @tournament.matches.where(tournament_id: @tournament.id).map { |m| m.attributes.symbolize_keys }
|
||||
@tournament.weights.each do |weight|
|
||||
assign_loser_names_in_memory(weight, rows)
|
||||
assign_bye_outcomes_in_memory(weight, rows)
|
||||
end
|
||||
rows
|
||||
end
|
||||
|
||||
def assign_loser_names_in_memory(weight, match_rows)
|
||||
rows = match_rows.select { |row| row[:weight_id] == weight.id }
|
||||
round_16 = rows.select { |r| r[:bracket_position] == "Bracket Round of 16" }
|
||||
conso_8 = rows.select { |r| r[:bracket_position] == "Conso Round of 8" }.sort_by { |r| r[:bracket_position_number] }
|
||||
|
||||
conso_8.each do |row|
|
||||
if row[:bracket_position_number] == 1
|
||||
m1 = round_16.find { |m| m[:bracket_position_number] == 1 }
|
||||
m2 = round_16.find { |m| m[:bracket_position_number] == 2 }
|
||||
row[:loser1_name] = "Loser of #{m1[:bout_number]}" if m1
|
||||
row[:loser2_name] = "Loser of #{m2[:bout_number]}" if m2
|
||||
elsif row[:bracket_position_number] == 2
|
||||
m3 = round_16.find { |m| m[:bracket_position_number] == 3 }
|
||||
m4 = round_16.find { |m| m[:bracket_position_number] == 4 }
|
||||
row[:loser1_name] = "Loser of #{m3[:bout_number]}" if m3
|
||||
row[:loser2_name] = "Loser of #{m4[:bout_number]}" if m4
|
||||
elsif row[:bracket_position_number] == 3
|
||||
m5 = round_16.find { |m| m[:bracket_position_number] == 5 }
|
||||
m6 = round_16.find { |m| m[:bracket_position_number] == 6 }
|
||||
row[:loser1_name] = "Loser of #{m5[:bout_number]}" if m5
|
||||
row[:loser2_name] = "Loser of #{m6[:bout_number]}" if m6
|
||||
elsif row[:bracket_position_number] == 4
|
||||
m7 = round_16.find { |m| m[:bracket_position_number] == 7 }
|
||||
m8 = round_16.find { |m| m[:bracket_position_number] == 8 }
|
||||
row[:loser1_name] = "Loser of #{m7[:bout_number]}" if m7
|
||||
row[:loser2_name] = "Loser of #{m8[:bout_number]}" if m8
|
||||
end
|
||||
end
|
||||
|
||||
def assign_loser_names
|
||||
matches_by_weight = nil
|
||||
@tournament.weights.each do |w|
|
||||
matches_by_weight = @tournament.matches.where(weight_id: w.id)
|
||||
conso_round_2(matches_by_weight)
|
||||
conso_round_3(matches_by_weight)
|
||||
third_fourth(matches_by_weight)
|
||||
seventh_eighth(matches_by_weight)
|
||||
save_matches(matches_by_weight)
|
||||
matches_by_weight = @tournament.matches.where(weight_id: w.id).reload
|
||||
advance_bye_matches_championship(matches_by_weight)
|
||||
save_matches(matches_by_weight)
|
||||
quarters = rows.select { |r| r[:bracket_position] == "Quarter" }
|
||||
conso_quarters = rows.select { |r| r[:bracket_position] == "Conso Quarter" }.sort_by { |r| r[:bracket_position_number] }
|
||||
conso_quarters.each do |row|
|
||||
source = case row[:bracket_position_number]
|
||||
when 1 then quarters.find { |q| q[:bracket_position_number] == 4 }
|
||||
when 2 then quarters.find { |q| q[:bracket_position_number] == 3 }
|
||||
when 3 then quarters.find { |q| q[:bracket_position_number] == 2 }
|
||||
when 4 then quarters.find { |q| q[:bracket_position_number] == 1 }
|
||||
end
|
||||
row[:loser1_name] = "Loser of #{source[:bout_number]}" if source
|
||||
end
|
||||
end
|
||||
|
||||
def conso_round_2(matches)
|
||||
matches.select{|m| m.bracket_position == "Conso Round of 8"}.sort_by{|m| m.bracket_position_number}.each do |match|
|
||||
if match.bracket_position_number == 1
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position_number == 1 and m.bracket_position == "Bracket Round of 16"}.first.bout_number}"
|
||||
match.loser2_name = "Loser of #{matches.select{|m| m.bracket_position_number == 2 and m.bracket_position == "Bracket Round of 16"}.first.bout_number}"
|
||||
elsif match.bracket_position_number == 2
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position_number == 3 and m.bracket_position == "Bracket Round of 16"}.first.bout_number}"
|
||||
match.loser2_name = "Loser of #{matches.select{|m| m.bracket_position_number == 4 and m.bracket_position == "Bracket Round of 16"}.first.bout_number}"
|
||||
elsif match.bracket_position_number == 3
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position_number == 5 and m.bracket_position == "Bracket Round of 16"}.first.bout_number}"
|
||||
match.loser2_name = "Loser of #{matches.select{|m| m.bracket_position_number == 6 and m.bracket_position == "Bracket Round of 16"}.first.bout_number}"
|
||||
elsif match.bracket_position_number == 4
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position_number == 7 and m.bracket_position == "Bracket Round of 16"}.first.bout_number}"
|
||||
match.loser2_name = "Loser of #{matches.select{|m| m.bracket_position_number == 8 and m.bracket_position == "Bracket Round of 16"}.first.bout_number}"
|
||||
end
|
||||
end
|
||||
end
|
||||
semis = rows.select { |r| r[:bracket_position] == "Semis" }
|
||||
third_fourth = rows.find { |r| r[:bracket_position] == "3/4" }
|
||||
if third_fourth
|
||||
third_fourth[:loser1_name] = "Loser of #{semis.first[:bout_number]}" if semis.first
|
||||
third_fourth[:loser2_name] = "Loser of #{semis.second[:bout_number]}" if semis.second
|
||||
end
|
||||
|
||||
def conso_round_3(matches)
|
||||
matches.select{|m| m.bracket_position == "Conso Quarter"}.sort_by{|m| m.bracket_position_number}.each do |match|
|
||||
if match.bracket_position_number == 1
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position_number == 4 and m.bracket_position == "Quarter"}.first.bout_number}"
|
||||
elsif match.bracket_position_number == 2
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position_number == 3 and m.bracket_position == "Quarter"}.first.bout_number}"
|
||||
elsif match.bracket_position_number == 3
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position_number == 2 and m.bracket_position == "Quarter"}.first.bout_number}"
|
||||
elsif match.bracket_position_number == 4
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position_number == 1 and m.bracket_position == "Quarter"}.first.bout_number}"
|
||||
end
|
||||
end
|
||||
end
|
||||
conso_semis = rows.select { |r| r[:bracket_position] == "Conso Semis" }
|
||||
seventh_eighth = rows.find { |r| r[:bracket_position] == "7/8" }
|
||||
if seventh_eighth
|
||||
seventh_eighth[:loser1_name] = "Loser of #{conso_semis.first[:bout_number]}" if conso_semis.first
|
||||
seventh_eighth[:loser2_name] = "Loser of #{conso_semis.second[:bout_number]}" if conso_semis.second
|
||||
end
|
||||
end
|
||||
|
||||
def third_fourth(matches)
|
||||
matches.select{|m| m.bracket_position == "3/4"}.sort_by{|m| m.bracket_position_number}.each do |match|
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position == "Semis"}.first.bout_number}"
|
||||
match.loser2_name = "Loser of #{matches.select{|m| m.bracket_position == "Semis"}.second.bout_number}"
|
||||
end
|
||||
end
|
||||
|
||||
def seventh_eighth(matches)
|
||||
matches.select{|m| m.bracket_position == "7/8"}.sort_by{|m| m.bracket_position_number}.each do |match|
|
||||
match.loser1_name = "Loser of #{matches.select{|m| m.bracket_position == "Conso Semis"}.first.bout_number}"
|
||||
match.loser2_name = "Loser of #{matches.select{|m| m.bracket_position == "Conso Semis"}.second.bout_number}"
|
||||
end
|
||||
end
|
||||
def assign_bye_outcomes_in_memory(weight, match_rows)
|
||||
rows = match_rows.select { |r| r[:weight_id] == weight.id && r[:bracket_position] == "Bracket Round of 16" }
|
||||
rows.each { |row| apply_bye_to_row(row) }
|
||||
end
|
||||
|
||||
def advance_bye_matches_championship(matches)
|
||||
matches.select{|m| m.bracket_position == "Bracket Round of 16"}.sort_by{|m| m.bracket_position_number}.each do |match|
|
||||
if match.w1 == nil or match.w2 == nil
|
||||
match.finished = 1
|
||||
match.win_type = "BYE"
|
||||
if match.w1 != nil
|
||||
match.winner_id = match.w1
|
||||
match.loser2_name = "BYE"
|
||||
match.score = ""
|
||||
match.save
|
||||
match.advance_wrestlers
|
||||
elsif match.w2 != nil
|
||||
match.winner_id = match.w2
|
||||
match.loser1_name = "BYE"
|
||||
match.score = ""
|
||||
match.save
|
||||
match.advance_wrestlers
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def save_matches(matches)
|
||||
matches.each do |m|
|
||||
m.save!
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
def apply_bye_to_row(row)
|
||||
return unless [row[:w1], row[:w2]].compact.size == 1
|
||||
|
||||
row[:finished] = 1
|
||||
row[:win_type] = "BYE"
|
||||
if row[:w1]
|
||||
row[:winner_id] = row[:w1]
|
||||
row[:loser2_name] = "BYE"
|
||||
else
|
||||
row[:winner_id] = row[:w2]
|
||||
row[:loser1_name] = "BYE"
|
||||
end
|
||||
row[:score] = ""
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,70 +1,75 @@
|
||||
class ModifiedSixteenManMatchGeneration
|
||||
def initialize( tournament )
|
||||
def initialize( tournament, weights: nil )
|
||||
@tournament = tournament
|
||||
@number_of_placers = @tournament.number_of_placers
|
||||
@weights = weights
|
||||
end
|
||||
|
||||
def generate_matches
|
||||
@tournament.weights.each do |weight|
|
||||
generate_matches_for_weight(weight)
|
||||
rows = []
|
||||
generation_weights.each do |weight|
|
||||
rows.concat(generate_match_rows_for_weight(weight))
|
||||
end
|
||||
rows
|
||||
end
|
||||
|
||||
def generate_matches_for_weight(weight)
|
||||
round_one(weight)
|
||||
round_two(weight)
|
||||
round_three(weight)
|
||||
round_four(weight)
|
||||
round_five(weight)
|
||||
def generate_match_rows_for_weight(weight)
|
||||
rows = []
|
||||
round_one(weight, rows)
|
||||
round_two(weight, rows)
|
||||
round_three(weight, rows)
|
||||
round_four(weight, rows)
|
||||
round_five(weight, rows)
|
||||
rows
|
||||
end
|
||||
|
||||
def round_one(weight)
|
||||
create_matchup_from_seed(1,16, "Bracket Round of 16", 1, 1,weight)
|
||||
create_matchup_from_seed(8,9, "Bracket Round of 16", 2, 1,weight)
|
||||
create_matchup_from_seed(5,12, "Bracket Round of 16", 3, 1,weight)
|
||||
create_matchup_from_seed(4,14, "Bracket Round of 16", 4, 1,weight)
|
||||
create_matchup_from_seed(3,13, "Bracket Round of 16", 5, 1,weight)
|
||||
create_matchup_from_seed(6,11, "Bracket Round of 16", 6, 1,weight)
|
||||
create_matchup_from_seed(7,10, "Bracket Round of 16", 7, 1,weight)
|
||||
create_matchup_from_seed(2,15, "Bracket Round of 16", 8, 1,weight)
|
||||
def round_one(weight, rows)
|
||||
rows << create_matchup_from_seed(1,16, "Bracket Round of 16", 1, 1,weight)
|
||||
rows << create_matchup_from_seed(8,9, "Bracket Round of 16", 2, 1,weight)
|
||||
rows << create_matchup_from_seed(5,12, "Bracket Round of 16", 3, 1,weight)
|
||||
rows << create_matchup_from_seed(4,14, "Bracket Round of 16", 4, 1,weight)
|
||||
rows << create_matchup_from_seed(3,13, "Bracket Round of 16", 5, 1,weight)
|
||||
rows << create_matchup_from_seed(6,11, "Bracket Round of 16", 6, 1,weight)
|
||||
rows << create_matchup_from_seed(7,10, "Bracket Round of 16", 7, 1,weight)
|
||||
rows << create_matchup_from_seed(2,15, "Bracket Round of 16", 8, 1,weight)
|
||||
end
|
||||
|
||||
def round_two(weight)
|
||||
create_matchup(nil,nil,"Quarter",1,2,weight)
|
||||
create_matchup(nil,nil,"Quarter",2,2,weight)
|
||||
create_matchup(nil,nil,"Quarter",3,2,weight)
|
||||
create_matchup(nil,nil,"Quarter",4,2,weight)
|
||||
create_matchup(nil,nil,"Conso Round of 8",1,2,weight)
|
||||
create_matchup(nil,nil,"Conso Round of 8",2,2,weight)
|
||||
create_matchup(nil,nil,"Conso Round of 8",3,2,weight)
|
||||
create_matchup(nil,nil,"Conso Round of 8",4,2,weight)
|
||||
def round_two(weight, rows)
|
||||
rows << create_matchup(nil,nil,"Quarter",1,2,weight)
|
||||
rows << create_matchup(nil,nil,"Quarter",2,2,weight)
|
||||
rows << create_matchup(nil,nil,"Quarter",3,2,weight)
|
||||
rows << create_matchup(nil,nil,"Quarter",4,2,weight)
|
||||
rows << create_matchup(nil,nil,"Conso Round of 8",1,2,weight)
|
||||
rows << create_matchup(nil,nil,"Conso Round of 8",2,2,weight)
|
||||
rows << create_matchup(nil,nil,"Conso Round of 8",3,2,weight)
|
||||
rows << create_matchup(nil,nil,"Conso Round of 8",4,2,weight)
|
||||
end
|
||||
|
||||
def round_three(weight)
|
||||
create_matchup(nil,nil,"Semis",1,3,weight)
|
||||
create_matchup(nil,nil,"Semis",2,3,weight)
|
||||
create_matchup(nil,nil,"Conso Quarter",1,3,weight)
|
||||
create_matchup(nil,nil,"Conso Quarter",2,3,weight)
|
||||
create_matchup(nil,nil,"Conso Quarter",3,3,weight)
|
||||
create_matchup(nil,nil,"Conso Quarter",4,3,weight)
|
||||
def round_three(weight, rows)
|
||||
rows << create_matchup(nil,nil,"Semis",1,3,weight)
|
||||
rows << create_matchup(nil,nil,"Semis",2,3,weight)
|
||||
rows << create_matchup(nil,nil,"Conso Quarter",1,3,weight)
|
||||
rows << create_matchup(nil,nil,"Conso Quarter",2,3,weight)
|
||||
rows << create_matchup(nil,nil,"Conso Quarter",3,3,weight)
|
||||
rows << create_matchup(nil,nil,"Conso Quarter",4,3,weight)
|
||||
end
|
||||
|
||||
def round_four(weight)
|
||||
create_matchup(nil,nil,"Conso Semis",1,4,weight)
|
||||
create_matchup(nil,nil,"Conso Semis",2,4,weight)
|
||||
def round_four(weight, rows)
|
||||
rows << create_matchup(nil,nil,"Conso Semis",1,4,weight)
|
||||
rows << create_matchup(nil,nil,"Conso Semis",2,4,weight)
|
||||
end
|
||||
|
||||
def round_five(weight)
|
||||
create_matchup(nil,nil,"1/2",1,5,weight)
|
||||
create_matchup(nil,nil,"3/4",1,5,weight)
|
||||
create_matchup(nil,nil,"5/6",1,5,weight)
|
||||
def round_five(weight, rows)
|
||||
rows << create_matchup(nil,nil,"1/2",1,5,weight)
|
||||
rows << create_matchup(nil,nil,"3/4",1,5,weight)
|
||||
rows << create_matchup(nil,nil,"5/6",1,5,weight)
|
||||
if @number_of_placers >= 8
|
||||
create_matchup(nil,nil,"7/8",1,5,weight)
|
||||
rows << create_matchup(nil,nil,"7/8",1,5,weight)
|
||||
end
|
||||
end
|
||||
|
||||
def wrestler_with_seed(seed,weight)
|
||||
wrestler = Wrestler.where("weight_id = ? and bracket_line = ?", weight.id, seed).first
|
||||
wrestler = weight.wrestlers.find { |w| w.bracket_line == seed }
|
||||
if wrestler
|
||||
return wrestler.id
|
||||
else
|
||||
@@ -79,13 +84,18 @@ class ModifiedSixteenManMatchGeneration
|
||||
end
|
||||
|
||||
def create_matchup(w1, w2, bracket_position, bracket_position_number,round,weight)
|
||||
@tournament.matches.create(
|
||||
{
|
||||
w1: w1,
|
||||
w2: w2,
|
||||
tournament_id: @tournament.id,
|
||||
weight_id: weight.id,
|
||||
round: round,
|
||||
bracket_position: bracket_position,
|
||||
bracket_position_number: bracket_position_number
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def generation_weights
|
||||
@weights || @tournament.weights.to_a
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,18 +12,19 @@ class PoolBracketGeneration
|
||||
end
|
||||
|
||||
def generateBracketMatches()
|
||||
@rows = []
|
||||
if @pool_bracket_type == "twoPoolsToSemi"
|
||||
return twoPoolsToSemi()
|
||||
twoPoolsToSemi()
|
||||
elsif @pool_bracket_type == "twoPoolsToFinal"
|
||||
return twoPoolsToFinal()
|
||||
twoPoolsToFinal()
|
||||
elsif @pool_bracket_type == "fourPoolsToQuarter"
|
||||
return fourPoolsToQuarter()
|
||||
fourPoolsToQuarter()
|
||||
elsif @pool_bracket_type == "fourPoolsToSemi"
|
||||
return fourPoolsToSemi()
|
||||
fourPoolsToSemi()
|
||||
elsif @pool_bracket_type == "eightPoolsToQuarter"
|
||||
return eightPoolsToQuarter()
|
||||
eightPoolsToQuarter()
|
||||
end
|
||||
return []
|
||||
@rows
|
||||
end
|
||||
|
||||
def twoPoolsToSemi()
|
||||
@@ -86,14 +87,15 @@ class PoolBracketGeneration
|
||||
end
|
||||
|
||||
def createMatchup(w1_name, w2_name, bracket_position, bracket_position_number)
|
||||
@tournament.matches.create(
|
||||
@rows << {
|
||||
loser1_name: w1_name,
|
||||
loser2_name: w2_name,
|
||||
tournament_id: @tournament.id,
|
||||
weight_id: @weight.id,
|
||||
round: @round,
|
||||
bracket_position: bracket_position,
|
||||
bracket_position_number: bracket_position_number
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,35 +1,46 @@
|
||||
class PoolGeneration
|
||||
def initialize(weight)
|
||||
def initialize(weight, wrestlers: nil)
|
||||
@weight = weight
|
||||
@tournament = @weight.tournament
|
||||
@pool = 1
|
||||
@wrestlers = wrestlers
|
||||
end
|
||||
|
||||
def generatePools
|
||||
GeneratePoolNumbers.new(@weight).savePoolNumbers
|
||||
GeneratePoolNumbers.new(@weight).savePoolNumbers(wrestlers: wrestlers_for_weight, persist: false)
|
||||
rows = []
|
||||
pools = @weight.pools
|
||||
while @pool <= pools
|
||||
roundRobin
|
||||
rows.concat(roundRobin)
|
||||
@pool += 1
|
||||
end
|
||||
rows
|
||||
end
|
||||
|
||||
def roundRobin
|
||||
wrestlers = @weight.wrestlers_in_pool(@pool)
|
||||
rows = []
|
||||
wrestlers = wrestlers_for_weight.select { |w| w.pool == @pool }
|
||||
pool_matches = RoundRobinTournament.schedule(wrestlers).reverse
|
||||
pool_matches.each_with_index do |b, index|
|
||||
round = index + 1
|
||||
bouts = b.map
|
||||
bouts.each do |bout|
|
||||
if bout[0] != nil and bout[1] != nil
|
||||
@tournament.matches.create(
|
||||
rows << {
|
||||
w1: bout[0].id,
|
||||
w2: bout[1].id,
|
||||
tournament_id: @tournament.id,
|
||||
weight_id: @weight.id,
|
||||
bracket_position: "Pool",
|
||||
round: round)
|
||||
round: round
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
rows
|
||||
end
|
||||
|
||||
def wrestlers_for_weight
|
||||
@wrestlers || @weight.wrestlers.to_a
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,80 +1,97 @@
|
||||
class PoolToBracketGenerateLoserNames
|
||||
def initialize( tournament )
|
||||
@tournament = tournament
|
||||
end
|
||||
|
||||
def assignLoserNamesWeight(weight)
|
||||
matches_by_weight = @tournament.matches.where(weight_id: weight.id)
|
||||
if weight.pool_bracket_type == "twoPoolsToSemi"
|
||||
twoPoolsToSemiLoser(matches_by_weight)
|
||||
elsif (weight.pool_bracket_type == "fourPoolsToQuarter") or (weight.pool_bracket_type == "eightPoolsToQuarter")
|
||||
fourPoolsToQuarterLoser(matches_by_weight)
|
||||
elsif weight.pool_bracket_type == "fourPoolsToSemi"
|
||||
fourPoolsToSemiLoser(matches_by_weight)
|
||||
end
|
||||
saveMatches(matches_by_weight)
|
||||
def initialize(tournament)
|
||||
@tournament = tournament
|
||||
end
|
||||
|
||||
def assignLoserNames
|
||||
matches_by_weight = nil
|
||||
@tournament.weights.each do |w|
|
||||
matches_by_weight = @tournament.matches.where(weight_id: w.id)
|
||||
if w.pool_bracket_type == "twoPoolsToSemi"
|
||||
twoPoolsToSemiLoser(matches_by_weight)
|
||||
elsif (w.pool_bracket_type == "fourPoolsToQuarter") or (w.pool_bracket_type == "eightPoolsToQuarter")
|
||||
fourPoolsToQuarterLoser(matches_by_weight)
|
||||
elsif w.pool_bracket_type == "fourPoolsToSemi"
|
||||
fourPoolsToSemiLoser(matches_by_weight)
|
||||
end
|
||||
saveMatches(matches_by_weight)
|
||||
end
|
||||
end
|
||||
|
||||
def twoPoolsToSemiLoser(matches_by_weight)
|
||||
match1 = matches_by_weight.select{|m| m.loser1_name == "Winner Pool 1"}.first
|
||||
match2 = matches_by_weight.select{|m| m.loser1_name == "Winner Pool 2"}.first
|
||||
matchChange = matches_by_weight.select{|m| m.bracket_position == "3/4"}.first
|
||||
matchChange.loser1_name = "Loser of #{match1.bout_number}"
|
||||
matchChange.loser2_name = "Loser of #{match2.bout_number}"
|
||||
# Compatibility wrapper. Returns transformed rows and does not persist.
|
||||
def assignLoserNamesWeight(weight, match_rows = nil)
|
||||
rows = match_rows || @tournament.matches.where(weight_id: weight.id).map { |m| m.attributes.symbolize_keys }
|
||||
assign_loser_names_in_memory(weight, rows)
|
||||
rows
|
||||
end
|
||||
|
||||
def fourPoolsToQuarterLoser(matches_by_weight)
|
||||
quarters = matches_by_weight.select{|m| m.bracket_position == "Quarter"}
|
||||
consoSemis = matches_by_weight.select{|m| m.bracket_position == "Conso Semis"}
|
||||
semis = matches_by_weight.select{|m| m.bracket_position == "Semis"}
|
||||
thirdFourth = matches_by_weight.select{|m| m.bracket_position == "3/4"}.first
|
||||
seventhEighth = matches_by_weight.select{|m| m.bracket_position == "7/8"}.first
|
||||
consoSemis.each do |m|
|
||||
if m.bracket_position_number == 1
|
||||
m.loser1_name = "Loser of #{quarters.select{|m| m.bracket_position_number == 1}.first.bout_number}"
|
||||
m.loser2_name = "Loser of #{quarters.select{|m| m.bracket_position_number == 2}.first.bout_number}"
|
||||
elsif m.bracket_position_number == 2
|
||||
m.loser1_name = "Loser of #{quarters.select{|m| m.bracket_position_number == 3}.first.bout_number}"
|
||||
m.loser2_name = "Loser of #{quarters.select{|m| m.bracket_position_number == 4}.first.bout_number}"
|
||||
# Compatibility wrapper. Returns transformed rows and does not persist.
|
||||
def assignLoserNames
|
||||
@tournament.weights.each_with_object([]) do |weight, all_rows|
|
||||
all_rows.concat(assignLoserNamesWeight(weight))
|
||||
end
|
||||
end
|
||||
|
||||
def assign_loser_names_in_memory(weight, match_rows)
|
||||
rows = match_rows.select { |row| row[:weight_id] == weight.id }
|
||||
if weight.pool_bracket_type == "twoPoolsToSemi"
|
||||
two_pools_to_semi_loser_rows(rows)
|
||||
elsif (weight.pool_bracket_type == "fourPoolsToQuarter") || (weight.pool_bracket_type == "eightPoolsToQuarter")
|
||||
four_pools_to_quarter_loser_rows(rows)
|
||||
elsif weight.pool_bracket_type == "fourPoolsToSemi"
|
||||
four_pools_to_semi_loser_rows(rows)
|
||||
end
|
||||
end
|
||||
|
||||
def two_pools_to_semi_loser_rows(rows)
|
||||
match1 = rows.find { |m| m[:loser1_name] == "Winner Pool 1" }
|
||||
match2 = rows.find { |m| m[:loser1_name] == "Winner Pool 2" }
|
||||
match_change = rows.find { |m| m[:bracket_position] == "3/4" }
|
||||
return unless match1 && match2 && match_change
|
||||
|
||||
match_change[:loser1_name] = "Loser of #{match1[:bout_number]}"
|
||||
match_change[:loser2_name] = "Loser of #{match2[:bout_number]}"
|
||||
end
|
||||
|
||||
def four_pools_to_quarter_loser_rows(rows)
|
||||
quarters = rows.select { |m| m[:bracket_position] == "Quarter" }
|
||||
conso_semis = rows.select { |m| m[:bracket_position] == "Conso Semis" }
|
||||
semis = rows.select { |m| m[:bracket_position] == "Semis" }
|
||||
third_fourth = rows.find { |m| m[:bracket_position] == "3/4" }
|
||||
seventh_eighth = rows.find { |m| m[:bracket_position] == "7/8" }
|
||||
|
||||
conso_semis.each do |m|
|
||||
if m[:bracket_position_number] == 1
|
||||
q1 = quarters.find { |q| q[:bracket_position_number] == 1 }
|
||||
q2 = quarters.find { |q| q[:bracket_position_number] == 2 }
|
||||
m[:loser1_name] = "Loser of #{q1[:bout_number]}" if q1
|
||||
m[:loser2_name] = "Loser of #{q2[:bout_number]}" if q2
|
||||
elsif m[:bracket_position_number] == 2
|
||||
q3 = quarters.find { |q| q[:bracket_position_number] == 3 }
|
||||
q4 = quarters.find { |q| q[:bracket_position_number] == 4 }
|
||||
m[:loser1_name] = "Loser of #{q3[:bout_number]}" if q3
|
||||
m[:loser2_name] = "Loser of #{q4[:bout_number]}" if q4
|
||||
end
|
||||
end
|
||||
thirdFourth.loser1_name = "Loser of #{semis.select{|m| m.bracket_position_number == 1}.first.bout_number}"
|
||||
thirdFourth.loser2_name = "Loser of #{semis.select{|m| m.bracket_position_number == 2}.first.bout_number}"
|
||||
consoSemis = matches_by_weight.select{|m| m.bracket_position == "Conso Semis"}
|
||||
seventhEighth.loser1_name = "Loser of #{consoSemis.select{|m| m.bracket_position_number == 1}.first.bout_number}"
|
||||
seventhEighth.loser2_name = "Loser of #{consoSemis.select{|m| m.bracket_position_number == 2}.first.bout_number}"
|
||||
|
||||
if third_fourth
|
||||
s1 = semis.find { |s| s[:bracket_position_number] == 1 }
|
||||
s2 = semis.find { |s| s[:bracket_position_number] == 2 }
|
||||
third_fourth[:loser1_name] = "Loser of #{s1[:bout_number]}" if s1
|
||||
third_fourth[:loser2_name] = "Loser of #{s2[:bout_number]}" if s2
|
||||
end
|
||||
|
||||
if seventh_eighth
|
||||
c1 = conso_semis.find { |c| c[:bracket_position_number] == 1 }
|
||||
c2 = conso_semis.find { |c| c[:bracket_position_number] == 2 }
|
||||
seventh_eighth[:loser1_name] = "Loser of #{c1[:bout_number]}" if c1
|
||||
seventh_eighth[:loser2_name] = "Loser of #{c2[:bout_number]}" if c2
|
||||
end
|
||||
end
|
||||
|
||||
def fourPoolsToSemiLoser(matches_by_weight)
|
||||
semis = matches_by_weight.select{|m| m.bracket_position == "Semis"}
|
||||
thirdFourth = matches_by_weight.select{|m| m.bracket_position == "3/4"}.first
|
||||
consoSemis = matches_by_weight.select{|m| m.bracket_position == "Conso Semis"}
|
||||
seventhEighth = matches_by_weight.select{|m| m.bracket_position == "7/8"}.first
|
||||
thirdFourth.loser1_name = "Loser of #{semis.select{|m| m.bracket_position_number == 1}.first.bout_number}"
|
||||
thirdFourth.loser2_name = "Loser of #{semis.select{|m| m.bracket_position_number == 2}.first.bout_number}"
|
||||
seventhEighth.loser1_name = "Loser of #{consoSemis.select{|m| m.bracket_position_number == 1}.first.bout_number}"
|
||||
seventhEighth.loser2_name = "Loser of #{consoSemis.select{|m| m.bracket_position_number == 2}.first.bout_number}"
|
||||
def four_pools_to_semi_loser_rows(rows)
|
||||
semis = rows.select { |m| m[:bracket_position] == "Semis" }
|
||||
conso_semis = rows.select { |m| m[:bracket_position] == "Conso Semis" }
|
||||
third_fourth = rows.find { |m| m[:bracket_position] == "3/4" }
|
||||
seventh_eighth = rows.find { |m| m[:bracket_position] == "7/8" }
|
||||
|
||||
if third_fourth
|
||||
s1 = semis.find { |s| s[:bracket_position_number] == 1 }
|
||||
s2 = semis.find { |s| s[:bracket_position_number] == 2 }
|
||||
third_fourth[:loser1_name] = "Loser of #{s1[:bout_number]}" if s1
|
||||
third_fourth[:loser2_name] = "Loser of #{s2[:bout_number]}" if s2
|
||||
end
|
||||
|
||||
if seventh_eighth
|
||||
c1 = conso_semis.find { |c| c[:bracket_position_number] == 1 }
|
||||
c2 = conso_semis.find { |c| c[:bracket_position_number] == 2 }
|
||||
seventh_eighth[:loser1_name] = "Loser of #{c1[:bout_number]}" if c1
|
||||
seventh_eighth[:loser2_name] = "Loser of #{c2[:bout_number]}" if c2
|
||||
end
|
||||
end
|
||||
|
||||
def saveMatches(matches)
|
||||
matches.each do |m|
|
||||
m.save!
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,44 +1,92 @@
|
||||
class PoolToBracketMatchGeneration
|
||||
def initialize( tournament )
|
||||
def initialize(tournament, weights: nil, wrestlers_by_weight_id: nil)
|
||||
@tournament = tournament
|
||||
@weights = weights
|
||||
@wrestlers_by_weight_id = wrestlers_by_weight_id
|
||||
end
|
||||
|
||||
def generatePoolToBracketMatches
|
||||
@tournament.weights.order(:max).each do |weight|
|
||||
PoolGeneration.new(weight).generatePools()
|
||||
last_match = @tournament.matches.where(weight: weight).order(round: :desc).limit(1).first
|
||||
highest_round = last_match.round
|
||||
PoolBracketGeneration.new(weight, highest_round).generateBracketMatches()
|
||||
rows = []
|
||||
generation_weights.each do |weight|
|
||||
wrestlers = wrestlers_for_weight(weight)
|
||||
pool_rows = PoolGeneration.new(weight, wrestlers: wrestlers).generatePools
|
||||
rows.concat(pool_rows)
|
||||
|
||||
highest_round = pool_rows.map { |row| row[:round] }.max || 0
|
||||
bracket_rows = PoolBracketGeneration.new(weight, highest_round).generateBracketMatches
|
||||
rows.concat(bracket_rows)
|
||||
end
|
||||
movePoolSeedsToFinalPoolRound
|
||||
|
||||
movePoolSeedsToFinalPoolRound(rows)
|
||||
rows
|
||||
end
|
||||
|
||||
def movePoolSeedsToFinalPoolRound
|
||||
@tournament.weights.each do |w|
|
||||
setOriginalSeedsToWrestleLastPoolRound(w)
|
||||
def movePoolSeedsToFinalPoolRound(match_rows)
|
||||
generation_weights.each do |w|
|
||||
setOriginalSeedsToWrestleLastPoolRound(w, match_rows)
|
||||
end
|
||||
end
|
||||
|
||||
def setOriginalSeedsToWrestleLastPoolRound(weight)
|
||||
def setOriginalSeedsToWrestleLastPoolRound(weight, match_rows)
|
||||
pool = 1
|
||||
until pool > weight.pools
|
||||
wrestler1 = weight.pool_wrestlers_sorted_by_bracket_line(pool).first
|
||||
wrestler2 = weight.pool_wrestlers_sorted_by_bracket_line(pool).second
|
||||
match = wrestler1.pool_matches.sort_by{|m| m.round}.last
|
||||
if match.w1 != wrestler2.id or match.w2 != wrestler2.id
|
||||
if match.w1 == wrestler1.id
|
||||
SwapWrestlers.new.swap_wrestlers_bracket_lines(match.w2,wrestler2.id)
|
||||
elsif match.w2 == wrestler1.id
|
||||
SwapWrestlers.new.swap_wrestlers_bracket_lines(match.w1,wrestler2.id)
|
||||
end
|
||||
wrestlers = wrestlers_for_weight(weight)
|
||||
weight_pools = weight.pools
|
||||
until pool > weight_pools
|
||||
pool_wrestlers = wrestlers.select { |w| w.pool == pool }.sort_by(&:bracket_line)
|
||||
wrestler1 = pool_wrestlers.first
|
||||
wrestler2 = pool_wrestlers.second
|
||||
if wrestler1 && wrestler2
|
||||
pool_matches = match_rows.select { |row| row[:weight_id] == weight.id && row[:bracket_position] == "Pool" && (row[:w1] == wrestler1.id || row[:w2] == wrestler1.id) }
|
||||
match = pool_matches.max_by { |row| row[:round] }
|
||||
if match && (match[:w1] != wrestler2.id || match[:w2] != wrestler2.id)
|
||||
if match[:w1] == wrestler1.id
|
||||
swap_wrestlers_in_memory(match_rows, wrestlers, match[:w2], wrestler2.id)
|
||||
elsif match[:w2] == wrestler1.id
|
||||
swap_wrestlers_in_memory(match_rows, wrestlers, match[:w1], wrestler2.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
pool += 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def swap_wrestlers_in_memory(match_rows, wrestlers, wrestler1_id, wrestler2_id)
|
||||
w1 = wrestlers.find { |w| w.id == wrestler1_id }
|
||||
w2 = wrestlers.find { |w| w.id == wrestler2_id }
|
||||
return unless w1 && w2
|
||||
|
||||
w1_bracket_line, w1_pool = w1.bracket_line, w1.pool
|
||||
w1.bracket_line, w1.pool = w2.bracket_line, w2.pool
|
||||
w2.bracket_line, w2.pool = w1_bracket_line, w1_pool
|
||||
|
||||
swap_match_rows(match_rows, wrestler1_id, wrestler2_id)
|
||||
end
|
||||
|
||||
def swap_match_rows(match_rows, wrestler1_id, wrestler2_id)
|
||||
match_rows.each do |row|
|
||||
row[:w1] = swap_id(row[:w1], wrestler1_id, wrestler2_id)
|
||||
row[:w2] = swap_id(row[:w2], wrestler1_id, wrestler2_id)
|
||||
row[:winner_id] = swap_id(row[:winner_id], wrestler1_id, wrestler2_id)
|
||||
end
|
||||
end
|
||||
|
||||
def swap_id(value, wrestler1_id, wrestler2_id)
|
||||
return wrestler2_id if value == wrestler1_id
|
||||
return wrestler1_id if value == wrestler2_id
|
||||
|
||||
value
|
||||
end
|
||||
|
||||
def generation_weights
|
||||
@weights || @tournament.weights.order(:max).to_a
|
||||
end
|
||||
|
||||
def wrestlers_for_weight(weight)
|
||||
@wrestlers_by_weight_id&.fetch(weight.id, nil) || weight.wrestlers.to_a
|
||||
end
|
||||
|
||||
def assignLoserNames
|
||||
PoolToBracketGenerateLoserNames.new(@tournament).assignLoserNames
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -37,7 +37,11 @@ class TournamentBackupService
|
||||
attributes: @tournament.attributes,
|
||||
schools: @tournament.schools.map(&:attributes),
|
||||
weights: @tournament.weights.map(&:attributes),
|
||||
mats: @tournament.mats.map(&:attributes),
|
||||
mats: @tournament.mats.map do |mat|
|
||||
mat.attributes.merge(
|
||||
"queue_bout_numbers" => mat.queue_matches.map { |match| match&.bout_number }
|
||||
)
|
||||
end,
|
||||
mat_assignment_rules: @tournament.mat_assignment_rules.map do |rule|
|
||||
rule.attributes.merge(
|
||||
mat: Mat.find_by(id: rule.mat_id)&.attributes.slice("name"),
|
||||
|
||||
@@ -3,30 +3,36 @@ class TournamentSeeding
|
||||
@tournament = tournament
|
||||
end
|
||||
|
||||
def set_seeds
|
||||
@tournament.weights.each do |weight|
|
||||
def set_seeds(weights: nil, persist: true)
|
||||
weights_to_seed = weights || @tournament.weights.includes(:wrestlers)
|
||||
updated_wrestlers = []
|
||||
|
||||
weights_to_seed.each do |weight|
|
||||
wrestlers = weight.wrestlers
|
||||
bracket_size = weight.calculate_bracket_size
|
||||
|
||||
wrestlers = reset_bracket_line_for_lines_higher_than_bracket_size(wrestlers, bracket_size)
|
||||
wrestlers = set_original_seed_to_bracket_line(wrestlers)
|
||||
wrestlers = random_seeding(wrestlers, bracket_size)
|
||||
wrestlers.each(&:save)
|
||||
updated_wrestlers.concat(wrestlers)
|
||||
end
|
||||
|
||||
persist_bracket_lines(updated_wrestlers) if persist
|
||||
updated_wrestlers
|
||||
end
|
||||
|
||||
def random_seeding(wrestlers, bracket_size)
|
||||
half_of_bracket = bracket_size / 2
|
||||
available_bracket_lines = (1..bracket_size).to_a
|
||||
first_half_available_bracket_lines = (1..half_of_bracket).to_a
|
||||
|
||||
# remove bracket lines that are taken from available_bracket_lines
|
||||
wrestlers_with_bracket_lines = wrestlers.select{|w| w.bracket_line != nil }
|
||||
wrestlers_with_bracket_lines.each do |wrestler|
|
||||
available_bracket_lines.delete(wrestler.bracket_line)
|
||||
first_half_available_bracket_lines.delete(wrestler.bracket_line)
|
||||
end
|
||||
|
||||
available_bracket_lines_to_use = set_random_seeding_bracket_line_order(available_bracket_lines)
|
||||
|
||||
wrestlers_without_bracket_lines = wrestlers.select{|w| w.bracket_line == nil }
|
||||
if @tournament.tournament_type == "Pool to bracket"
|
||||
wrestlers_without_bracket_lines.shuffle.each do |wrestler|
|
||||
@@ -38,15 +44,10 @@ class TournamentSeeding
|
||||
else
|
||||
# Iterrate over the list randomly
|
||||
wrestlers_without_bracket_lines.shuffle.each do |wrestler|
|
||||
if first_half_available_bracket_lines.size > 0
|
||||
random_available_bracket_line = first_half_available_bracket_lines.sample
|
||||
wrestler.bracket_line = random_available_bracket_line
|
||||
available_bracket_lines.delete(random_available_bracket_line)
|
||||
first_half_available_bracket_lines.delete(random_available_bracket_line)
|
||||
else
|
||||
random_available_bracket_line = available_bracket_lines.sample
|
||||
wrestler.bracket_line = random_available_bracket_line
|
||||
available_bracket_lines.delete(random_available_bracket_line)
|
||||
if available_bracket_lines_to_use.size > 0
|
||||
bracket_line_to_use = available_bracket_lines_to_use.first
|
||||
wrestler.bracket_line = bracket_line_to_use
|
||||
available_bracket_lines_to_use.delete(bracket_line_to_use)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -81,4 +82,39 @@ class TournamentSeeding
|
||||
end
|
||||
return wrestlers
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_random_seeding_bracket_line_order(available_bracket_lines)
|
||||
# This method prevents double BYEs in round 1
|
||||
# It also evenly distributes matches from the top half of the bracket to the bottom half
|
||||
# It does both of these while keeping the randomness of the line assignment
|
||||
odd_or_even = [0, 1]
|
||||
odd_or_even_sample = odd_or_even.sample
|
||||
|
||||
# sort by odd or even based on the sample above
|
||||
if odd_or_even_sample == 1
|
||||
# odd numbers first
|
||||
result = available_bracket_lines.sort_by { |n| n.even? ? 1 : 0 }
|
||||
else
|
||||
# even numbers first
|
||||
result = available_bracket_lines.sort_by { |n| n.odd? ? 1 : 0 }
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def persist_bracket_lines(wrestlers)
|
||||
return if wrestlers.blank?
|
||||
|
||||
timestamp = Time.current
|
||||
updates = wrestlers.map do |wrestler|
|
||||
{
|
||||
id: wrestler.id,
|
||||
bracket_line: wrestler.bracket_line,
|
||||
updated_at: timestamp
|
||||
}
|
||||
end
|
||||
|
||||
Wrestler.upsert_all(updates)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,10 +14,10 @@ class WipeTournamentMatches
|
||||
end
|
||||
|
||||
def wipeMatches
|
||||
@tournament.matches.destroy_all
|
||||
@tournament.destroy_all_matches
|
||||
end
|
||||
|
||||
def resetSchoolScores
|
||||
@tournament.schools.update_all("score = 0.0")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -41,20 +41,20 @@ class WrestlingdevImporter
|
||||
@tournament.matches.destroy_all
|
||||
@tournament.mat_assignment_rules.destroy_all # Explicitly destroy rules (might be redundant if Mat cascades)
|
||||
@tournament.delegates.destroy_all
|
||||
@tournament.tournament_backups.destroy_all
|
||||
@tournament.tournament_job_statuses.destroy_all
|
||||
# Note: Teampointadjusts are deleted via School/Wrestler cascade
|
||||
end
|
||||
|
||||
def parse_data
|
||||
parse_tournament(@import_data["tournament"]["attributes"])
|
||||
parse_schools(@import_data["tournament"]["schools"])
|
||||
parse_weights(@import_data["tournament"]["weights"])
|
||||
parse_mats(@import_data["tournament"]["mats"])
|
||||
parse_wrestlers(@import_data["tournament"]["wrestlers"])
|
||||
parse_matches(@import_data["tournament"]["matches"])
|
||||
parse_mat_assignment_rules(@import_data["tournament"]["mat_assignment_rules"])
|
||||
end
|
||||
def parse_data
|
||||
parse_tournament(@import_data["tournament"]["attributes"])
|
||||
parse_schools(@import_data["tournament"]["schools"])
|
||||
parse_weights(@import_data["tournament"]["weights"])
|
||||
parse_mats(@import_data["tournament"]["mats"])
|
||||
parse_wrestlers(@import_data["tournament"]["wrestlers"])
|
||||
parse_matches(@import_data["tournament"]["matches"])
|
||||
apply_mat_queues
|
||||
parse_mat_assignment_rules(@import_data["tournament"]["mat_assignment_rules"])
|
||||
end
|
||||
|
||||
def parse_tournament(attributes)
|
||||
attributes.except!("id")
|
||||
@@ -75,12 +75,18 @@ class WrestlingdevImporter
|
||||
end
|
||||
end
|
||||
|
||||
def parse_mats(mats)
|
||||
mats.each do |mat_attributes|
|
||||
mat_attributes.except!("id")
|
||||
Mat.create(mat_attributes.merge(tournament_id: @tournament.id))
|
||||
end
|
||||
end
|
||||
def parse_mats(mats)
|
||||
@mat_queue_bout_numbers = {}
|
||||
mats.each do |mat_attributes|
|
||||
mat_name = mat_attributes["name"]
|
||||
queue_bout_numbers = mat_attributes["queue_bout_numbers"]
|
||||
mat_attributes.except!("id", "queue1", "queue2", "queue3", "queue4", "queue_bout_numbers", "tournament_id")
|
||||
Mat.create(mat_attributes.merge(tournament_id: @tournament.id))
|
||||
if mat_name && queue_bout_numbers
|
||||
@mat_queue_bout_numbers[mat_name] = queue_bout_numbers
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse_mat_assignment_rules(mat_assignment_rules)
|
||||
mat_assignment_rules.each do |rule_attributes|
|
||||
@@ -135,9 +141,9 @@ class WrestlingdevImporter
|
||||
end
|
||||
end
|
||||
|
||||
def parse_matches(matches)
|
||||
matches.each do |match_attributes|
|
||||
next unless match_attributes # Skip if match_attributes is nil
|
||||
def parse_matches(matches)
|
||||
matches.each do |match_attributes|
|
||||
next unless match_attributes # Skip if match_attributes is nil
|
||||
|
||||
weight = Weight.find_by(max: match_attributes.dig("weight", "max"), tournament_id: @tournament.id)
|
||||
mat = Mat.find_by(name: match_attributes.dig("mat", "name"), tournament_id: @tournament.id)
|
||||
@@ -156,6 +162,53 @@ class WrestlingdevImporter
|
||||
w2: w2&.id,
|
||||
winner_id: winner&.id
|
||||
))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def apply_mat_queues
|
||||
if @mat_queue_bout_numbers.blank?
|
||||
Mat.where(tournament_id: @tournament.id).find_each do |mat|
|
||||
match_ids = mat.matches.where(finished: [nil, 0]).order(:bout_number).limit(4).pluck(:id)
|
||||
mat.update(
|
||||
queue1: match_ids[0],
|
||||
queue2: match_ids[1],
|
||||
queue3: match_ids[2],
|
||||
queue4: match_ids[3]
|
||||
)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
@mat_queue_bout_numbers.each do |mat_name, bout_numbers|
|
||||
mat = Mat.find_by(name: mat_name, tournament_id: @tournament.id)
|
||||
next unless mat
|
||||
|
||||
matches = Array(bout_numbers).map do |bout_number|
|
||||
Match.find_by(bout_number: bout_number, tournament_id: @tournament.id)
|
||||
end
|
||||
|
||||
mat.update(
|
||||
queue1: matches[0]&.id,
|
||||
queue2: matches[1]&.id,
|
||||
queue3: matches[2]&.id,
|
||||
queue4: matches[3]&.id
|
||||
)
|
||||
|
||||
matches.compact.each do |match|
|
||||
match.update(mat_id: mat.id)
|
||||
end
|
||||
end
|
||||
|
||||
Mat.where(tournament_id: @tournament.id)
|
||||
.where(queue1: nil, queue2: nil, queue3: nil, queue4: nil)
|
||||
.find_each do |mat|
|
||||
match_ids = mat.matches.where(finished: [nil, 0]).order(:bout_number).limit(4).pluck(:id)
|
||||
mat.update(
|
||||
queue1: match_ids[0],
|
||||
queue2: match_ids[1],
|
||||
queue3: match_ids[2],
|
||||
queue4: match_ids[3]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,11 +3,13 @@ class GeneratePoolNumbers
|
||||
@weight = weight
|
||||
end
|
||||
|
||||
def savePoolNumbers
|
||||
@weight.wrestlers.each do |wrestler|
|
||||
def savePoolNumbers(wrestlers: nil, persist: true)
|
||||
wrestlers_to_update = wrestlers || @weight.wrestlers.to_a
|
||||
wrestlers_to_update.each do |wrestler|
|
||||
wrestler.pool = get_wrestler_pool_number(@weight.pools, wrestler.bracket_line)
|
||||
wrestler.save
|
||||
end
|
||||
persist_pool_numbers(wrestlers_to_update) if persist
|
||||
wrestlers_to_update
|
||||
end
|
||||
|
||||
def get_wrestler_pool_number(number_of_pools, wrestler_seed)
|
||||
@@ -36,4 +38,20 @@ class GeneratePoolNumbers
|
||||
|
||||
pool
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def persist_pool_numbers(wrestlers)
|
||||
return if wrestlers.blank?
|
||||
|
||||
timestamp = Time.current
|
||||
rows = wrestlers.map do |w|
|
||||
{
|
||||
id: w.id,
|
||||
pool: w.pool,
|
||||
updated_at: timestamp
|
||||
}
|
||||
end
|
||||
Wrestler.upsert_all(rows)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,29 +54,20 @@ class CalculateWrestlerTeamScore
|
||||
def byePoints
|
||||
points = 0
|
||||
if @tournament.tournament_type == "Pool to bracket"
|
||||
if @wrestler.pool_wins.size >= 1 and @wrestler.has_a_pool_bye == true
|
||||
if pool_bye_points_eligible?
|
||||
points += 2
|
||||
end
|
||||
end
|
||||
if @tournament.tournament_type.include? "Regular Double Elimination"
|
||||
if @wrestler.championship_advancement_wins.size > 0 or @wrestler.matches_won.select{|m| m.bracket_position == "1/2" and m.win_type != "BYE"}.size > 0
|
||||
# if they have a win in the championship round or if they got a bye all the way to finals and won the finals
|
||||
points += @wrestler.championship_byes.size * 2
|
||||
if @tournament.tournament_type.include? "Double Elimination"
|
||||
if @wrestler.championship_advancement_wins.any? &&
|
||||
@wrestler.championship_byes.any? &&
|
||||
any_bye_round_had_wrestled_match?(@wrestler.championship_byes)
|
||||
points += 2
|
||||
end
|
||||
if @wrestler.consolation_advancement_wins.size > 0 or @wrestler.matches_won.select{|m| m.bracket_position == "3/4" and m.win_type != "BYE"}.size > 0
|
||||
# if they have a win in the consolation round or if they got a bye all the way to 3rd/4th match and won
|
||||
points += @wrestler.consolation_byes.size * 1
|
||||
end
|
||||
end
|
||||
if @tournament.tournament_type.include? "Modified 16 Man Double Elimination"
|
||||
if @wrestler.championship_advancement_wins.size > 0 or @wrestler.matches_won.select{|m| m.bracket_position == "1/2" and m.win_type != "BYE"}.size > 0
|
||||
# if they have a win in the championship round or if they got a bye all the way to finals and won the finals
|
||||
points += @wrestler.championship_byes.size * 2
|
||||
end
|
||||
if @wrestler.consolation_advancement_wins.size > 0 or @wrestler.matches_won.select{|m| m.bracket_position == "5/6" and m.win_type != "BYE"}.size > 0
|
||||
# if they have a win in the consolation round or if they got a bye all the way to 5th/6th match and won
|
||||
# since the consolation bracket goes to 5/6 in a modified tournament
|
||||
points += @wrestler.consolation_byes.size * 1
|
||||
if @wrestler.consolation_advancement_wins.any? &&
|
||||
@wrestler.consolation_byes.any? &&
|
||||
any_bye_round_had_wrestled_match?(@wrestler.consolation_byes)
|
||||
points += 1
|
||||
end
|
||||
end
|
||||
return points
|
||||
@@ -86,4 +77,30 @@ class CalculateWrestlerTeamScore
|
||||
(@wrestler.pin_wins.size * 2) + (@wrestler.tech_wins.size * 1.5) + (@wrestler.major_wins.size * 1)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pool_bye_points_eligible?
|
||||
return false unless @wrestler.pool_wins.size >= 1
|
||||
return false unless @wrestler.weight.pools.to_i > 1
|
||||
|
||||
wrestler_pool_size = @wrestler.weight.wrestlers_in_pool(@wrestler.pool).size
|
||||
largest_pool_size = (1..@wrestler.weight.pools).map { |pool_number| @wrestler.weight.wrestlers_in_pool(pool_number).size }.max
|
||||
|
||||
wrestler_pool_size < largest_pool_size
|
||||
end
|
||||
|
||||
def any_bye_round_had_wrestled_match?(bye_matches)
|
||||
bye_matches.any? do |bye_match|
|
||||
next false if bye_match.round.nil?
|
||||
|
||||
@wrestler.weight.matches.any? do |match|
|
||||
next false if match.id == bye_match.id
|
||||
next false if match.round != bye_match.round
|
||||
next false if match.is_consolation_match != bye_match.is_consolation_match
|
||||
|
||||
match.finished == 1 && match.win_type.present? && match.win_type != "BYE"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ json.cache! ["api_tournament", @tournament] do
|
||||
|
||||
json.mats @mats do |mat|
|
||||
json.name mat.name
|
||||
json.unfinished_matches mat.unfinished_matches do |match|
|
||||
json.unfinished_matches mat.queue_matches.compact do |match|
|
||||
json.bout_number match.bout_number
|
||||
json.w1_name match.w1_name
|
||||
json.w2_name match.w2_name
|
||||
|
||||
@@ -38,9 +38,10 @@
|
||||
<li><strong>Pages</strong></li>
|
||||
<li></span> <%= link_to "Edit Tournament Info", edit_tournament_path(@tournament) %></li>
|
||||
<li><%= link_to "Weigh In Page" , "/tournaments/#{@tournament.id}/weigh_in" %></li>
|
||||
<li><%= link_to "All Matches" , "/tournaments/#{@tournament.id}/matches" %></li>
|
||||
<li><%= link_to "Full Screen Bout Board" , "/tournaments/#{@tournament.id}/up_matches?print=true" , target: :_blank %></li>
|
||||
<li><%= link_to "Deduct Team Points" , "/tournaments/#{@tournament.id}/teampointadjust" %></li>
|
||||
<li><%= link_to "All Matches" , "/tournaments/#{@tournament.id}/matches" %></li>
|
||||
<li><%= link_to "Full Screen Bout Board" , "/tournaments/#{@tournament.id}/up_matches?print=true" , target: :_blank %></li>
|
||||
<li><%= link_to "QR Code (Full Screen)" , "/tournaments/#{@tournament.id}/qrcode?print=true" , target: :_blank %></li>
|
||||
<li><%= link_to "Deduct Team Points" , "/tournaments/#{@tournament.id}/teampointadjust" %></li>
|
||||
<li><%= link_to "View All Mat Assignment Rules", tournament_mat_assignment_rules_path(@tournament) %></li>
|
||||
<li><%= link_to 'Manage Backups', tournament_tournament_backups_path(@tournament) %></li>
|
||||
<li><%= link_to "Reset Bout Board", reset_bout_board_tournament_path(@tournament), data: { turbo_method: :post, turbo_confirm: "Are you sure you want to reset the bout board?" } %></li>
|
||||
@@ -60,7 +61,7 @@
|
||||
<li><%= link_to "Create Boys Middle School Weights (80-245)" , "/tournaments/#{@tournament.id}/create_custom_weights?customValue=#{Weight::MS_WEIGHT_CLASSES}",data: { turbo_method: :get, turbo_confirm: 'Are you sure? This will delete all current weights.' } %></li>
|
||||
<li><%= link_to "Create Girls Middle School Weights (72-235)" , "/tournaments/#{@tournament.id}/create_custom_weights?customValue=#{Weight::MS_GIRLS_WEIGHT_CLASSES}",data: { turbo_method: :get, turbo_confirm: 'Are you sure? This will delete all current weights.' } %></li>
|
||||
<li><strong>Tournament Actions</strong></li>
|
||||
<li><%= link_to "Calculate Team Scores" , "/tournaments/#{@tournament.id}/calculate_team_scores", :method => :put %></li>
|
||||
<li><%= link_to "Calculate Team Scores" , "/tournaments/#{@tournament.id}/calculate_team_scores", data: { turbo_method: :put } %></li>
|
||||
<li><%= link_to "Generate Brackets" , "/tournaments/#{@tournament.id}/generate_matches", data: { turbo_method: :get, turbo_confirm: 'Are you sure? This will delete all current matches.' } %></li>
|
||||
<li><%= link_to "Export Data" , "/tournaments/#{@tournament.id}/export?print=true", target: :_blank %></li>
|
||||
</ul>
|
||||
|
||||
@@ -36,7 +36,11 @@
|
||||
|
||||
<div id="page-content">
|
||||
<div class="row">
|
||||
<div class="col-md-12"><%= render 'layouts/underheader' %></div>
|
||||
<div class="col-md-12">
|
||||
<% unless hide_ads? %>
|
||||
<%= render 'layouts/underheader' %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-margin">
|
||||
<div class="col-md-12" style="padding-left: 2%;">
|
||||
@@ -58,4 +62,3 @@
|
||||
</body>
|
||||
<% end %>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -18,8 +18,19 @@
|
||||
|
||||
<div id="cable-status-indicator" data-match-data-target="statusIndicator" class="alert alert-secondary" style="padding: 5px; margin-bottom: 10px; border-radius: 4px;"></div>
|
||||
<h4>Bout <strong><%= @match.bout_number %></strong></h4>
|
||||
<% if @show_next_bout_button && @next_match %>
|
||||
<%= link_to "Skip to Next Match for Mat #{@mat.name}", mat_path(@mat, bout_number: @next_match.bout_number), class: "btn btn-primary" %>
|
||||
<% if @mat %>
|
||||
<% queue_matches = @queue_matches || @mat.queue_matches %>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<% queue_matches.each_with_index do |queue_match, index| %>
|
||||
<% queue_label = "Queue #{index + 1}" %>
|
||||
<% if queue_match %>
|
||||
<% button_class = queue_match.id == @match.id ? "btn btn-success btn-sm" : "btn btn-primary btn-sm" %>
|
||||
<%= link_to "#{queue_label}: Bout #{queue_match.bout_number}", mat_path(@mat, bout_number: queue_match.bout_number), class: button_class %>
|
||||
<% else %>
|
||||
<button type="button" class="btn btn-default btn-sm" disabled><%= "#{queue_label}: Not assigned" %></button>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<h4>Bracket Position: <strong><%= @match.bracket_position %></strong></h4>
|
||||
|
||||
|
||||
34
app/views/matches/edit_assignment.html.erb
Normal file
34
app/views/matches/edit_assignment.html.erb
Normal file
@@ -0,0 +1,34 @@
|
||||
<h1>Assign Mat/Queue for Match <%= @match.bout_number %></h1>
|
||||
|
||||
<% if @current_mat %>
|
||||
<p>Current Assignment: Mat <%= @current_mat.name %><%= @current_queue_position ? " (Queue #{@current_queue_position})" : "" %></p>
|
||||
<% else %>
|
||||
<p>Current Assignment: Unassigned</p>
|
||||
<% end %>
|
||||
|
||||
<%= form_with model: @match, url: update_assignment_match_path(@match), method: :patch do |f| %>
|
||||
<div class="field">
|
||||
<%= f.label :mat_id, "Mat" %><br>
|
||||
<%= f.collection_select :mat_id, @mats, :id, :name, { include_blank: "Unassigned" } %>
|
||||
</div>
|
||||
<br>
|
||||
<div class="field">
|
||||
<%= f.label :queue_position, "Queue Position" %><br>
|
||||
<%= f.select :queue_position,
|
||||
options_for_select(
|
||||
[
|
||||
["On Mat (Queue 1)", 1],
|
||||
["On Deck (Queue 2)", 2],
|
||||
["In The Hole (Queue 3)", 3],
|
||||
["Warm Up (Queue 4)", 4]
|
||||
],
|
||||
@current_queue_position
|
||||
),
|
||||
include_blank: "Select position"
|
||||
%>
|
||||
</div>
|
||||
<br>
|
||||
<div class="actions">
|
||||
<%= f.submit "Update Assignment", class: "btn btn-success" %>
|
||||
</div>
|
||||
<% end %>
|
||||
39
app/views/mats/_current_match.html.erb
Normal file
39
app/views/mats/_current_match.html.erb
Normal file
@@ -0,0 +1,39 @@
|
||||
<% @mat = mat %>
|
||||
<% @queue_matches = local_assigns[:queue_matches] || mat.queue_matches %>
|
||||
<% @match = local_assigns[:match] || @queue_matches[0] %>
|
||||
<% @match ||= @queue_matches[0] %>
|
||||
<% @next_match = local_assigns[:next_match] || @queue_matches[1] %>
|
||||
<% @show_next_bout_button = local_assigns.key?(:show_next_bout_button) ? local_assigns[:show_next_bout_button] : true %>
|
||||
|
||||
<% @wrestlers = [] %>
|
||||
<% if @match %>
|
||||
<% if @match.w1 %>
|
||||
<% @wrestler1_name = @match.wrestler1.name %>
|
||||
<% @wrestler1_school_name = @match.wrestler1.school.name %>
|
||||
<% @wrestler1_last_match = @match.wrestler1.last_match %>
|
||||
<% @wrestlers.push(@match.wrestler1) %>
|
||||
<% else %>
|
||||
<% @wrestler1_name = "Not assigned" %>
|
||||
<% @wrestler1_school_name = "N/A" %>
|
||||
<% @wrestler1_last_match = nil %>
|
||||
<% end %>
|
||||
|
||||
<% if @match.w2 %>
|
||||
<% @wrestler2_name = @match.wrestler2.name %>
|
||||
<% @wrestler2_school_name = @match.wrestler2.school.name %>
|
||||
<% @wrestler2_last_match = @match.wrestler2.last_match %>
|
||||
<% @wrestlers.push(@match.wrestler2) %>
|
||||
<% else %>
|
||||
<% @wrestler2_name = "Not assigned" %>
|
||||
<% @wrestler2_school_name = "N/A" %>
|
||||
<% @wrestler2_last_match = nil %>
|
||||
<% end %>
|
||||
|
||||
<% @tournament = @match.tournament %>
|
||||
<% end %>
|
||||
|
||||
<% if @match %>
|
||||
<%= render "matches/matchstats" %>
|
||||
<% else %>
|
||||
<p>No matches assigned to this mat.</p>
|
||||
<% end %>
|
||||
@@ -1,9 +1,12 @@
|
||||
<h3>Mat <%= @mat.name %></h3>
|
||||
<h3>Tournament: <%= @mat.tournament.name %></h3>
|
||||
|
||||
<% if @match %>
|
||||
<%= render 'matches/matchstats' %>
|
||||
<% else %>
|
||||
<p>No matches assigned to this mat.</p>
|
||||
<% end %>
|
||||
<%= turbo_stream_from @mat %>
|
||||
|
||||
<%= turbo_frame_tag dom_id(@mat, :current_match) do %>
|
||||
<%= render "mats/current_match",
|
||||
mat: @mat,
|
||||
match: @match,
|
||||
next_match: @next_match,
|
||||
show_next_bout_button: @show_next_bout_button %>
|
||||
<% end %>
|
||||
|
||||
13
app/views/schools/_wrestler_row_cells.html.erb
Normal file
13
app/views/schools/_wrestler_row_cells.html.erb
Normal file
@@ -0,0 +1,13 @@
|
||||
<% if local_assigns[:school_permission_key].present? %>
|
||||
<% wrestler_path_with_key = wrestler_path(wrestler) %>
|
||||
<% wrestler_path_with_key += "?school_permission_key=#{school_permission_key}" %>
|
||||
<td><%= link_to wrestler.name, wrestler_path_with_key %></td>
|
||||
<% else %>
|
||||
<td><%= link_to wrestler.name, wrestler_path(wrestler) %></td>
|
||||
<% end %>
|
||||
<td><%= wrestler.weight.max %></td>
|
||||
<td><%= wrestler.season_win %>-<%= wrestler.season_loss %> <%= wrestler.criteria %></td>
|
||||
<td><%= wrestler.original_seed %></td>
|
||||
<td><%= wrestler.total_team_points - wrestler.total_points_deducted %></td>
|
||||
<td><%= "Yes" if wrestler.extra? %></td>
|
||||
<td><%= wrestler.next_match_bout_number %> <%= wrestler.next_match_mat_name %></td>
|
||||
@@ -54,19 +54,8 @@
|
||||
<tbody>
|
||||
<% @wrestlers.sort_by { |w| w.weight.max }.each do |wrestler| %>
|
||||
<% if params[:school_permission_key].present? %>
|
||||
<!-- No caching when school_permission_key is present -->
|
||||
<tr>
|
||||
<td>
|
||||
<% wrestler_path_with_key = wrestler_path(wrestler) %>
|
||||
<% wrestler_path_with_key += "?school_permission_key=#{params[:school_permission_key]}" if params[:school_permission_key].present? %>
|
||||
<%= link_to wrestler.name, wrestler_path_with_key %>
|
||||
</td>
|
||||
<td><%= wrestler.weight.max %></td>
|
||||
<td><%= wrestler.season_win %>-<%= wrestler.season_loss %> <%= wrestler.criteria %></td>
|
||||
<td><%= wrestler.original_seed %></td>
|
||||
<td><%= wrestler.total_team_points - wrestler.total_points_deducted %></td>
|
||||
<td><%= "Yes" if wrestler.extra? %></td>
|
||||
<td><%= wrestler.next_match_bout_number %> <%= wrestler.next_match_mat_name %></td>
|
||||
<%= render "schools/wrestler_row_cells", wrestler: wrestler, school_permission_key: params[:school_permission_key] %>
|
||||
|
||||
<% if can? :manage, wrestler.school %>
|
||||
<td>
|
||||
@@ -76,36 +65,31 @@
|
||||
<% delete_wrestler_path_with_key = wrestler_path(wrestler) %>
|
||||
<% delete_wrestler_path_with_key += "?school_permission_key=#{params[:school_permission_key]}" if params[:school_permission_key].present? %>
|
||||
|
||||
<%= link_to '', edit_wrestler_path_with_key, class: "fas fa-edit" %>
|
||||
<%= link_to '', delete_wrestler_path_with_key, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{wrestler.name}? This will delete all of his matches." }, class: "fas fa-trash-alt" %>
|
||||
<%= link_to edit_wrestler_path_with_key, class: "text-decoration-none" do %>
|
||||
<span class="fas fa-edit" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
<%= link_to delete_wrestler_path_with_key, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{wrestler.name}? This will delete all of his matches." }, class: "text-decoration-none" do %>
|
||||
<span class="fas fa-trash-alt" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% else %>
|
||||
<!-- Use caching only when school_permission_key is NOT present -->
|
||||
<% cache ["#{wrestler.id}_school_show", @school] do %>
|
||||
<tr>
|
||||
<td><%= link_to wrestler.name, wrestler_path(wrestler) %></td>
|
||||
<td><%= wrestler.weight.max %></td>
|
||||
<td><%= wrestler.season_win %>-<%= wrestler.season_loss %> <%= wrestler.criteria %></td>
|
||||
<td><%= wrestler.original_seed %></td>
|
||||
<td><%= wrestler.total_team_points - wrestler.total_points_deducted %></td>
|
||||
<td><%= "Yes" if wrestler.extra? %></td>
|
||||
<td><%= wrestler.next_match_bout_number %> <%= wrestler.next_match_mat_name %></td>
|
||||
<% end %>
|
||||
<% if can? :manage, wrestler.school %>
|
||||
<td>
|
||||
<% edit_wrestler_path_with_key = edit_wrestler_path(wrestler) %>
|
||||
<% edit_wrestler_path_with_key += "?school_permission_key=#{params[:school_permission_key]}" if params[:school_permission_key].present? %>
|
||||
|
||||
<% delete_wrestler_path_with_key = wrestler_path(wrestler) %>
|
||||
<% delete_wrestler_path_with_key += "?school_permission_key=#{params[:school_permission_key]}" if params[:school_permission_key].present? %>
|
||||
|
||||
<%= link_to '', edit_wrestler_path_with_key, class: "fas fa-edit" %>
|
||||
<%= link_to '', delete_wrestler_path_with_key, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{wrestler.name}? This will delete all of his matches." }, class: "fas fa-trash-alt" %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<tr>
|
||||
<% cache ["school_show_wrestler_cells", wrestler] do %>
|
||||
<%= render "schools/wrestler_row_cells", wrestler: wrestler %>
|
||||
<% end %>
|
||||
<% if can? :manage, wrestler.school %>
|
||||
<td>
|
||||
<%= link_to edit_wrestler_path(wrestler), class: "text-decoration-none" do %>
|
||||
<span class="fas fa-edit" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
<%= link_to wrestler_path(wrestler), data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{wrestler.name}? This will delete all of his matches." }, class: "text-decoration-none" do %>
|
||||
<span class="fas fa-trash-alt" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<li>Win by major: 1pt extra</li>
|
||||
<li>Win by tech fall: 1.5pt extra</li>
|
||||
<li>Win by fall, default, dq: 2pt extra</li>
|
||||
<li>BYE points: 2pt (if you win at least 1 match in a pool with a BYE)</li>
|
||||
<li>BYE points: 2pt (if you win at least 1 match in a pool with a BYE). - This only applies if your pool has more BYEs than other pools in your bracket. This does not apply to weight classes with 1 pool.</li>
|
||||
</ul>
|
||||
<p>See placement points below (based on the largest bracket of the tournament)</p>
|
||||
<h4>Pool Types</h4>
|
||||
@@ -71,7 +71,7 @@
|
||||
<li>Win by major: 1pt extra</li>
|
||||
<li>Win by tech: 1.5pt extra</li>
|
||||
<li>Win by fall, default, dq, etc: 2pt extra</li>
|
||||
<li>BYE points: 2pts if you have a bye in the championship bracket and win the next match. 1pt if you have a bye in the consolation bracket and win the next match.</li>
|
||||
<li>BYE points: 2pts if you have a bye in the championship bracket and win the next match. 1pt if you have a bye in the consolation bracket and win the next match. - This only applies if you received a bye in a round with at least 1 match in your backet.</li>
|
||||
</ul>
|
||||
<br>
|
||||
<h3>Modified 16 Man Double Elimination Information</h3>
|
||||
@@ -142,7 +142,7 @@
|
||||
<br>
|
||||
<h3>Future Plans</h3>
|
||||
<br>
|
||||
<p>Future development plans to support 32 and 64 man regulard double elimination, modified (5 per day match rule) 32 man double elimination, and true second double elimination brackets are underway.</p>
|
||||
<p>Future development plans are underway to make the application more flexible, make changes after weigh ins easier, and to add functionality for a live scoreboard.</p>
|
||||
<br>
|
||||
<h3>Contact</h3>
|
||||
<br>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
<% if @tournaments.size > 0 %>
|
||||
<h3>My Tournaments</h3>
|
||||
<script>
|
||||
// $(document).ready(function() {
|
||||
// $('#tournamentList').dataTable();
|
||||
// pagingType: "bootstrap";
|
||||
// } );
|
||||
</script>
|
||||
<table class="table table-hover" id="tournamentList">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -27,12 +27,12 @@ and will also delete all of your current data. It's best to use the create backu
|
||||
</tbody>
|
||||
</table>
|
||||
<br><br>
|
||||
<h3>Import Manual Backup</h3>
|
||||
<p>Paste the backup text here. Note, if this is formatted wrong, you'll need to restore a backup from above to fix it and you'll see an error in your background jobs.</p>
|
||||
<%= form_for(:tournament, url: import_manual_tournament_tournament_backups_path(@tournament)) do |f| %>
|
||||
<div class="field">
|
||||
<%= f.label 'Import text' %><br>
|
||||
<%= f.text_area :import_text, cols: "30", rows: "20" %>
|
||||
</div>
|
||||
<%= submit_tag "Import", class: "btn btn-success", data: { confirm: 'Are you sure? This will delete everything for the current tournament and restore it with the backup text pasted below.' } %>
|
||||
<% end %>
|
||||
<% if ENV["RAILS_ENV"] == "development" %>
|
||||
<%= form_for(:tournament, url: import_manual_tournament_tournament_backups_path(@tournament)) do |f| %>
|
||||
<div class="field">
|
||||
<%= f.label 'Import text' %><br>
|
||||
<%= f.text_area :import_text, cols: "30", rows: "20" %>
|
||||
</div>
|
||||
<%= submit_tag "Import", class: "btn btn-success", data: { turbo_confirm: 'Are you sure? This will delete everything for the current tournament and restore it with the backup text pasted below.' } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -1,9 +1,15 @@
|
||||
<% @final_match.each do |match| %>
|
||||
<div class="round">
|
||||
<div class="game">
|
||||
<div class="game-top "><%= match.w1_bracket_name.html_safe %> <span></span></div>
|
||||
<div class="bout-number"><p><%= link_to match.bout_number, spectate_match_path(match) %> <%= match.bracket_score_string %></p><p><%= @winner_place %> Place Winner</p></div>
|
||||
<div class="game-bottom "><%= match.w2_bracket_name.html_safe %><span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<% cache ["bracket_final_match", match, match.wrestler1, match.wrestler2, @winner_place, params[:print].to_s] do %>
|
||||
<div class="round">
|
||||
<div class="game">
|
||||
<div class="game-top "><%= match.w1_bracket_name.html_safe %> <span></span></div>
|
||||
<% if params[:print] %>
|
||||
<div class="bout-number"><p><%= match.bout_number %> <%= match.bracket_score_string %></p><p><%= @winner_place %> Place Winner</p></div>
|
||||
<% else %>
|
||||
<div class="bout-number"><p><%= link_to match.bout_number, spectate_match_path(match) %> <%= match.bracket_score_string %></p><p><%= @winner_place %> Place Winner</p></div>
|
||||
<% end %>
|
||||
<div class="game-bottom "><%= match.w2_bracket_name.html_safe %><span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
<style>
|
||||
table.smallText tr td { font-size: 10px; }
|
||||
table.smallText {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.smallText th,
|
||||
table.smallText td {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
/*
|
||||
* Bracket Layout Specifics
|
||||
*/
|
||||
.bracket {
|
||||
display: flex;
|
||||
font-size: 10px;
|
||||
font-size: 10.5px;
|
||||
gap: 2px;
|
||||
}
|
||||
.game {
|
||||
min-width: 150px;
|
||||
min-height: 50px;
|
||||
min-height: 58px;
|
||||
/*background-color: #ddd;*/
|
||||
border: 1px solid #ddd;
|
||||
margin: 5px;
|
||||
border: 1.5px solid #000; /* Dark border so boxes stay visible when printed */
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
/*.game:after {
|
||||
@@ -56,14 +64,15 @@ table.smallText tr td { font-size: 10px; }
|
||||
}
|
||||
|
||||
.game-top {
|
||||
border-bottom:1px solid #ddd;
|
||||
padding: 2px;
|
||||
min-height: 12px;
|
||||
border-bottom:1.5px solid #000;
|
||||
padding: 3px 4px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
.bout-number {
|
||||
text-align: center;
|
||||
/*padding-top: 15px;*/
|
||||
line-height: 1.35;
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
||||
/* Style links within bout-number like default links */
|
||||
@@ -77,15 +86,29 @@ table.smallText tr td { font-size: 10px; }
|
||||
}
|
||||
|
||||
.bracket-winner {
|
||||
border-bottom:1px solid #ddd;
|
||||
padding: 2px;
|
||||
min-height: 12px;
|
||||
border-bottom:1.5px solid #000;
|
||||
padding: 3px 4px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
.game-bottom {
|
||||
border-top:1px solid #ddd;
|
||||
padding: 2px;
|
||||
min-height: 12px;
|
||||
border-top:1.5px solid #000;
|
||||
padding: 3px 4px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.game,
|
||||
.game-top,
|
||||
.game-bottom,
|
||||
.bracket-winner,
|
||||
table.smallText,
|
||||
table.smallText th,
|
||||
table.smallText td {
|
||||
border-color: #000 !important;
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<% if @tournament.tournament_type == "Pool to bracket" %>
|
||||
@@ -95,8 +118,6 @@ table.smallText tr td { font-size: 10px; }
|
||||
<table class='smallText'>
|
||||
<tr>
|
||||
<td valign="top" style="padding: 10px;">
|
||||
<% @matches = @tournament.matches.select{|m| m.weight_id == @weight.id} %>
|
||||
<% @wrestlers = Wrestler.where(weight_id: @weight.id) %>
|
||||
<% @pools = @weight.pool_rounds(@matches) %>
|
||||
<%= render 'pool' %>
|
||||
</td>
|
||||
@@ -131,4 +152,4 @@ table.smallText tr td { font-size: 10px; }
|
||||
<% elsif @tournament.tournament_type.include? "Regular Double Elimination" %>
|
||||
<%= render 'double_elimination_bracket' %>
|
||||
<% end %>
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
<div class="round">
|
||||
<% @round_matches.sort_by{|m| m.bracket_position_number}.each do |match| %>
|
||||
<div class="game">
|
||||
<div class="game-top "><%= match.w1_bracket_name.html_safe %> <span></span></div>
|
||||
<div class="bout-number"><%= link_to match.bout_number, spectate_match_path(match) %> <%= match.bracket_score_string %> </div>
|
||||
<div class="game-bottom "><%= match.w2_bracket_name.html_safe %><span></span></div>
|
||||
</div>
|
||||
<% cache ["bracket_round_match", match, match.wrestler1, match.wrestler2, params[:print].to_s] do %>
|
||||
<div class="game">
|
||||
<div class="game-top "><%= match.w1_bracket_name.html_safe %> <span></span></div>
|
||||
<% if params[:print] %>
|
||||
<div class="bout-number"><%= match.bout_number %> <%= match.bracket_score_string %> </div>
|
||||
<% else %>
|
||||
<div class="bout-number"><%= link_to match.bout_number, spectate_match_path(match) %> <%= match.bracket_score_string %> </div>
|
||||
<% end %>
|
||||
<div class="bout-number">Round <%= match.round %></div>
|
||||
<div class="bout-number"><%= match.bracket_position %></div>
|
||||
<div class="game-bottom "><%= match.w2_bracket_name.html_safe %><span></span></div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
6
app/views/tournaments/_team_score_row.html.erb
Normal file
6
app/views/tournaments/_team_score_row.html.erb
Normal file
@@ -0,0 +1,6 @@
|
||||
<% cache ["team_score_row", school, rank] do %>
|
||||
<tr>
|
||||
<td><%= rank %>. <%= school.name %> (<%= school.abbreviation %>)</td>
|
||||
<td><%= school.page_score_string %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
38
app/views/tournaments/_up_matches_board.html.erb
Normal file
38
app/views/tournaments/_up_matches_board.html.erb
Normal file
@@ -0,0 +1,38 @@
|
||||
<div id="up_matches_board">
|
||||
<h3>Upcoming Matches</h3>
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mat </th>
|
||||
<th>On Mat</th>
|
||||
<th>On Deck</th>
|
||||
<th>In The Hole</th>
|
||||
<th>Warm Up</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% (local_assigns[:mats] || tournament.up_matches_mats).each do |m| %>
|
||||
<%= render "tournaments/up_matches_mat_row", mat: m %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
<h3>Matches not assigned</h3>
|
||||
<br>
|
||||
<table class="table table-striped table-bordered table-condensed" id="matchList">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Round</th>
|
||||
<th>Bout Number</th>
|
||||
<th>Weight Class</th>
|
||||
<th>Matchup</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<%= render partial: "tournaments/up_matches_unassigned_row", collection: (local_assigns[:matches] || tournament.up_matches_unassigned_matches), as: :match %>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
</div>
|
||||
35
app/views/tournaments/_up_matches_mat_row.html.erb
Normal file
35
app/views/tournaments/_up_matches_mat_row.html.erb
Normal file
@@ -0,0 +1,35 @@
|
||||
<% queue1_match, queue2_match, queue3_match, queue4_match = mat.queue_matches %>
|
||||
<% queue_match_dependencies = [queue1_match, queue2_match, queue3_match, queue4_match].compact.flat_map { |match| [match, match.wrestler1, match.wrestler2] } %>
|
||||
<% cache ["up_matches_mat_row", mat, *queue_match_dependencies] do %>
|
||||
<tr>
|
||||
<td><%= mat.name %></td>
|
||||
<td>
|
||||
<% if queue1_match %><strong><%= queue1_match.bout_number %></strong> (<%= queue1_match.bracket_position %>)<br>
|
||||
<%= queue1_match.weight_max %> lbs
|
||||
<br><%= queue1_match.w1_bracket_name %> vs. <br>
|
||||
<%= queue1_match.w2_bracket_name %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<% if queue2_match %><strong><%= queue2_match.bout_number %></strong> (<%= queue2_match.bracket_position %>)<br>
|
||||
<%= queue2_match.weight_max %> lbs
|
||||
<br><%= queue2_match.w1_bracket_name %> vs. <br>
|
||||
<%= queue2_match.w2_bracket_name %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<% if queue3_match %><strong><%= queue3_match.bout_number %></strong> (<%= queue3_match.bracket_position %>)<br>
|
||||
<%= queue3_match.weight_max %> lbs
|
||||
<br><%= queue3_match.w1_bracket_name %> vs. <br>
|
||||
<%= queue3_match.w2_bracket_name %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<% if queue4_match %><strong><%= queue4_match.bout_number %></strong> (<%= queue4_match.bracket_position %>)<br>
|
||||
<%= queue4_match.weight_max %> lbs
|
||||
<br><%= queue4_match.w1_bracket_name %> vs. <br>
|
||||
<%= queue4_match.w2_bracket_name %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
@@ -0,0 +1,8 @@
|
||||
<% cache ["up_matches_unassigned_row", match, match.wrestler1, match.wrestler2] do %>
|
||||
<tr>
|
||||
<td>Round <%= match.round %></td>
|
||||
<td><%= match.bout_number %></td>
|
||||
<td><%= match.weight_max %></td>
|
||||
<td><%= match.w1_bracket_name %> vs. <%= match.w2_bracket_name %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
@@ -1,20 +1,20 @@
|
||||
<style>
|
||||
/* General styles for pages */
|
||||
@page {
|
||||
margin: 0.5in; /* Universal margin for all pages */
|
||||
margin: 0.35in;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: 7.5in; /* Portrait width (8.5in - margins) */
|
||||
height: 10in; /* Portrait height (11in - margins) */
|
||||
width: 7.8in; /* 8.5in - 2 * 0.35in */
|
||||
height: 10.3in; /* 11in - 2 * 0.35in */
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.page-landscape {
|
||||
width: 10in; /* Landscape width (11in - margins) */
|
||||
height: 7.5in; /* Landscape height (8.5in - margins) */
|
||||
width: 10.3in; /* 11in - 2 * 0.35in */
|
||||
height: 7.8in; /* 8.5in - 2 * 0.35in */
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
@@ -26,6 +26,11 @@
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.bracket-container h4 {
|
||||
margin-top: 0.15rem;
|
||||
margin-bottom: 0.45rem;
|
||||
}
|
||||
|
||||
/* Print-specific styles */
|
||||
@media print {
|
||||
/* Set orientation for portrait pages */
|
||||
@@ -51,6 +56,10 @@
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.bracket {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
/* Optional: Hide elements not needed in print */
|
||||
.no-print {
|
||||
display: none;
|
||||
@@ -62,15 +71,10 @@
|
||||
function scaleContent() {
|
||||
document.querySelectorAll('.page, .page-landscape').forEach(page => {
|
||||
const container = page.querySelector('.bracket-container');
|
||||
const isLandscape = page.classList.contains('page-landscape');
|
||||
|
||||
// Page dimensions (1 inch = 96px)
|
||||
const pageWidth = isLandscape ? 10 * 96 : 7.5 * 96;
|
||||
const pageHeight = isLandscape ? 7.5 * 96 : 10 * 96;
|
||||
|
||||
// Subtract margins (0.5 inch margin)
|
||||
const availableWidth = pageWidth - (0.5 * 96 * 2);
|
||||
const availableHeight = pageHeight - (0.5 * 96 * 2);
|
||||
// Use the actual page box size (already accounts for @page margins)
|
||||
const availableWidth = page.clientWidth;
|
||||
const availableHeight = page.clientHeight;
|
||||
|
||||
// Measure content dimensions
|
||||
const contentWidth = container.scrollWidth;
|
||||
@@ -80,8 +84,8 @@
|
||||
const scaleX = availableWidth / contentWidth;
|
||||
const scaleY = availableHeight / contentHeight;
|
||||
|
||||
// Use a slightly relaxed scaling to avoid over-aggressive shrinking
|
||||
const scale = Math.min(scaleX, scaleY, 1); // Ensure scale does not exceed 100% (1)
|
||||
// Keep a tiny buffer so borders/text don't clip at print edges
|
||||
const scale = Math.min(scaleX, scaleY, 1) * 0.99;
|
||||
|
||||
// Apply the scale
|
||||
container.style.transform = `scale(${scale})`;
|
||||
@@ -91,9 +95,9 @@
|
||||
const scaledWidth = contentWidth * scale;
|
||||
const scaledHeight = contentHeight * scale;
|
||||
|
||||
// Center the content within the page
|
||||
const horizontalPadding = (pageWidth - scaledWidth) / 2;
|
||||
const verticalPadding = (pageHeight - scaledHeight) / 2;
|
||||
// Center the content within the available page box
|
||||
const horizontalPadding = (availableWidth - scaledWidth) / 2;
|
||||
const verticalPadding = (availableHeight - scaledHeight) / 2;
|
||||
|
||||
// Apply margin adjustments
|
||||
container.style.marginLeft = `${Math.max(0, horizontalPadding)}px`;
|
||||
@@ -119,16 +123,17 @@
|
||||
<% @weights.sort_by{|w| w.max}.each do |weight| %>
|
||||
<% if @tournament.tournament_type == "Pool to bracket" %>
|
||||
<!-- Need to define what the tournaments#bracket controller defines -->
|
||||
<% @matches = @tournament.matches.select{|m| m.weight_id == weight.id} %>
|
||||
<% @wrestlers = Wrestler.where(weight_id: weight.id) %>
|
||||
<% @matches = @matches_by_weight_id[weight.id] || [] %>
|
||||
<% @wrestlers = @wrestlers_by_weight_id[weight.id] || [] %>
|
||||
<% @pools = weight.pool_rounds(@matches) %>
|
||||
<% @weight = weight %>
|
||||
<%= render 'bracket_partial' %>
|
||||
<% elsif @tournament.tournament_type.include? "Modified 16 Man Double Elimination" or @tournament.tournament_type.include? "Regular Double Elimination" %>
|
||||
<!-- Need to define what the tournaments#bracket controller defines -->
|
||||
<% @matches = weight.matches %>
|
||||
<% @matches = @matches_by_weight_id[weight.id] || [] %>
|
||||
<% @wrestlers = @wrestlers_by_weight_id[weight.id] || [] %>
|
||||
<% @weight = weight %>
|
||||
<%= render 'bracket_partial' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -54,28 +54,28 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<% @matches.each do |match| %>
|
||||
<% if match.w1 && match.w2 %>
|
||||
<% w1 = Wrestler.find(match.w1) %>
|
||||
<% w2 = Wrestler.find(match.w2) %>
|
||||
<% end %>
|
||||
|
||||
<div class="pagebreak">
|
||||
<p><strong>Bout Number:</strong> <%= match.bout_number %> <strong>Weight Class:</strong> <%= match.weight.max %> <strong>Round:</strong> <%= match.round %> <strong>Bracket Position:</strong> <%= match.bracket_position %></p>
|
||||
<p><strong>Key: </strong>Takedown: T3, Escape: E1, Reversal: R2, Nearfall: N2 or N3 or N4, Stalling: S, Caution: C, Penalty Point: P1</p>
|
||||
<% @matches.each do |match| %>
|
||||
<% w1 = @wrestlers_by_id[match.w1] %>
|
||||
<% w2 = @wrestlers_by_id[match.w2] %>
|
||||
<% w1_name = w1&.name || match.loser1_name %>
|
||||
<% w2_name = w2&.name || match.loser2_name %>
|
||||
|
||||
<div class="pagebreak">
|
||||
<p><strong>Bout Number:</strong> <%= match.bout_number %> <strong>Weight Class:</strong> <%= match.weight.max %> <strong>Round:</strong> <%= match.round %> <strong>Bracket Position:</strong> <%= match.bracket_position %></p>
|
||||
<p><strong>Key: </strong>Takedown: T3, Escape: E1, Reversal: R2, Nearfall: N2 or N3 or N4, Stalling: S, Caution: C, Penalty Point: P1</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr class="small-row">
|
||||
<th class="fixed-width">Circle Winner</th>
|
||||
<th>
|
||||
<p><%= match.w1_name %>-<%= w1&.school&.name %></p>
|
||||
</th>
|
||||
<th>
|
||||
<p><%= match.w2_name %>-<%= w2&.school&.name %></p>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<th class="fixed-width">Circle Winner</th>
|
||||
<th>
|
||||
<p><%= w1_name %>-<%= w1&.school&.name %></p>
|
||||
</th>
|
||||
<th>
|
||||
<p><%= w2_name %>-<%= w2&.school&.name %></p>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="small-row">
|
||||
<td class="fixed-width"></td>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<% cache ["#{@weight.id}_bracket", @weight] do %>
|
||||
<% cache ["#{@weight.id}_bracket", @weight, params[:print].to_s] do %>
|
||||
<%= render 'bracket_partial' %>
|
||||
<% end %>
|
||||
<% if @tournament.tournament_type == "Pool to bracket" %>
|
||||
<%= render 'pool_bracket_director_actions' %>
|
||||
<% elsif @tournament.tournament_type.include? "Modified 16 Man Double Elimination" or @tournament.tournament_type.include? "Regular Double Elimination" %>
|
||||
<%= render 'bracket_director_actions' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
{
|
||||
"tournament": {
|
||||
"attributes": <%= @tournament.attributes.to_json %>,
|
||||
<%
|
||||
wrestlers_by_id = @tournament.wrestlers.index_by(&:id)
|
||||
weights_by_id = @tournament.weights.index_by(&:id)
|
||||
mats_by_id = @tournament.mats.index_by(&:id)
|
||||
sorted_matches = @tournament.matches.sort_by(&:bout_number)
|
||||
%>
|
||||
{
|
||||
"tournament": {
|
||||
"attributes": <%= @tournament.attributes.to_json %>,
|
||||
"schools": <%= @tournament.schools.map(&:attributes).to_json %>,
|
||||
"weights": <%= @tournament.weights.map(&:attributes).to_json %>,
|
||||
"mats": <%= @tournament.mats.map(&:attributes).to_json %>,
|
||||
"mats": <%= @tournament.mats.map { |mat| mat.attributes.merge(
|
||||
{
|
||||
"queue_bout_numbers": mat.queue_matches.map { |match| match&.bout_number }
|
||||
}
|
||||
) }.to_json %>,
|
||||
"wrestlers": <%= @tournament.wrestlers.map { |wrestler| wrestler.attributes.merge(
|
||||
{
|
||||
"school": wrestler.school&.attributes,
|
||||
"weight": wrestler.weight&.attributes
|
||||
}
|
||||
) }.to_json %>,
|
||||
"matches": <%= @tournament.matches.sort_by(&:bout_number).map { |match| match.attributes.merge(
|
||||
{
|
||||
"w1_name": Wrestler.find_by(id: match.w1)&.name,
|
||||
"w2_name": Wrestler.find_by(id: match.w2)&.name,
|
||||
"winner_name": Wrestler.find_by(id: match.winner_id)&.name,
|
||||
"weight": Weight.find_by(id: match.weight_id)&.attributes,
|
||||
"mat": Mat.find_by(id: match.mat_id)&.attributes
|
||||
}
|
||||
) }.to_json %>
|
||||
}
|
||||
}
|
||||
"matches": <%= sorted_matches.map { |match| match.attributes.merge(
|
||||
{
|
||||
"w1_name": wrestlers_by_id[match.w1]&.name,
|
||||
"w2_name": wrestlers_by_id[match.w2]&.name,
|
||||
"winner_name": wrestlers_by_id[match.winner_id]&.name,
|
||||
"weight": weights_by_id[match.weight_id]&.attributes,
|
||||
"mat": mats_by_id[match.mat_id]&.attributes
|
||||
}
|
||||
) }.to_json %>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
|
||||
|
||||
<h1>All <%= @tournament.name %> matches</h1>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#matchesList').dataTable();
|
||||
pagingType: "bootstrap";
|
||||
} );
|
||||
</script>
|
||||
</br>
|
||||
|
||||
<% matches_path = "/tournaments/#{@tournament.id}/matches" %>
|
||||
|
||||
<%= form_tag(matches_path, method: :get, id: "search-form") do %>
|
||||
<%= text_field_tag :search, params[:search], placeholder: "Search wrestler, school, or bout #" %>
|
||||
<%= submit_tag "Search" %>
|
||||
<% end %>
|
||||
|
||||
<p>Search by wrestler name, school name, or bout number.</p>
|
||||
|
||||
<br>
|
||||
<table class="table table-striped table-bordered table-condensed" id="matchesList">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -28,9 +30,56 @@
|
||||
<td><%= match.finished %></td>
|
||||
<td><%= link_to 'Show', match, :class=>"btn btn-default btn-sm" %>
|
||||
<%= link_to 'Edit Wrestlers', edit_match_path(match), :class=>"btn btn-primary btn-sm" %>
|
||||
<%= link_to 'Edit Mat/Queue', edit_assignment_match_path(match), :class=>"btn btn-primary btn-sm" %>
|
||||
<%= link_to 'Stat Match', "/matches/#{match.id}/stat", :class=>"btn btn-primary btn-sm" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
|
||||
<% if @total_pages.present? && @total_pages > 1 %>
|
||||
<nav aria-label="Matches pagination">
|
||||
<ul class="pagination">
|
||||
<% if @page > 1 %>
|
||||
<li class="page-item">
|
||||
<%= link_to "Previous", { controller: "tournaments", action: "matches", id: @tournament.id, page: @page - 1, search: params[:search] }, class: "page-link" %>
|
||||
</li>
|
||||
<% else %>
|
||||
<li class="page-item disabled"><span class="page-link">Previous</span></li>
|
||||
<% end %>
|
||||
|
||||
<% window = 5
|
||||
left = [1, @page - window / 2].max
|
||||
right = [@total_pages, left + window - 1].min
|
||||
left = [1, right - window + 1].max
|
||||
%>
|
||||
<% (left..right).each do |p| %>
|
||||
<% if p == @page %>
|
||||
<li class="page-item active"><span class="page-link"><%= p %></span></li>
|
||||
<% else %>
|
||||
<li class="page-item"><%= link_to p, { controller: "tournaments", action: "matches", id: @tournament.id, page: p, search: params[:search] }, class: "page-link" %></li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @page < @total_pages %>
|
||||
<li class="page-item">
|
||||
<%= link_to "Next", { controller: "tournaments", action: "matches", id: @tournament.id, page: @page + 1, search: params[:search] }, class: "page-link" %>
|
||||
</li>
|
||||
<% else %>
|
||||
<li class="page-item disabled"><span class="page-link">Next</span></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<p class="text-muted">
|
||||
<% start_index = ((@page - 1) * @per_page) + 1
|
||||
end_index = [@page * @per_page, @total_count].min
|
||||
%>
|
||||
Showing <%= start_index %> - <%= end_index %> of <%= @total_count %> matches
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<br>
|
||||
<p>Total matches without byes: <%= @matches_without_byes_count %></p>
|
||||
<p>Unfinished matches: <%= @unfinished_matches_without_byes_count %></p>
|
||||
|
||||
50
app/views/tournaments/qrcode.html.erb
Normal file
50
app/views/tournaments/qrcode.html.erb
Normal file
@@ -0,0 +1,50 @@
|
||||
<style>
|
||||
.qr-page {
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.qr-page h1 {
|
||||
margin: 0 0 24px 0;
|
||||
font-size: 40px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.qr-code svg {
|
||||
width: min(80vmin, 720px);
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="qr-page">
|
||||
<h1><%= @tournament.name %> Brackets and Results Available Here</h1>
|
||||
<div class="qr-code">
|
||||
<%= raw @qrcode.as_svg(
|
||||
offset: 0,
|
||||
color: "000",
|
||||
shape_rendering: "crispEdges",
|
||||
module_size: 8,
|
||||
standalone: true
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,9 +70,13 @@
|
||||
</td>
|
||||
<td>
|
||||
<% if can? :manage, school %>
|
||||
<%= link_to '', edit_school_path(school), :class=>"fas fa-edit" %>
|
||||
<%= link_to edit_school_path(school), class: "text-decoration-none" do %>
|
||||
<span class="fas fa-edit" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
<% if can? :manage, @tournament %>
|
||||
<%= link_to '', school, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{school.name}?" }, :class=>"fas fa-trash-alt" %>
|
||||
<%= link_to school, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{school.name}?" }, class: "text-decoration-none" do %>
|
||||
<span class="fas fa-trash-alt" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</td>
|
||||
@@ -105,8 +109,12 @@
|
||||
<td><%= weight.bracket_size %></td>
|
||||
<% if can? :manage, @tournament %>
|
||||
<td>
|
||||
<%= link_to '', edit_weight_path(weight), :class=>"fas fa-edit" %>
|
||||
<%= link_to '', weight, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete the #{weight.max} weight class?" }, :class=>"fas fa-trash-alt" %>
|
||||
<%= link_to edit_weight_path(weight), class: "text-decoration-none" do %>
|
||||
<span class="fas fa-edit" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
<%= link_to weight, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete the #{weight.max} weight class?" }, class: "text-decoration-none" do %>
|
||||
<span class="fas fa-trash-alt" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
@@ -130,7 +138,9 @@
|
||||
<td><%= link_to "Mat #{mat.name}", mat %></td>
|
||||
<% if can? :manage, @tournament %>
|
||||
<td>
|
||||
<%= link_to '', mat, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete Mat #{mat.name}?" }, :class=>"fas fa-trash-alt" %>
|
||||
<%= link_to mat, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete Mat #{mat.name}?" }, class: "text-decoration-none" do %>
|
||||
<span class="fas fa-trash-alt" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
<%= link_to '', "/mats/#{mat.id}/assign_next_match", data: { turbo_method: :post }, :class=>"fas fa-solid fa-arrow-right" %>
|
||||
</td>
|
||||
<% end %>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
<% cache ["#{@tournament.id}_team_scores", @tournament] do %>
|
||||
|
||||
<% team_scores_last_updated = @schools.map(&:updated_at).compact.max&.utc&.to_fs(:nsec) %>
|
||||
<% cache ["team_scores", @tournament.id, @schools.size, team_scores_last_updated] do %>
|
||||
<table class="pagebreak table table-striped table-bordered">
|
||||
<h3>Team Scores</h3>
|
||||
<thead>
|
||||
@@ -11,12 +10,9 @@
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% @schools.each do |school| %>
|
||||
<tr>
|
||||
<td><%= @schools.index(school) + 1 %>. <%= school.name %> (<%= school.abbreviation %>)</td>
|
||||
<td><%= school.page_score_string %></td>
|
||||
</tr>
|
||||
<% @schools.each_with_index do |school, index| %>
|
||||
<%= render "tournaments/team_score_row", school: school, rank: index + 1 %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,67 +1,19 @@
|
||||
<% cache ["#{@tournament.id}_up_matches", @tournament] do %>
|
||||
<script>
|
||||
// $(document).ready(function() {
|
||||
// $('#matchList').dataTable();
|
||||
// } );
|
||||
</script>
|
||||
<script>
|
||||
setTimeout("location.reload(true);",30000);
|
||||
</script>
|
||||
<br>
|
||||
<br>
|
||||
<h5 style="color:red">This page reloads every 30s</h5>
|
||||
<br>
|
||||
<h3>Upcoming Matches</h3>
|
||||
<br>
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mat </th>
|
||||
<th>On Mat</th>
|
||||
<th>On Deck</th>
|
||||
<th>In The Hole</th>
|
||||
<th>Warm Up</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<div data-controller="up-matches-connection">
|
||||
<% if params[:print] != "true" %>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<%= link_to "Show Bout Board in Full Screen", up_matches_path(@tournament, print: true), class: "btn btn-primary" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<tbody>
|
||||
<% @mats.each.map do |m| %>
|
||||
<tr>
|
||||
<td><%= m.name %></td>
|
||||
<td><% if m.unfinished_matches.first %><strong><%=m.unfinished_matches.first.bout_number%></strong> - <%= m.unfinished_matches.first.weight_max %><br><%= m.unfinished_matches.first.w1_bracket_name %> vs. <%= m.unfinished_matches.first.w2_bracket_name %><% end %></td>
|
||||
<td><% if m.unfinished_matches.second %><strong><%=m.unfinished_matches.second.bout_number%></strong> - <%= m.unfinished_matches.second.weight_max %><br><%= m.unfinished_matches.second.w1_bracket_name %> vs. <%= m.unfinished_matches.second.w2_bracket_name %><% end %></td>
|
||||
<td><% if m.unfinished_matches.third %><strong><%=m.unfinished_matches.third.bout_number%></strong> - <%= m.unfinished_matches.third.weight_max %><br><%= m.unfinished_matches.third.w1_bracket_name %> vs. <%= m.unfinished_matches.third.w2_bracket_name %><% end %></td>
|
||||
<td><% if m.unfinished_matches.fourth %><strong><%=m.unfinished_matches.fourth.bout_number%></strong> - <%= m.unfinished_matches.fourth.weight_max %><br><%= m.unfinished_matches.fourth.w1_bracket_name %> vs. <%= m.unfinished_matches.fourth.w2_bracket_name %><% end %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
<h3>Matches not assigned</h3>
|
||||
<br>
|
||||
<table class="table table-striped table-bordered table-condensed" id="matchList">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Round</th>
|
||||
<th>Bout Number</th>
|
||||
<th>Weight Class</th>
|
||||
<th>Matchup</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<%= turbo_stream_from @tournament, data: { up_matches_connection_target: "stream" } %>
|
||||
<div
|
||||
id="up-matches-cable-status-indicator"
|
||||
data-up-matches-connection-target="statusIndicator"
|
||||
class="alert alert-secondary"
|
||||
style="padding: 5px; margin-bottom: 10px; border-radius: 4px;"
|
||||
>
|
||||
Connecting to server for real-time up matches updates...
|
||||
</div>
|
||||
|
||||
<tbody>
|
||||
<% if @matches.size > 0 %>
|
||||
<% @matches.each.map do |m| %>
|
||||
<tr>
|
||||
<td>Round <%= m.round %></td>
|
||||
<td><%= m.bout_number %></td>
|
||||
<td><%= m.weight_max %></td>
|
||||
<td><%= m.w1_bracket_name %> vs. <%= m.w2_bracket_name %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
<% end %>
|
||||
<%= render "up_matches_board", tournament: @tournament, mats: @mats, matches: @matches %>
|
||||
</div>
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
height: 1in;
|
||||
}
|
||||
</style>
|
||||
<% @tournament.schools.each do |school| %>
|
||||
<% @schools.each do |school| %>
|
||||
<table class="table table-striped table-bordered table-condensed pagebreak">
|
||||
<h5><%= school.name %></h4>
|
||||
<p><strong>Weigh In Ref:</strong> <%= @tournament.weigh_in_ref %></p>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
@@ -27,9 +28,9 @@
|
||||
<tr>
|
||||
<td><%= wrestler.name %></td>
|
||||
<td><%= wrestler.weight.max %></td>
|
||||
<td></td>
|
||||
<td><%= wrestler.offical_weight %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<table class="table table-striped table-bordered">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
@@ -9,19 +9,19 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%= form_tag @wrestlers_update_path do %>
|
||||
<% @wrestlers.order("original_seed asc").each do |wrestler| %>
|
||||
<% if wrestler.weight_id == @weight.id %>
|
||||
<tr>
|
||||
<%= form_tag "/tournaments/#{@tournament.id}/weigh_in/#{@weight.id}", method: :post do %>
|
||||
<% @wrestlers.order("original_seed asc").each do |wrestler| %>
|
||||
<% if wrestler.weight_id == @weight.id %>
|
||||
<tr>
|
||||
<td><%= wrestler.name %></td>
|
||||
<td><%= wrestler.school.name %></td>
|
||||
<td><%= wrestler.original_seed %></td>
|
||||
<td><%= wrestler.weight.max %></td>
|
||||
<td>
|
||||
<% if user_signed_in? %>
|
||||
<%= fields_for "wrestler[]", wrestler do |w| %>
|
||||
<%= w.number_field :offical_weight, :step => 'any' %>
|
||||
<% end %>
|
||||
<% if user_signed_in? %>
|
||||
<%= fields_for "wrestler[#{wrestler.id}]", wrestler do |w| %>
|
||||
<%= w.number_field :offical_weight, :step => 'any' %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= wrestler.offical_weight %>
|
||||
<% end %>
|
||||
|
||||
10
app/views/weights/_readonly_wrestler_row.html.erb
Normal file
10
app/views/weights/_readonly_wrestler_row.html.erb
Normal file
@@ -0,0 +1,10 @@
|
||||
<% cache ["weight_show_wrestler_row", wrestler] do %>
|
||||
<tr>
|
||||
<td><%= link_to wrestler.name, wrestler %></td>
|
||||
<td><%= wrestler.school.name %></td>
|
||||
<td><%= wrestler.original_seed %></td>
|
||||
<td><%= wrestler.season_win %>-<%= wrestler.season_loss %></td>
|
||||
<td><%= wrestler.criteria %> Win <%= wrestler.season_win_percentage %>%</td>
|
||||
<td><%= "Yes" if wrestler.extra? %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
@@ -1,48 +1,51 @@
|
||||
<h3>Weight Class:<%= @weight.max %> <% if can? :manage, @tournament %><%= link_to " Edit", edit_weight_path(@weight), :class=>"fas fa-edit" %><% end %></h3>
|
||||
<h3>Weight Class:<%= @weight.max %> <% if can? :manage, @tournament %><%= link_to edit_weight_path(@weight), class: "text-decoration-none" do %><span class="fas fa-edit" aria-hidden="true"></span><% end %><% end %></h3>
|
||||
<br>
|
||||
<br>
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>School</th>
|
||||
<th>Seed</th>
|
||||
<th>Record</th>
|
||||
<th>Seed Criteria</th>
|
||||
<th>Extra?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<%= form_tag @wrestlers_update_path do %>
|
||||
<% @wrestlers.sort_by{|w| [w.original_seed ? 0 : 1, w.original_seed || 0]}.each do |wrestler| %>
|
||||
<% if wrestler.weight_id == @weight.id %>
|
||||
<tr>
|
||||
<td><%= link_to "#{wrestler.name}", wrestler %></td>
|
||||
<td><%= wrestler.school.name %></td>
|
||||
<td>
|
||||
<% if can? :manage, @tournament %>
|
||||
<%= fields_for "wrestler[]", wrestler do |w| %>
|
||||
<%= w.text_field :original_seed %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= wrestler.original_seed %>
|
||||
<%= form_tag @wrestlers_update_path do %>
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>School</th>
|
||||
<th>Seed</th>
|
||||
<th>Record</th>
|
||||
<th>Seed Criteria</th>
|
||||
<th>Extra?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% sorted_wrestlers = @wrestlers.sort_by{|w| [w.original_seed ? 0 : 1, w.original_seed || 0]} %>
|
||||
<% if can? :manage, @tournament %>
|
||||
<% sorted_wrestlers.each do |wrestler| %>
|
||||
<% if wrestler.weight_id == @weight.id %>
|
||||
<tr>
|
||||
<td><%= link_to wrestler.name, wrestler %></td>
|
||||
<td><%= wrestler.school.name %></td>
|
||||
<td>
|
||||
<%= fields_for "wrestler[]", wrestler do |w| %>
|
||||
<%= w.text_field :original_seed %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td><%= wrestler.season_win %>-<%= wrestler.season_loss %></td>
|
||||
<td><%= wrestler.criteria %> Win <%= wrestler.season_win_percentage %>%</td>
|
||||
<td><% if wrestler.extra? == true %>
|
||||
Yes
|
||||
<% end %></td>
|
||||
<% if can? :manage, @tournament %>
|
||||
<td>
|
||||
<%= link_to '', wrestler, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{wrestler.name}? THIS WILL DELETE ALL MATCHES." } , :class=>"fas fa-trash-alt" %>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<td><%= wrestler.season_win %>-<%= wrestler.season_loss %></td>
|
||||
<td><%= wrestler.criteria %> Win <%= wrestler.season_win_percentage %>%</td>
|
||||
<td><%= "Yes" if wrestler.extra? %></td>
|
||||
<td>
|
||||
<%= link_to wrestler, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{wrestler.name}? THIS WILL DELETE ALL MATCHES." }, class: "text-decoration-none" do %>
|
||||
<span class="fas fa-trash-alt" aria-hidden="true"></span>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<% sorted_wrestlers.each do |wrestler| %>
|
||||
<% if wrestler.weight_id == @weight.id %>
|
||||
<%= render "weights/readonly_wrestler_row", wrestler: wrestler %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
<br><p>*All wrestlers without a seed (determined by tournament director) will be assigned a random bracket line.</p>
|
||||
<% if can? :manage, @tournament %>
|
||||
<br>
|
||||
|
||||
6
bin/bundler-audit
Executable file
6
bin/bundler-audit
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env ruby
|
||||
require_relative "../config/boot"
|
||||
require "bundler/audit/cli"
|
||||
|
||||
ARGV.concat %w[ --config config/bundler-audit.yml ] if ARGV.empty? || ARGV.include?("check")
|
||||
Bundler::Audit::CLI.start
|
||||
6
bin/ci
Executable file
6
bin/ci
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env ruby
|
||||
require_relative "../config/boot"
|
||||
require "active_support/continuous_integration"
|
||||
|
||||
CI = ActiveSupport::ContinuousIntegration
|
||||
require_relative "../config/ci.rb"
|
||||
@@ -2,7 +2,7 @@
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
# explicit rubocop config increases performance slightly while avoiding config confusion.
|
||||
# Explicit RuboCop config increases performance slightly while avoiding config confusion.
|
||||
ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
|
||||
|
||||
load Gem.bin_path("rubocop", "rubocop")
|
||||
|
||||
@@ -4,5 +4,5 @@ project_dir="$(dirname $( dirname $(readlink -f ${BASH_SOURCE[0]})))"
|
||||
cd ${project_dir}
|
||||
bundle exec rake db:migrate RAILS_ENV=test
|
||||
CI=true brakeman
|
||||
bundle exec bundle-audit check --update
|
||||
bundle exec rake test
|
||||
bundle audit
|
||||
rails test -v
|
||||
|
||||
@@ -22,6 +22,7 @@ FileUtils.chdir APP_ROOT do
|
||||
|
||||
puts "\n== Preparing database =="
|
||||
system! "bin/rails db:prepare"
|
||||
system! "bin/rails db:reset" if ARGV.include?("--reset")
|
||||
|
||||
puts "\n== Removing old logs and tempfiles =="
|
||||
system! "bin/rails log:clear tmp:clear"
|
||||
|
||||
@@ -25,9 +25,6 @@ module Wrestling
|
||||
config.active_record.schema_format = :ruby
|
||||
config.active_record.dump_schemas = :all
|
||||
|
||||
# Fix deprecation warning for to_time in Rails 8.1
|
||||
config.active_support.to_time_preserves_timezone = :zone
|
||||
|
||||
# Please, add to the `ignore` list any other `lib` subdirectories that do
|
||||
# not contain `.rb` files, or that should not be reloaded or eager loaded.
|
||||
# Common ones are `templates`, `generators`, or `middleware`, for example.
|
||||
@@ -56,6 +53,6 @@ module Wrestling
|
||||
# Valid values are 7.0 or 7.1
|
||||
config.active_support.cache_format_version = 7.1
|
||||
|
||||
config.load_defaults 8.0
|
||||
config.load_defaults 8.1
|
||||
end
|
||||
end
|
||||
|
||||
5
config/bundler-audit.yml
Normal file
5
config/bundler-audit.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
# Audit all gems listed in the Gemfile for known security problems by running bin/bundler-audit.
|
||||
# CVEs that are not relevant to the application can be enumerated on the ignore list below.
|
||||
|
||||
ignore:
|
||||
- CVE-THAT-DOES-NOT-APPLY
|
||||
@@ -1,9 +1,10 @@
|
||||
# Async adapter only works within the same process, so for manually triggering cable updates from a console,
|
||||
# and seeing results in the browser, you must do so from the web console (running inside the dev process),
|
||||
# not a terminal started via bin/rails console! Add "console" to any action or any ERB template view
|
||||
# to make the web console appear.
|
||||
development:
|
||||
adapter: async
|
||||
adapter: solid_cable
|
||||
connects_to:
|
||||
database:
|
||||
writing: cable
|
||||
polling_interval: 0.1.seconds
|
||||
message_retention: 1.day
|
||||
|
||||
test:
|
||||
adapter: test
|
||||
|
||||
24
config/ci.rb
Normal file
24
config/ci.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# Run using bin/ci
|
||||
|
||||
CI.run do
|
||||
step "Setup", "bin/setup --skip-server"
|
||||
|
||||
step "Style: Ruby", "bin/rubocop"
|
||||
|
||||
step "Security: Gem audit", "bin/bundler-audit"
|
||||
step "Security: Importmap vulnerability audit", "bin/importmap audit"
|
||||
step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
|
||||
step "Tests: Rails", "bin/rails test"
|
||||
step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant"
|
||||
|
||||
# Optional: Run system tests
|
||||
# step "Tests: System", "bin/rails test:system"
|
||||
|
||||
# Optional: set a green GitHub commit status to unblock PR merge.
|
||||
# Requires the `gh` CLI and `gh extension install basecamp/gh-signoff`.
|
||||
# if success?
|
||||
# step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff"
|
||||
# else
|
||||
# failure "Signoff: CI failed. Do not merge or deploy.", "Fix the issues and try again."
|
||||
# end
|
||||
end
|
||||
@@ -11,9 +11,8 @@ pin "@rails/actioncable", to: "actioncable.esm.js" # For Action Cable
|
||||
# and pin it directly, e.g., pin "jquery", to: "jquery.min.js"
|
||||
pin "jquery", to: "jquery.js"
|
||||
|
||||
# Pin Bootstrap and DataTables from vendor/assets/javascripts/
|
||||
# Pin Bootstrap from vendor/assets/javascripts/
|
||||
pin "bootstrap", to: "bootstrap.min.js"
|
||||
pin "datatables.net", to: "jquery.dataTables.min.js" # Assuming this is how you want to import it
|
||||
|
||||
# If Bootstrap requires Popper.js, and you have it in vendor/assets/javascripts/
|
||||
# pin "@popperjs/core", to: "popper.min.js" # Or the actual filename if different
|
||||
@@ -22,4 +21,4 @@ pin "datatables.net", to: "jquery.dataTables.min.js" # Assuming this is how you
|
||||
pin_all_from "app/assets/javascripts/controllers", under: "controllers"
|
||||
|
||||
# Pin all JS files from app/assets/javascripts directory
|
||||
pin_all_from "app/assets/javascripts", under: "assets/javascripts"
|
||||
pin_all_from "app/assets/javascripts", under: "assets/javascripts"
|
||||
|
||||
@@ -10,6 +10,8 @@ Wrestling::Application.routes.draw do
|
||||
member do
|
||||
get :stat
|
||||
get :spectate
|
||||
get :edit_assignment
|
||||
patch :update_assignment
|
||||
end
|
||||
end
|
||||
|
||||
@@ -71,6 +73,7 @@ Wrestling::Application.routes.draw do
|
||||
get 'tournaments/:id/bout_sheets' => 'tournaments#bout_sheets'
|
||||
get 'tournaments/:id/no_matches' => 'tournaments#no_matches'
|
||||
get 'tournaments/:id/matches' => 'tournaments#matches'
|
||||
get 'tournaments/:id/qrcode' => 'tournaments#qrcode'
|
||||
get 'tournaments/:id/delegate' => 'tournaments#delegate', :as => :tournament_delegate
|
||||
post 'tournaments/:id/delegate' => 'tournaments#delegate', :as => :set_tournament_delegate
|
||||
delete 'tournaments/:id/:delegate/remove_delegate' => 'tournaments#remove_delegate', :as => :delete_delegate_path
|
||||
|
||||
49
db/migrate/20260129120000_add_queues_to_mats.rb
Normal file
49
db/migrate/20260129120000_add_queues_to_mats.rb
Normal file
@@ -0,0 +1,49 @@
|
||||
class AddQueuesToMats < ActiveRecord::Migration[7.0]
|
||||
class Mat < ActiveRecord::Base
|
||||
self.table_name = "mats"
|
||||
has_many :matches, class_name: "AddQueuesToMats::Match", foreign_key: "mat_id"
|
||||
end
|
||||
|
||||
class Match < ActiveRecord::Base
|
||||
self.table_name = "matches"
|
||||
end
|
||||
|
||||
def up
|
||||
add_column :mats, :queue1, :bigint
|
||||
add_column :mats, :queue2, :bigint
|
||||
add_column :mats, :queue3, :bigint
|
||||
add_column :mats, :queue4, :bigint
|
||||
|
||||
add_index :mats, :queue1
|
||||
add_index :mats, :queue2
|
||||
add_index :mats, :queue3
|
||||
add_index :mats, :queue4
|
||||
|
||||
say_with_time "Backfilling mat queues from unfinished matches" do
|
||||
Mat.reset_column_information
|
||||
Match.reset_column_information
|
||||
|
||||
Mat.find_each do |mat|
|
||||
match_ids = mat.matches.where(finished: [nil, 0]).order(:bout_number).limit(4).pluck(:id)
|
||||
mat.update_columns(
|
||||
queue1: match_ids[0],
|
||||
queue2: match_ids[1],
|
||||
queue3: match_ids[2],
|
||||
queue4: match_ids[3]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
remove_index :mats, :queue1
|
||||
remove_index :mats, :queue2
|
||||
remove_index :mats, :queue3
|
||||
remove_index :mats, :queue4
|
||||
|
||||
remove_column :mats, :queue1
|
||||
remove_column :mats, :queue2
|
||||
remove_column :mats, :queue3
|
||||
remove_column :mats, :queue4
|
||||
end
|
||||
end
|
||||
10
db/schema.rb
10
db/schema.rb
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_04_15_173921) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2026_01_29_120000) do
|
||||
create_table "mat_assignment_rules", force: :cascade do |t|
|
||||
t.bigint "tournament_id"
|
||||
t.bigint "mat_id"
|
||||
@@ -56,6 +56,14 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_15_173921) do
|
||||
t.bigint "tournament_id"
|
||||
t.datetime "created_at", precision: nil
|
||||
t.datetime "updated_at", precision: nil
|
||||
t.bigint "queue1"
|
||||
t.bigint "queue2"
|
||||
t.bigint "queue3"
|
||||
t.bigint "queue4"
|
||||
t.index ["queue1"], name: "index_mats_on_queue1"
|
||||
t.index ["queue2"], name: "index_mats_on_queue2"
|
||||
t.index ["queue3"], name: "index_mats_on_queue3"
|
||||
t.index ["queue4"], name: "index_mats_on_queue4"
|
||||
t.index ["tournament_id"], name: "index_mats_on_tournament_id"
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ruby:3.2.0
|
||||
FROM ruby:4.0.1
|
||||
|
||||
# Accept build arguments for user/group IDs
|
||||
ARG USER_ID=1000
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ruby:3.2.0-slim
|
||||
FROM ruby:4.0.1-slim
|
||||
|
||||
#HEALTHCHECK --start-period=60s CMD curl http://127.0.0.1/
|
||||
|
||||
@@ -15,6 +15,8 @@ RUN apt-get -qq update --fix-missing \
|
||||
libsqlite3-dev \
|
||||
wget \
|
||||
default-libmysqlclient-dev \
|
||||
libyaml-dev \
|
||||
pkg-config \
|
||||
nodejs \
|
||||
tzdata \
|
||||
git \
|
||||
|
||||
@@ -35,53 +35,69 @@ namespace :tournament do
|
||||
end
|
||||
|
||||
sleep(10)
|
||||
@tournament.reload # Ensure matches association is fresh before iterating
|
||||
@tournament.matches.reload.sort_by(&:bout_number).each do |match|
|
||||
if match.reload.loser1_name != "BYE" and match.reload.loser2_name != "BYE" and match.reload.finished != 1
|
||||
# Wait until both wrestlers are assigned
|
||||
while (match.w1.nil? || match.w2.nil?)
|
||||
puts "Waiting for wrestlers in match #{match.bout_number}..."
|
||||
sleep(5) # Wait for 5 seconds before checking again
|
||||
match.reload
|
||||
end
|
||||
puts "Finishing match with bout number #{match.bout_number}..."
|
||||
loop do
|
||||
@tournament.reload
|
||||
@tournament.refill_open_bout_board_queues
|
||||
|
||||
# Choose a random winner
|
||||
wrestlers = [match.w1, match.w2]
|
||||
match.winner_id = wrestlers.sample
|
||||
|
||||
# Choose a random win type
|
||||
win_type = WIN_TYPES.sample
|
||||
match.win_type = win_type
|
||||
|
||||
# Assign score based on win type
|
||||
match.score = case win_type
|
||||
when "Decision"
|
||||
low_score = rand(0..10)
|
||||
high_score = low_score + rand(1..7)
|
||||
"#{high_score}-#{low_score}"
|
||||
when "Major"
|
||||
low_score = rand(0..10)
|
||||
high_score = low_score + rand(8..14)
|
||||
"#{high_score}-#{low_score}"
|
||||
when "Tech Fall"
|
||||
low_score = rand(0..10)
|
||||
high_score = low_score + rand(15..19)
|
||||
"#{high_score}-#{low_score}"
|
||||
when "Pin"
|
||||
pin_times = ["0:30","1:12","5:37","2:34","3:54","4:23","5:56","0:12","1:00"]
|
||||
pin_times.sample
|
||||
else
|
||||
"" # Default score
|
||||
end
|
||||
|
||||
# Mark match as finished
|
||||
match.finished = 1
|
||||
match.save!
|
||||
# sleep to prevent mysql locks when assign_next_match to a mat runs
|
||||
sleep(0.5)
|
||||
mats_with_queue1 = @tournament.mats.select do |mat|
|
||||
match = mat.queue1_match
|
||||
match && match.finished != 1 && match.loser1_name != "BYE" && match.loser2_name != "BYE"
|
||||
end
|
||||
|
||||
break if mats_with_queue1.empty?
|
||||
|
||||
mat = mats_with_queue1.sample
|
||||
match = mat.queue1_match
|
||||
|
||||
# Wait until both wrestlers are assigned for the selected queue1 match.
|
||||
while match && (match.w1.nil? || match.w2.nil?)
|
||||
puts "Waiting for wrestlers in match #{match.bout_number} on mat #{mat.name}..."
|
||||
sleep(5)
|
||||
@tournament.reload
|
||||
@tournament.refill_open_bout_board_queues
|
||||
match = mat.reload.queue1_match
|
||||
end
|
||||
|
||||
next unless match
|
||||
next if match.finished == 1 || match.loser1_name == "BYE" || match.loser2_name == "BYE"
|
||||
|
||||
puts "Finishing queue1 match on mat #{mat.name} with bout number #{match.bout_number}..."
|
||||
|
||||
# Choose a random winner
|
||||
wrestlers = [match.w1, match.w2]
|
||||
match.winner_id = wrestlers.sample
|
||||
|
||||
# Choose a random win type
|
||||
win_type = WIN_TYPES.sample
|
||||
match.win_type = win_type
|
||||
|
||||
# Assign score based on win type
|
||||
match.score = case win_type
|
||||
when "Decision"
|
||||
low_score = rand(0..10)
|
||||
high_score = low_score + rand(1..7)
|
||||
"#{high_score}-#{low_score}"
|
||||
when "Major"
|
||||
low_score = rand(0..10)
|
||||
high_score = low_score + rand(8..14)
|
||||
"#{high_score}-#{low_score}"
|
||||
when "Tech Fall"
|
||||
low_score = rand(0..10)
|
||||
high_score = low_score + rand(15..19)
|
||||
"#{high_score}-#{low_score}"
|
||||
when "Pin"
|
||||
pin_times = ["0:30","1:12","5:37","2:34","3:54","4:23","5:56","0:12","1:00"]
|
||||
pin_times.sample
|
||||
else
|
||||
""
|
||||
end
|
||||
|
||||
# Mark match as finished
|
||||
match.finished = 1
|
||||
match.save!
|
||||
# sleep to prevent mysql locks when queue advancement runs
|
||||
sleep(0.5)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -35,12 +35,35 @@
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.0025em;
|
||||
line-height: 1.4;
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
place-items: center;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
#error-description {
|
||||
fill: #d30001;
|
||||
}
|
||||
|
||||
#error-id {
|
||||
fill: #f0eff0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: #101010;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
#error-description {
|
||||
fill: #FF6161;
|
||||
}
|
||||
|
||||
#error-id {
|
||||
fill: #2c2c2c;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
font-weight: 700;
|
||||
@@ -83,13 +106,11 @@
|
||||
}
|
||||
|
||||
main article br {
|
||||
|
||||
display: none;
|
||||
|
||||
@media(min-width: 48em) {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -102,10 +123,10 @@
|
||||
|
||||
<main>
|
||||
<header>
|
||||
<svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m124.48 3.00509-45.6889 100.02991h26.2239v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.1833v-31.901l50.2851-103.27391zm115.583 168.69891c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm140.456 133.2831c-40.823 0-64.884-35.146-64.884-85.7015 0-50.5554 24.061-85.700907 64.884-85.700907 40.822 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.062 85.7015-64.884 85.7015zm0-133.2831c-17.573 0-22.71 21.8984-22.71 47.5816 0 25.6835 5.137 47.5815 22.71 47.5815 17.302 0 22.709-21.898 22.709-47.5815 0-25.6832-5.407-47.5816-22.709-47.5816z" fill="#f0eff0"/><path d="m123.606 85.4445c3.212 1.0523 5.538 4.2089 5.538 8.0301 0 6.1472-4.209 9.5254-11.298 9.5254h-15.617v-34.0033h14.565c7.089 0 11.353 3.1566 11.353 9.2484 0 3.6551-2.049 6.3134-4.541 7.1994zm-12.904-2.9905h5.095c2.603 0 3.988-.9968 3.988-3.1013 0-2.1044-1.385-3.0459-3.988-3.0459h-5.095zm0 6.6456v6.5902h5.981c2.492 0 3.877-1.3291 3.877-3.2674 0-2.049-1.385-3.3228-3.877-3.3228zm43.786 13.9004h-8.362v-1.274c-.831.831-3.323 1.717-5.981 1.717-4.929 0-9.083-2.769-9.083-8.0301 0-4.818 4.154-7.9193 9.581-7.9193 2.049 0 4.486.6646 5.483 1.3845v-1.606c0-1.606-.942-2.9905-3.046-2.9905-1.606 0-2.548.7199-2.935 1.8275h-8.197c.72-4.8181 4.985-8.6393 11.409-8.6393 7.088 0 11.131 3.7659 11.131 10.2453zm-8.362-6.9779v-1.4399c-.554-1.0522-2.049-1.7167-3.655-1.7167-1.717 0-3.434.7199-3.434 2.3813 0 1.7168 1.717 2.4367 3.434 2.4367 1.606 0 3.101-.6645 3.655-1.6614zm27.996 6.9779v-1.994c-1.163 1.329-3.599 2.548-6.147 2.548-7.199 0-11.131-5.8151-11.131-13.0145s3.932-13.0143 11.131-13.0143c2.548 0 4.984 1.2184 6.147 2.5475v-13.0697h8.695v35.997zm0-9.1931v-6.5902c-.664-1.3291-2.159-2.326-3.821-2.326-2.99 0-4.763 2.4368-4.763 5.6488s1.773 5.5934 4.763 5.5934c1.717 0 3.157-.9415 3.821-2.326zm35.471-2.049h-3.101v11.2421h-8.806v-34.0033h15.285c7.31 0 12.35 4.1535 12.35 11.5744 0 5.1503-2.603 8.6947-6.757 10.2453l7.975 12.1836h-9.858zm-3.101-15.2849v8.1962h5.538c3.156 0 4.596-1.606 4.596-4.0981s-1.44-4.0981-4.596-4.0981zm36.957 17.8323h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.643 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.515-13.0143 7.643 0 11.962 5.095 11.962 12.5159v2.1598h-16.115c.277 2.9905 1.827 4.5965 4.32 4.5965 1.772 0 3.156-.7753 3.655-2.4921zm-3.822-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm30.98 27.5234v-10.799c-1.163 1.329-3.6 2.548-6.147 2.548-7.2 0-11.132-5.9259-11.132-13.0145 0-7.144 3.932-13.0143 11.132-13.0143 2.547 0 4.984 1.2184 6.147 2.5475v-1.9937h8.695v33.726zm0-17.9981v-6.5902c-.665-1.3291-2.105-2.326-3.821-2.326-2.991 0-4.763 2.4368-4.763 5.6488s1.772 5.5934 4.763 5.5934c1.661 0 3.156-.9415 3.821-2.326zm36.789-15.7279v24.921h-8.695v-2.16c-1.329 1.551-3.821 2.714-6.646 2.714-5.482 0-8.75-3.5999-8.75-9.1379v-16.3371h8.64v14.288c0 2.1045.996 3.5997 3.212 3.5997 1.606 0 3.101-1.0522 3.544-2.769v-15.1187zm19.084 16.2263h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.643 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.515-13.0143 7.643 0 11.963 5.095 11.963 12.5159v2.1598h-16.116c.277 2.9905 1.828 4.5965 4.32 4.5965 1.772 0 3.156-.7753 3.655-2.4921zm-3.822-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm13.428 11.0206h8.474c.387 1.3845 1.606 2.1598 3.156 2.1598 1.44 0 2.548-.5538 2.548-1.7168 0-.9414-.72-1.2737-1.939-1.5506l-4.873-.9969c-4.154-.886-6.867-2.8797-6.867-7.2547 0-5.3165 4.762-8.4178 10.633-8.4178 6.812 0 10.522 3.1567 11.297 8.0855h-8.03c-.277-1.0522-1.052-1.9937-3.046-1.9937-1.273 0-2.326.5538-2.326 1.6614 0 .7753.554 1.163 1.717 1.3845l4.929 1.163c4.541 1.0522 6.978 3.4335 6.978 7.4763 0 5.3168-4.818 8.2518-10.91 8.2518-6.369 0-10.965-2.88-11.741-8.2518zm27.538-.8861v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.205v6.7564h-5.205v8.307c0 1.9383.941 2.769 2.658 2.769.941 0 1.993-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.871 0-9.193-2.769-9.193-9.0819z" fill="#d30001"/></svg>
|
||||
<svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m124.48 3.00509-45.6889 100.02991h26.2239v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.1833v-31.901l50.2851-103.27391zm115.583 168.69891c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm140.456 133.2831c-40.823 0-64.884-35.146-64.884-85.7015 0-50.5554 24.061-85.700907 64.884-85.700907 40.822 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.062 85.7015-64.884 85.7015zm0-133.2831c-17.573 0-22.71 21.8984-22.71 47.5816 0 25.6835 5.137 47.5815 22.71 47.5815 17.302 0 22.709-21.898 22.709-47.5815 0-25.6832-5.407-47.5816-22.709-47.5816z" id="error-id"/><path d="m123.606 85.4445c3.212 1.0523 5.538 4.2089 5.538 8.0301 0 6.1472-4.209 9.5254-11.298 9.5254h-15.617v-34.0033h14.565c7.089 0 11.353 3.1566 11.353 9.2484 0 3.6551-2.049 6.3134-4.541 7.1994zm-12.904-2.9905h5.095c2.603 0 3.988-.9968 3.988-3.1013 0-2.1044-1.385-3.0459-3.988-3.0459h-5.095zm0 6.6456v6.5902h5.981c2.492 0 3.877-1.3291 3.877-3.2674 0-2.049-1.385-3.3228-3.877-3.3228zm43.786 13.9004h-8.362v-1.274c-.831.831-3.323 1.717-5.981 1.717-4.929 0-9.083-2.769-9.083-8.0301 0-4.818 4.154-7.9193 9.581-7.9193 2.049 0 4.486.6646 5.483 1.3845v-1.606c0-1.606-.942-2.9905-3.046-2.9905-1.606 0-2.548.7199-2.935 1.8275h-8.197c.72-4.8181 4.985-8.6393 11.409-8.6393 7.088 0 11.131 3.7659 11.131 10.2453zm-8.362-6.9779v-1.4399c-.554-1.0522-2.049-1.7167-3.655-1.7167-1.717 0-3.434.7199-3.434 2.3813 0 1.7168 1.717 2.4367 3.434 2.4367 1.606 0 3.101-.6645 3.655-1.6614zm27.996 6.9779v-1.994c-1.163 1.329-3.599 2.548-6.147 2.548-7.199 0-11.131-5.8151-11.131-13.0145s3.932-13.0143 11.131-13.0143c2.548 0 4.984 1.2184 6.147 2.5475v-13.0697h8.695v35.997zm0-9.1931v-6.5902c-.664-1.3291-2.159-2.326-3.821-2.326-2.99 0-4.763 2.4368-4.763 5.6488s1.773 5.5934 4.763 5.5934c1.717 0 3.157-.9415 3.821-2.326zm35.471-2.049h-3.101v11.2421h-8.806v-34.0033h15.285c7.31 0 12.35 4.1535 12.35 11.5744 0 5.1503-2.603 8.6947-6.757 10.2453l7.975 12.1836h-9.858zm-3.101-15.2849v8.1962h5.538c3.156 0 4.596-1.606 4.596-4.0981s-1.44-4.0981-4.596-4.0981zm36.957 17.8323h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.643 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.515-13.0143 7.643 0 11.962 5.095 11.962 12.5159v2.1598h-16.115c.277 2.9905 1.827 4.5965 4.32 4.5965 1.772 0 3.156-.7753 3.655-2.4921zm-3.822-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm30.98 27.5234v-10.799c-1.163 1.329-3.6 2.548-6.147 2.548-7.2 0-11.132-5.9259-11.132-13.0145 0-7.144 3.932-13.0143 11.132-13.0143 2.547 0 4.984 1.2184 6.147 2.5475v-1.9937h8.695v33.726zm0-17.9981v-6.5902c-.665-1.3291-2.105-2.326-3.821-2.326-2.991 0-4.763 2.4368-4.763 5.6488s1.772 5.5934 4.763 5.5934c1.661 0 3.156-.9415 3.821-2.326zm36.789-15.7279v24.921h-8.695v-2.16c-1.329 1.551-3.821 2.714-6.646 2.714-5.482 0-8.75-3.5999-8.75-9.1379v-16.3371h8.64v14.288c0 2.1045.996 3.5997 3.212 3.5997 1.606 0 3.101-1.0522 3.544-2.769v-15.1187zm19.084 16.2263h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.643 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.515-13.0143 7.643 0 11.963 5.095 11.963 12.5159v2.1598h-16.116c.277 2.9905 1.828 4.5965 4.32 4.5965 1.772 0 3.156-.7753 3.655-2.4921zm-3.822-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm13.428 11.0206h8.474c.387 1.3845 1.606 2.1598 3.156 2.1598 1.44 0 2.548-.5538 2.548-1.7168 0-.9414-.72-1.2737-1.939-1.5506l-4.873-.9969c-4.154-.886-6.867-2.8797-6.867-7.2547 0-5.3165 4.762-8.4178 10.633-8.4178 6.812 0 10.522 3.1567 11.297 8.0855h-8.03c-.277-1.0522-1.052-1.9937-3.046-1.9937-1.273 0-2.326.5538-2.326 1.6614 0 .7753.554 1.163 1.717 1.3845l4.929 1.163c4.541 1.0522 6.978 3.4335 6.978 7.4763 0 5.3168-4.818 8.2518-10.91 8.2518-6.369 0-10.965-2.88-11.741-8.2518zm27.538-.8861v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.205v6.7564h-5.205v8.307c0 1.9383.941 2.769 2.658 2.769.941 0 1.993-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.871 0-9.193-2.769-9.193-9.0819z" id="error-description"/></svg>
|
||||
</header>
|
||||
<article>
|
||||
<p><strong>The server cannot process the request due to a client error.</strong> Please check the request and try again. If you’re the application owner check the logs for more information.</p>
|
||||
<p><strong>The server cannot process the request due to a client error.</strong> Please check the request and try again. If you're the application owner check the logs for more information.</p>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<head>
|
||||
|
||||
<title>The page you were looking for doesn’t exist (404 Not found)</title>
|
||||
<title>The page you were looking for doesn't exist (404 Not found)</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="initial-scale=1, width=device-width">
|
||||
@@ -35,12 +35,35 @@
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.0025em;
|
||||
line-height: 1.4;
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
place-items: center;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
#error-description {
|
||||
fill: #d30001;
|
||||
}
|
||||
|
||||
#error-id {
|
||||
fill: #f0eff0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: #101010;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
#error-description {
|
||||
fill: #FF6161;
|
||||
}
|
||||
|
||||
#error-id {
|
||||
fill: #2c2c2c;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
font-weight: 700;
|
||||
@@ -83,13 +106,11 @@
|
||||
}
|
||||
|
||||
main article br {
|
||||
|
||||
display: none;
|
||||
|
||||
@media(min-width: 48em) {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -102,10 +123,10 @@
|
||||
|
||||
<main>
|
||||
<header>
|
||||
<svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m124.48 3.00509-45.6889 100.02991h26.2239v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.1833v-31.901l50.2851-103.27391zm115.583 168.69891c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm165.328-35.41581-45.689 100.02991h26.224v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.184v-31.901l50.285-103.27391z" fill="#f0eff0"/><path d="m157.758 68.9967v34.0033h-7.199l-14.233-19.8814v19.8814h-8.584v-34.0033h8.307l13.125 18.7184v-18.7184zm28.454 21.5428c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.528 0c0-3.4336-1.496-5.8703-4.209-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.209-2.3813 4.209-5.8149zm13.184 3.8766v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.205v6.7564h-5.205v8.307c0 1.9383.941 2.769 2.658 2.769.941 0 1.994-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.87 0-9.193-2.769-9.193-9.0819zm37.027 8.5839h-8.806v-34.0033h23.924v7.6978h-15.118v6.7564h13.9v7.5316h-13.9zm41.876-12.4605c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.529 0c0-3.4336-1.495-5.8703-4.208-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.208-2.3813 4.208-5.8149zm35.337-12.4605v24.921h-8.695v-2.16c-1.329 1.551-3.821 2.714-6.646 2.714-5.482 0-8.75-3.5999-8.75-9.1379v-16.3371h8.64v14.288c0 2.1045.997 3.5997 3.212 3.5997 1.606 0 3.101-1.0522 3.544-2.769v-15.1187zm4.076 24.921v-24.921h8.694v2.1598c1.385-1.5506 3.822-2.7136 6.701-2.7136 5.538 0 8.806 3.5997 8.806 9.1377v16.3371h-8.639v-14.2327c0-2.049-1.053-3.5443-3.268-3.5443-1.717 0-3.156.9969-3.6 2.7136v15.0634zm44.113 0v-1.994c-1.163 1.329-3.6 2.548-6.147 2.548-7.2 0-11.132-5.8151-11.132-13.0145s3.932-13.0143 11.132-13.0143c2.547 0 4.984 1.2184 6.147 2.5475v-13.0697h8.695v35.997zm0-9.1931v-6.5902c-.665-1.3291-2.16-2.326-3.821-2.326-2.991 0-4.763 2.4368-4.763 5.6488s1.772 5.5934 4.763 5.5934c1.717 0 3.156-.9415 3.821-2.326z" fill="#d30001"/></svg>
|
||||
<svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m124.48 3.00509-45.6889 100.02991h26.2239v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.1833v-31.901l50.2851-103.27391zm115.583 168.69891c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm165.328-35.41581-45.689 100.02991h26.224v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.184v-31.901l50.285-103.27391z" id="error-id"/><path d="m157.758 68.9967v34.0033h-7.199l-14.233-19.8814v19.8814h-8.584v-34.0033h8.307l13.125 18.7184v-18.7184zm28.454 21.5428c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.528 0c0-3.4336-1.496-5.8703-4.209-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.209-2.3813 4.209-5.8149zm13.184 3.8766v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.205v6.7564h-5.205v8.307c0 1.9383.941 2.769 2.658 2.769.941 0 1.994-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.87 0-9.193-2.769-9.193-9.0819zm37.027 8.5839h-8.806v-34.0033h23.924v7.6978h-15.118v6.7564h13.9v7.5316h-13.9zm41.876-12.4605c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.529 0c0-3.4336-1.495-5.8703-4.208-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.208-2.3813 4.208-5.8149zm35.337-12.4605v24.921h-8.695v-2.16c-1.329 1.551-3.821 2.714-6.646 2.714-5.482 0-8.75-3.5999-8.75-9.1379v-16.3371h8.64v14.288c0 2.1045.997 3.5997 3.212 3.5997 1.606 0 3.101-1.0522 3.544-2.769v-15.1187zm4.076 24.921v-24.921h8.694v2.1598c1.385-1.5506 3.822-2.7136 6.701-2.7136 5.538 0 8.806 3.5997 8.806 9.1377v16.3371h-8.639v-14.2327c0-2.049-1.053-3.5443-3.268-3.5443-1.717 0-3.156.9969-3.6 2.7136v15.0634zm44.113 0v-1.994c-1.163 1.329-3.6 2.548-6.147 2.548-7.2 0-11.132-5.8151-11.132-13.0145s3.932-13.0143 11.132-13.0143c2.547 0 4.984 1.2184 6.147 2.5475v-13.0697h8.695v35.997zm0-9.1931v-6.5902c-.665-1.3291-2.16-2.326-3.821-2.326-2.991 0-4.763 2.4368-4.763 5.6488s1.772 5.5934 4.763 5.5934c1.717 0 3.156-.9415 3.821-2.326z" id="error-description"/></svg>
|
||||
</header>
|
||||
<article>
|
||||
<p><strong>The page you were looking for doesn’t exist.</strong> You may have mistyped the address or the page may have moved. If you’re the application owner check the logs for more information.</p>
|
||||
<p><strong>The page you were looking for doesn't exist.</strong> You may have mistyped the address or the page may have moved. If you're the application owner check the logs for more information.</p>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -35,12 +35,35 @@
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.0025em;
|
||||
line-height: 1.4;
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
place-items: center;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
#error-description {
|
||||
fill: #d30001;
|
||||
}
|
||||
|
||||
#error-id {
|
||||
fill: #f0eff0;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: #101010;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
#error-description {
|
||||
fill: #FF6161;
|
||||
}
|
||||
|
||||
#error-id {
|
||||
fill: #2c2c2c;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
font-weight: 700;
|
||||
@@ -83,13 +106,11 @@
|
||||
}
|
||||
|
||||
main article br {
|
||||
|
||||
display: none;
|
||||
|
||||
@media(min-width: 48em) {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -102,7 +123,7 @@
|
||||
|
||||
<main>
|
||||
<header>
|
||||
<svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m124.48 3.00509-45.6889 100.02991h26.2239v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.1833v-31.901l50.2851-103.27391zm115.583 168.69891c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm202.906 9.7326h-41.093c-2.433-7.2994-7.84-12.4361-17.302-12.4361-16.221 0-25.413 17.5728-25.954 34.8752v1.3517c5.137-7.0291 16.221-12.4361 30.82-12.4361 33.524 0 54.881 24.0612 54.881 53.7998 0 33.253-23.791 58.396-61.64 58.396-21.628 0-39.741-10.003-50.825-27.576-9.733-14.599-13.788-32.442-13.788-54.3406 0-51.9072 24.331-89.485807 66.236-89.485807 32.712 0 53.258 18.654107 58.665 47.851907zm-82.727 66.2355c0 13.247 9.463 22.439 22.71 22.439 12.977 0 22.439-9.192 22.439-22.439 0-13.517-9.462-22.7091-22.439-22.7091-13.247 0-22.71 9.1921-22.71 22.7091z" fill="#f0eff0"/><path d="m100.761 68.9967v34.0033h-7.1991l-14.2326-19.8814v19.8814h-8.5839v-34.0033h8.307l13.125 18.7184v-18.7184zm28.454 21.5428c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.529 0c0-3.4336-1.495-5.8703-4.208-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.208-2.3813 4.208-5.8149zm13.185 3.8766v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.205v6.7564h-5.205v8.307c0 1.9383.941 2.769 2.658 2.769.941 0 1.994-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.87 0-9.193-2.769-9.193-9.0819zm39.02-25.4194h9.083l12.958 34.0033h-9.027l-2.436-6.5902h-12.35l-2.381 6.5902h-8.806zm4.431 10.5222-3.489 9.5807h6.978zm17.44 11.0206c0-7.6978 5.095-13.0143 12.572-13.0143 6.701 0 10.854 3.9874 11.574 9.8023h-8.418c-.221-1.4953-1.384-2.6029-3.156-2.6029-2.437 0-3.988 2.2706-3.988 5.8149s1.551 5.7595 3.988 5.7595c1.772 0 2.935-1.0522 3.156-2.5475h8.418c-.72 5.7596-4.873 9.8025-11.574 9.8025-7.477 0-12.572-5.3167-12.572-13.0145zm25.676 0c0-7.6978 5.095-13.0143 12.572-13.0143 6.701 0 10.854 3.9874 11.574 9.8023h-8.418c-.221-1.4953-1.384-2.6029-3.156-2.6029-2.437 0-3.988 2.2706-3.988 5.8149s1.551 5.7595 3.988 5.7595c1.772 0 2.935-1.0522 3.156-2.5475h8.418c-.72 5.7596-4.873 9.8025-11.574 9.8025-7.477 0-12.572-5.3167-12.572-13.0145zm42.013 3.7658h8.031c-.887 5.7597-5.206 9.2487-11.686 9.2487-7.642 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.317-13.0143 12.516-13.0143 7.643 0 11.962 5.095 11.962 12.5159v2.1598h-16.115c.277 2.9905 1.827 4.5965 4.319 4.5965 1.773 0 3.157-.7753 3.655-2.4921zm-3.821-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm23.4 16.7244v10.799h-8.694v-33.726h8.694v1.9937c1.163-1.3291 3.6-2.5475 6.148-2.5475 7.199 0 11.131 5.8703 11.131 13.0143 0 7.0886-3.932 13.0145-11.131 13.0145-2.548 0-4.985-1.219-6.148-2.548zm0-13.7893v6.5902c.665 1.3845 2.16 2.326 3.822 2.326 2.99 0 4.762-2.3814 4.762-5.5934s-1.772-5.6488-4.762-5.6488c-1.717 0-3.157.9969-3.822 2.326zm21.892 7.1994v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.206v6.7564h-5.206v8.307c0 1.9383.941 2.769 2.658 2.769.942 0 1.994-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.87 0-9.193-2.769-9.193-9.0819zm39.458 8.5839h-8.363v-1.274c-.83.831-3.322 1.717-5.981 1.717-4.928 0-9.082-2.769-9.082-8.0301 0-4.818 4.154-7.9193 9.581-7.9193 2.049 0 4.486.6646 5.482 1.3845v-1.606c0-1.606-.941-2.9905-3.045-2.9905-1.606 0-2.548.7199-2.936 1.8275h-8.196c.72-4.8181 4.984-8.6393 11.408-8.6393 7.089 0 11.132 3.7659 11.132 10.2453zm-8.363-6.9779v-1.4399c-.553-1.0522-2.049-1.7167-3.655-1.7167-1.716 0-3.433.7199-3.433 2.3813 0 1.7168 1.717 2.4367 3.433 2.4367 1.606 0 3.102-.6645 3.655-1.6614zm20.742 4.9839v1.994h-8.694v-35.997h8.694v13.0697c1.163-1.3291 3.6-2.5475 6.148-2.5475 7.199 0 11.131 5.8149 11.131 13.0143s-3.932 13.0145-11.131 13.0145c-2.548 0-4.985-1.219-6.148-2.548zm0-13.7893v6.5902c.665 1.3845 2.105 2.326 3.822 2.326 2.99 0 4.762-2.3814 4.762-5.5934s-1.772-5.6488-4.762-5.6488c-1.662 0-3.157.9969-3.822 2.326zm28.759-20.2137v35.997h-8.695v-35.997zm19.172 27.3023h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.643 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.516-13.0143 7.642 0 11.962 5.095 11.962 12.5159v2.1598h-16.116c.277 2.9905 1.828 4.5965 4.32 4.5965 1.772 0 3.157-.7753 3.655-2.4921zm-3.821-10.0237c-2.049 0-3.434 1.2737-3.988 3.5997h7.532c-.111-2.0491-1.384-3.5997-3.544-3.5997z" fill="#d30001"/></svg>
|
||||
<svg height="172" viewBox="0 0 480 172" width="480" xmlns="http://www.w3.org/2000/svg"><path d="m124.48 3.00509-45.6889 100.02991h26.2239v-28.1168h38.119v28.1168h21.628v35.145h-21.628v30.82h-37.308v-30.82h-72.1833v-31.901l50.2851-103.27391zm115.583 168.69891c-40.822 0-64.884-35.146-64.884-85.7015 0-50.5554 24.062-85.700907 64.884-85.700907 40.823 0 64.884 35.145507 64.884 85.700907 0 50.5555-24.061 85.7015-64.884 85.7015zm0-133.2831c-17.572 0-22.709 21.8984-22.709 47.5816 0 25.6835 5.137 47.5815 22.709 47.5815 17.303 0 22.71-21.898 22.71-47.5815 0-25.6832-5.407-47.5816-22.71-47.5816zm202.906 9.7326h-41.093c-2.433-7.2994-7.84-12.4361-17.302-12.4361-16.221 0-25.413 17.5728-25.954 34.8752v1.3517c5.137-7.0291 16.221-12.4361 30.82-12.4361 33.524 0 54.881 24.0612 54.881 53.7998 0 33.253-23.791 58.396-61.64 58.396-21.628 0-39.741-10.003-50.825-27.576-9.733-14.599-13.788-32.442-13.788-54.3406 0-51.9072 24.331-89.485807 66.236-89.485807 32.712 0 53.258 18.654107 58.665 47.851907zm-82.727 66.2355c0 13.247 9.463 22.439 22.71 22.439 12.977 0 22.439-9.192 22.439-22.439 0-13.517-9.462-22.7091-22.439-22.7091-13.247 0-22.71 9.1921-22.71 22.7091z" id="error-id"/><path d="m100.761 68.9967v34.0033h-7.1991l-14.2326-19.8814v19.8814h-8.5839v-34.0033h8.307l13.125 18.7184v-18.7184zm28.454 21.5428c0 7.6978-5.15 13.0145-12.737 13.0145-7.532 0-12.738-5.3167-12.738-13.0145s5.206-13.0143 12.738-13.0143c7.587 0 12.737 5.3165 12.737 13.0143zm-8.529 0c0-3.4336-1.495-5.8703-4.208-5.8703-2.659 0-4.154 2.4367-4.154 5.8703s1.495 5.8149 4.154 5.8149c2.713 0 4.208-2.3813 4.208-5.8149zm13.185 3.8766v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.205v6.7564h-5.205v8.307c0 1.9383.941 2.769 2.658 2.769.941 0 1.994-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.87 0-9.193-2.769-9.193-9.0819zm39.02-25.4194h9.083l12.958 34.0033h-9.027l-2.436-6.5902h-12.35l-2.381 6.5902h-8.806zm4.431 10.5222-3.489 9.5807h6.978zm17.44 11.0206c0-7.6978 5.095-13.0143 12.572-13.0143 6.701 0 10.854 3.9874 11.574 9.8023h-8.418c-.221-1.4953-1.384-2.6029-3.156-2.6029-2.437 0-3.988 2.2706-3.988 5.8149s1.551 5.7595 3.988 5.7595c1.772 0 2.935-1.0522 3.156-2.5475h8.418c-.72 5.7596-4.873 9.8025-11.574 9.8025-7.477 0-12.572-5.3167-12.572-13.0145zm25.676 0c0-7.6978 5.095-13.0143 12.572-13.0143 6.701 0 10.854 3.9874 11.574 9.8023h-8.418c-.221-1.4953-1.384-2.6029-3.156-2.6029-2.437 0-3.988 2.2706-3.988 5.8149s1.551 5.7595 3.988 5.7595c1.772 0 2.935-1.0522 3.156-2.5475h8.418c-.72 5.7596-4.873 9.8025-11.574 9.8025-7.477 0-12.572-5.3167-12.572-13.0145zm42.013 3.7658h8.031c-.887 5.7597-5.206 9.2487-11.686 9.2487-7.642 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.317-13.0143 12.516-13.0143 7.643 0 11.962 5.095 11.962 12.5159v2.1598h-16.115c.277 2.9905 1.827 4.5965 4.319 4.5965 1.773 0 3.157-.7753 3.655-2.4921zm-3.821-10.0237c-2.049 0-3.433 1.2737-3.987 3.5997h7.532c-.111-2.0491-1.385-3.5997-3.545-3.5997zm23.4 16.7244v10.799h-8.694v-33.726h8.694v1.9937c1.163-1.3291 3.6-2.5475 6.148-2.5475 7.199 0 11.131 5.8703 11.131 13.0143 0 7.0886-3.932 13.0145-11.131 13.0145-2.548 0-4.985-1.219-6.148-2.548zm0-13.7893v6.5902c.665 1.3845 2.16 2.326 3.822 2.326 2.99 0 4.762-2.3814 4.762-5.5934s-1.772-5.6488-4.762-5.6488c-1.717 0-3.157.9969-3.822 2.326zm21.892 7.1994v-9.5807h-3.655v-6.7564h3.655v-6.8671h8.584v6.8671h5.206v6.7564h-5.206v8.307c0 1.9383.941 2.769 2.658 2.769.942 0 1.994-.2216 2.769-.5538v7.3654c-.997.443-2.88.775-4.818.775-5.87 0-9.193-2.769-9.193-9.0819zm39.458 8.5839h-8.363v-1.274c-.83.831-3.322 1.717-5.981 1.717-4.928 0-9.082-2.769-9.082-8.0301 0-4.818 4.154-7.9193 9.581-7.9193 2.049 0 4.486.6646 5.482 1.3845v-1.606c0-1.606-.941-2.9905-3.045-2.9905-1.606 0-2.548.7199-2.936 1.8275h-8.196c.72-4.8181 4.984-8.6393 11.408-8.6393 7.089 0 11.132 3.7659 11.132 10.2453zm-8.363-6.9779v-1.4399c-.553-1.0522-2.049-1.7167-3.655-1.7167-1.716 0-3.433.7199-3.433 2.3813 0 1.7168 1.717 2.4367 3.433 2.4367 1.606 0 3.102-.6645 3.655-1.6614zm20.742 4.9839v1.994h-8.694v-35.997h8.694v13.0697c1.163-1.3291 3.6-2.5475 6.148-2.5475 7.199 0 11.131 5.8149 11.131 13.0143s-3.932 13.0145-11.131 13.0145c-2.548 0-4.985-1.219-6.148-2.548zm0-13.7893v6.5902c.665 1.3845 2.105 2.326 3.822 2.326 2.99 0 4.762-2.3814 4.762-5.5934s-1.772-5.6488-4.762-5.6488c-1.662 0-3.157.9969-3.822 2.326zm28.759-20.2137v35.997h-8.695v-35.997zm19.172 27.3023h8.03c-.886 5.7597-5.206 9.2487-11.685 9.2487-7.643 0-12.682-5.2613-12.682-13.0145 0-7.6978 5.316-13.0143 12.516-13.0143 7.642 0 11.962 5.095 11.962 12.5159v2.1598h-16.116c.277 2.9905 1.828 4.5965 4.32 4.5965 1.772 0 3.157-.7753 3.655-2.4921zm-3.821-10.0237c-2.049 0-3.434 1.2737-3.988 3.5997h7.532c-.111-2.0491-1.384-3.5997-3.544-3.5997z" id="error-description"/></svg>
|
||||
</header>
|
||||
<article>
|
||||
<p><strong>Your browser is not supported.</strong><br> Please upgrade your browser to continue.</p>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user