1
0
mirror of https://github.com/jcwimer/wrestlingApp synced 2026-05-13 17:18:58 +00:00

36 Commits

Author SHA1 Message Date
9c2a9d62ad Fixed random seeding for double elimination. Since bracket positions are already evenly distributed on top half and bottom half of the bracket, I only need to pick odd or even bracket line numbers. 2026-01-23 17:35:16 -05:00
556090c16b Fixed double elimination generate loser names for a 6 man bracket when we're placing top 8 2026-01-23 17:35:16 -05:00
86f9c03991 Fixed double elim match generation errors and added tests 2026-01-23 17:35:16 -05:00
c8764c149b Added back tournament import text for the development environment 2026-01-23 17:35:16 -05:00
fe9a9c628c Fix arguements for the tournament backup and import jobs 2026-01-22 16:59:44 -05:00
7e4b6d8fc8 Fix round 1 bracket name when the first round of the bracket is not the first round of the tournament 2026-01-19 23:25:15 -05:00
940f7b1d00 Job concurrency per tournament is 1 so we don't have to scale too much on active queue. Pages no longer refresh automatically after navigating away from the bout board. Tournament backups are no longer deleted when restoring from a backup. Cloudflare is blocking manual backup imports so I have deleted that form on the backups page. 2026-01-16 18:21:17 -05:00
52df73d14f Fixed random double elimination seeding to avoid double byes in round 1 and evenly distribute the number of round 1 matches from the top and bottom half of the bracket 2026-01-14 19:00:35 -05:00
8b03a74b1e Fixed the save seeds button on weights#show to work on mobile. Fixed the trashcan and edit icons on tournaments#show schools#show and weights#show to work on mobile. Destroy all tournament backups on tournament cleanup. Added bracket position to bout board. 2026-01-13 17:02:59 -05:00
b4bca8f10a Fixed calculate team scores button, fixed import button, fixed deleting a mat causing match deletes 2026-01-10 23:39:23 -05:00
af1f8df4b6 Fix print views 2026-01-09 23:06:24 -05:00
3576445a1c Added a turbo stream for the current and next match on mat stats page. 2026-01-09 18:37:01 -05:00
8c2ddf55ed Increased solid queue arguments limit to support tournament backups 2026-01-09 00:49:32 -05:00
cfd3e7aecd Fixed create new backup link syntax for turbo_method and made the assign_next_match button a turbo_method 2026-01-08 23:59:33 -05:00
608999cb51 Fixed create new backup link as a turbo_method and hid the baumspage importer 2026-01-08 23:51:16 -05:00
6b5308360e Fixed a bug where logged in users could not access a school with a school permission key 2026-01-06 17:24:45 -05:00
9ca6572d9b Need to bring services down before bringing them back up on deploy test 2025-12-11 14:17:27 -05:00
61dc5e3cdd Added mission control for solid queue ui. 2025-11-21 15:43:05 +05:30
af2fc3feba Fixed a test after changing links to turbo 2025-11-11 21:55:36 -05:00
793a9e3ecc All links with a confirm now use turbo 2025-11-11 21:09:24 -05:00
f73e9bfc4e Fix the reset bout board link 2025-11-11 20:55:34 -05:00
92bd06fe3c No longer using memcached. Replication settings for standalong mariadb. Use --single-transaction in mariadb replica-watcher so mysqldump does not lock tables. Added horizontal pod autoscaler to the app statefulset 2025-10-30 08:50:31 -04:00
6e9554be55 Fixed the JSON 3 deprecation in the backup and import service 2025-10-08 13:54:38 -04:00
34f1783031 Upgraded to rails 8.0.2 2025-10-08 11:35:44 -04:00
bbd2bd9b44 Fixed ads.txt 2025-10-07 15:31:04 -04:00
6ecebba70d Updated gems 2025-10-07 15:30:47 -04:00
e64751e471 Fixed name of db to replicate 2025-10-03 08:50:34 -04:00
d0f19e855f Added a mariadb replica watcher to fix replication issues 2025-09-30 16:31:43 -04:00
3e1ae22b6b Added pagination for the tournaments index page 2025-09-26 12:31:37 -04:00
15f85e439c Fixed finish_tournament_204.rake 2025-09-15 18:57:29 -04:00
c5b9783853 Fixed logout link 2025-09-15 18:32:55 -04:00
cd77268070 Trying to make the finish_tournament_204 rake job reliable 2025-09-15 18:28:35 -04:00
d61ed80287 Reload last_match and wrestler in advanced_wrestler. Moved calculate team score to the end of advance_wrestler. 2025-09-15 18:22:42 -04:00
dd5ce9bd60 Fixed my ads.txt to contain my publisher id for adsense 2025-09-15 17:31:04 -04:00
9a4e6f6597 Dynamic double elim match generation and loser name generation 2025-09-02 22:10:55 -04:00
782baedcfe Added support for 64 man brackets 2025-08-28 10:32:35 -04:00
72 changed files with 1522 additions and 786 deletions

BIN
.DS_Store vendored

Binary file not shown.

10
Gemfile
View File

@@ -2,7 +2,7 @@ source 'https://rubygems.org'
ruby '3.2.0' ruby '3.2.0'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '8.0.2' gem 'rails', '8.0.3'
# Added in rails 7.1 # Added in rails 7.1
gem 'rails-html-sanitizer' gem 'rails-html-sanitizer'
@@ -74,11 +74,9 @@ gem 'solid_cable'
gem 'puma' gem 'puma'
gem 'tzinfo-data' gem 'tzinfo-data'
gem 'daemons' gem 'daemons'
# Interface for viewing and managing background jobs # Solid Queue UI
# gem 'delayed_job_web' gem "mission_control-jobs"
# Note: solid_queue-ui is not compatible with Rails 8.0 yet
# We'll create a custom UI or wait for compatibility updates
# gem 'solid_queue_ui', '~> 0.1.1'
group :development do group :development do
# gem 'rubocop' # gem 'rubocop'

View File

@@ -1,29 +1,29 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (8.0.2) actioncable (8.0.3)
actionpack (= 8.0.2) actionpack (= 8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
actionmailbox (8.0.2) actionmailbox (8.0.3)
actionpack (= 8.0.2) actionpack (= 8.0.3)
activejob (= 8.0.2) activejob (= 8.0.3)
activerecord (= 8.0.2) activerecord (= 8.0.3)
activestorage (= 8.0.2) activestorage (= 8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
mail (>= 2.8.0) mail (>= 2.8.0)
actionmailer (8.0.2) actionmailer (8.0.3)
actionpack (= 8.0.2) actionpack (= 8.0.3)
actionview (= 8.0.2) actionview (= 8.0.3)
activejob (= 8.0.2) activejob (= 8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
mail (>= 2.8.0) mail (>= 2.8.0)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
actionpack (8.0.2) actionpack (8.0.3)
actionview (= 8.0.2) actionview (= 8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
rack (>= 2.2.4) rack (>= 2.2.4)
rack-session (>= 1.0.1) rack-session (>= 1.0.1)
@@ -31,35 +31,35 @@ GEM
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
useragent (~> 0.16) useragent (~> 0.16)
actiontext (8.0.2) actiontext (8.0.3)
actionpack (= 8.0.2) actionpack (= 8.0.3)
activerecord (= 8.0.2) activerecord (= 8.0.3)
activestorage (= 8.0.2) activestorage (= 8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (8.0.2) actionview (8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.11) erubi (~> 1.11)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
activejob (8.0.2) activejob (8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (8.0.2) activemodel (8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
activerecord (8.0.2) activerecord (8.0.3)
activemodel (= 8.0.2) activemodel (= 8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activestorage (8.0.2) activestorage (8.0.3)
actionpack (= 8.0.2) actionpack (= 8.0.3)
activejob (= 8.0.2) activejob (= 8.0.3)
activerecord (= 8.0.2) activerecord (= 8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
marcel (~> 1.0) marcel (~> 1.0)
activesupport (8.0.2) activesupport (8.0.3)
base64 base64
benchmark (>= 0.3) benchmark (>= 0.3)
bigdecimal bigdecimal
@@ -73,16 +73,16 @@ GEM
tzinfo (~> 2.0, >= 2.0.5) tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1) uri (>= 0.13.1)
ast (2.4.3) ast (2.4.3)
base64 (0.2.0) base64 (0.3.0)
bcrypt (3.1.20) bcrypt (3.1.20)
benchmark (0.4.0) benchmark (0.4.1)
bigdecimal (3.1.9) bigdecimal (3.3.0)
bootsnap (1.18.6) bootsnap (1.18.6)
msgpack (~> 1.2) msgpack (~> 1.2)
brakeman (7.0.2) brakeman (7.1.0)
racc racc
builder (3.3.0) builder (3.3.0)
bullet (8.0.7) bullet (8.0.8)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.11) uniform_notifier (~> 1.11)
bundler-audit (0.9.2) bundler-audit (0.9.2)
@@ -90,22 +90,23 @@ GEM
thor (~> 1.0) thor (~> 1.0)
cancancan (3.6.1) cancancan (3.6.1)
concurrent-ruby (1.3.5) concurrent-ruby (1.3.5)
connection_pool (2.5.3) connection_pool (2.5.4)
crass (1.0.6) crass (1.0.6)
daemons (1.4.1) daemons (1.4.1)
date (3.4.1) date (3.4.1)
drb (2.2.1) drb (2.2.3)
erb (5.0.3)
erubi (1.13.1) erubi (1.13.1)
et-orbi (1.2.11) et-orbi (1.4.0)
tzinfo tzinfo
fugit (1.11.1) fugit (1.11.2)
et-orbi (~> 1, >= 1.2.11) et-orbi (~> 1, >= 1.2.11)
raabro (~> 1.4) raabro (~> 1.4)
globalid (1.2.1) globalid (1.3.0)
activesupport (>= 6.1) activesupport (>= 6.1)
i18n (1.14.7) i18n (1.14.7)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
importmap-rails (2.1.0) importmap-rails (2.2.2)
actionpack (>= 6.0.0) actionpack (>= 6.0.0)
activesupport (>= 6.0.0) activesupport (>= 6.0.0)
railties (>= 6.0.0) railties (>= 6.0.0)
@@ -113,19 +114,19 @@ GEM
influxdb-rails (1.0.3) influxdb-rails (1.0.3)
influxdb (~> 0.6, >= 0.6.4) influxdb (~> 0.6, >= 0.6.4)
railties (>= 5.0) railties (>= 5.0)
io-console (0.8.0) io-console (0.8.1)
irb (1.15.2) irb (1.15.2)
pp (>= 0.6.0) pp (>= 0.6.0)
rdoc (>= 4.0.0) rdoc (>= 4.0.0)
reline (>= 0.4.2) reline (>= 0.4.2)
jbuilder (2.13.0) jbuilder (2.14.1)
actionview (>= 5.0.0) actionview (>= 7.0.0)
activesupport (>= 5.0.0) activesupport (>= 7.0.0)
jquery-rails (4.6.0) jquery-rails (4.6.0)
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json (2.12.2) json (2.15.1)
language_server-protocol (3.17.0.5) language_server-protocol (3.17.0.5)
lint_roller (1.1.0) lint_roller (1.1.0)
logger (1.7.0) logger (1.7.0)
@@ -137,14 +138,25 @@ GEM
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
marcel (1.0.4) marcel (1.1.0)
mini_mime (1.1.5) mini_mime (1.1.5)
minitest (5.25.5) minitest (5.25.5)
mission_control-jobs (1.1.0)
actioncable (>= 7.1)
actionpack (>= 7.1)
activejob (>= 7.1)
activerecord (>= 7.1)
importmap-rails (>= 1.2.1)
irb (~> 1.13)
railties (>= 7.1)
stimulus-rails
turbo-rails
mocha (2.7.1) mocha (2.7.1)
ruby2_keywords (>= 0.0.5) ruby2_keywords (>= 0.0.5)
msgpack (1.8.0) msgpack (1.8.0)
mysql2 (0.5.6) mysql2 (0.5.7)
net-imap (0.5.8) bigdecimal
net-imap (0.5.12)
date date
net-protocol net-protocol
net-pop (0.1.2) net-pop (0.1.2)
@@ -154,43 +166,42 @@ GEM
net-smtp (0.5.1) net-smtp (0.5.1)
net-protocol net-protocol
nio4r (2.7.4) nio4r (2.7.4)
nokogiri (1.18.8-aarch64-linux-gnu) nokogiri (1.18.10-aarch64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-aarch64-linux-musl) nokogiri (1.18.10-aarch64-linux-musl)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-arm-linux-gnu) nokogiri (1.18.10-arm-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-arm-linux-musl) nokogiri (1.18.10-arm-linux-musl)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-arm64-darwin) nokogiri (1.18.10-arm64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-x86_64-darwin) nokogiri (1.18.10-x86_64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-x86_64-linux-gnu) nokogiri (1.18.10-x86_64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.8-x86_64-linux-musl) nokogiri (1.18.10-x86_64-linux-musl)
racc (~> 1.4) racc (~> 1.4)
parallel (1.27.0) parallel (1.27.0)
parser (3.3.8.0) parser (3.3.9.0)
ast (~> 2.4.1) ast (~> 2.4.1)
racc racc
pp (0.6.2) pp (0.6.3)
prettyprint prettyprint
prettyprint (0.2.0) prettyprint (0.2.0)
prism (1.4.0) prism (1.5.1)
propshaft (1.1.0) propshaft (1.3.1)
actionpack (>= 7.0.0) actionpack (>= 7.0.0)
activesupport (>= 7.0.0) activesupport (>= 7.0.0)
rack rack
railties (>= 7.0.0)
psych (5.2.6) psych (5.2.6)
date date
stringio stringio
puma (6.6.0) puma (7.0.4)
nio4r (~> 2.0) nio4r (~> 2.0)
raabro (1.4.0) raabro (1.4.0)
racc (1.8.1) racc (1.8.1)
rack (3.1.14) rack (3.2.2)
rack-session (2.1.1) rack-session (2.1.1)
base64 (>= 0.1.0) base64 (>= 0.1.0)
rack (>= 3.0.0) rack (>= 3.0.0)
@@ -198,25 +209,25 @@ GEM
rack (>= 1.3) rack (>= 1.3)
rackup (2.2.1) rackup (2.2.1)
rack (>= 3) rack (>= 3)
rails (8.0.2) rails (8.0.3)
actioncable (= 8.0.2) actioncable (= 8.0.3)
actionmailbox (= 8.0.2) actionmailbox (= 8.0.3)
actionmailer (= 8.0.2) actionmailer (= 8.0.3)
actionpack (= 8.0.2) actionpack (= 8.0.3)
actiontext (= 8.0.2) actiontext (= 8.0.3)
actionview (= 8.0.2) actionview (= 8.0.3)
activejob (= 8.0.2) activejob (= 8.0.3)
activemodel (= 8.0.2) activemodel (= 8.0.3)
activerecord (= 8.0.2) activerecord (= 8.0.3)
activestorage (= 8.0.2) activestorage (= 8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 8.0.2) railties (= 8.0.3)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1)
activesupport (>= 5.0.1.rc1) activesupport (>= 5.0.1.rc1)
rails-dom-testing (2.2.0) rails-dom-testing (2.3.0)
activesupport (>= 5.0.0) activesupport (>= 5.0.0)
minitest minitest
nokogiri (>= 1.6) nokogiri (>= 1.6)
@@ -228,24 +239,27 @@ GEM
rails_stdout_logging rails_stdout_logging
rails_serve_static_assets (0.0.5) rails_serve_static_assets (0.0.5)
rails_stdout_logging (0.0.5) rails_stdout_logging (0.0.5)
railties (8.0.2) railties (8.0.3)
actionpack (= 8.0.2) actionpack (= 8.0.3)
activesupport (= 8.0.2) activesupport (= 8.0.3)
irb (~> 1.13) irb (~> 1.13)
rackup (>= 1.0.0) rackup (>= 1.0.0)
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0, >= 1.2.2) thor (~> 1.0, >= 1.2.2)
tsort (>= 0.2)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
rainbow (3.1.1) rainbow (3.1.1)
rake (13.2.1) rake (13.3.0)
rb-readline (0.5.5) rb-readline (0.5.5)
rdoc (6.13.1) rdoc (6.15.0)
erb
psych (>= 4.0.0) psych (>= 4.0.0)
regexp_parser (2.10.0) tsort
reline (0.6.1) regexp_parser (2.11.3)
reline (0.6.2)
io-console (~> 0.5) io-console (~> 0.5)
round_robin_tournament (0.1.2) round_robin_tournament (0.1.2)
rubocop (1.76.0) rubocop (1.81.1)
json (~> 2.3) json (~> 2.3)
language_server-protocol (~> 3.17.0.2) language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0) lint_roller (~> 1.1.0)
@@ -253,18 +267,18 @@ GEM
parser (>= 3.3.0.2) parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0) regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.45.0, < 2.0) rubocop-ast (>= 1.47.1, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0) unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.45.0) rubocop-ast (1.47.1)
parser (>= 3.3.7.2) parser (>= 3.3.7.2)
prism (~> 1.4) prism (~> 1.4)
ruby-progressbar (1.13.0) ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
sdoc (2.6.1) sdoc (2.6.4)
rdoc (>= 5.0) rdoc (>= 5.0)
securerandom (0.4.1) securerandom (0.4.1)
solid_cable (3.0.8) solid_cable (3.0.12)
actioncable (>= 7.2) actioncable (>= 7.2)
activejob (>= 7.2) activejob (>= 7.2)
activerecord (>= 7.2) activerecord (>= 7.2)
@@ -273,45 +287,46 @@ GEM
activejob (>= 7.2) activejob (>= 7.2)
activerecord (>= 7.2) activerecord (>= 7.2)
railties (>= 7.2) railties (>= 7.2)
solid_queue (1.1.5) solid_queue (1.2.1)
activejob (>= 7.1) activejob (>= 7.1)
activerecord (>= 7.1) activerecord (>= 7.1)
concurrent-ruby (>= 1.3.1) concurrent-ruby (>= 1.3.1)
fugit (~> 1.11.0) fugit (~> 1.11.0)
railties (>= 7.1) railties (>= 7.1)
thor (~> 1.3.1) thor (>= 1.3.1)
spring (4.3.0) spring (4.4.0)
sqlite3 (2.6.0-aarch64-linux-gnu) sqlite3 (2.7.4-aarch64-linux-gnu)
sqlite3 (2.6.0-aarch64-linux-musl) sqlite3 (2.7.4-aarch64-linux-musl)
sqlite3 (2.6.0-arm-linux-gnu) sqlite3 (2.7.4-arm-linux-gnu)
sqlite3 (2.6.0-arm-linux-musl) sqlite3 (2.7.4-arm-linux-musl)
sqlite3 (2.6.0-arm64-darwin) sqlite3 (2.7.4-arm64-darwin)
sqlite3 (2.6.0-x86_64-darwin) sqlite3 (2.7.4-x86_64-darwin)
sqlite3 (2.6.0-x86_64-linux-gnu) sqlite3 (2.7.4-x86_64-linux-gnu)
sqlite3 (2.6.0-x86_64-linux-musl) sqlite3 (2.7.4-x86_64-linux-musl)
stimulus-rails (1.3.4) stimulus-rails (1.3.4)
railties (>= 6.0.0) railties (>= 6.0.0)
stringio (3.1.7) stringio (3.1.7)
thor (1.3.2) thor (1.4.0)
timeout (0.4.3) timeout (0.4.3)
turbo-rails (2.0.13) tsort (0.2.0)
turbo-rails (2.0.17)
actionpack (>= 7.1.0) actionpack (>= 7.1.0)
railties (>= 7.1.0) railties (>= 7.1.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
tzinfo-data (1.2025.2) tzinfo-data (1.2025.2)
tzinfo (>= 1.0.0) tzinfo (>= 1.0.0)
unicode-display_width (3.1.4) unicode-display_width (3.2.0)
unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (~> 4.1)
unicode-emoji (4.0.4) unicode-emoji (4.1.0)
uniform_notifier (1.17.0) uniform_notifier (1.18.0)
uri (1.0.3) uri (1.0.4)
useragent (0.16.11) useragent (0.16.11)
websocket-driver (0.7.7) websocket-driver (0.8.0)
base64 base64
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
zeitwerk (2.7.2) zeitwerk (2.7.3)
PLATFORMS PLATFORMS
aarch64-linux-gnu aarch64-linux-gnu
@@ -335,11 +350,12 @@ DEPENDENCIES
influxdb-rails influxdb-rails
jbuilder jbuilder
jquery-rails jquery-rails
mission_control-jobs
mocha mocha
mysql2 mysql2
propshaft propshaft
puma puma
rails (= 8.0.2) rails (= 8.0.3)
rails-controller-testing rails-controller-testing
rails-html-sanitizer rails-html-sanitizer
rails_12factor rails_12factor

View File

@@ -148,6 +148,9 @@ Available system resources: X CPU(s), YMMMB RAM
SolidQueue plugin enabled in Puma 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 WRESTLINGDEV_MISSION_CONTROL_USER/WRESTLINGDEV_MISSION_CONTROL_PASSWORD. You can see this in `config/environments/production.rb` and `config/environments/development.rb`.
## Environment Variables ## Environment Variables
### Required Environment Variables ### Required Environment Variables
@@ -160,6 +163,8 @@ SolidQueue plugin enabled in Puma
* `WRESTLINGDEV_SECRET_KEY_BASE` - Rails application secret key (can be generated with `rake secret`) * `WRESTLINGDEV_SECRET_KEY_BASE` - Rails application secret key (can be generated with `rake secret`)
* `WRESTLINGDEV_EMAIL` - Email address (currently must be a Gmail account) * `WRESTLINGDEV_EMAIL` - Email address (currently must be a Gmail account)
* `WRESTLINGDEV_EMAIL_PWD` - Email password * `WRESTLINGDEV_EMAIL_PWD` - Email password
* `WRESTLINGDEV_MISSION_CONTROL_USER` - mission control username
* `WRESTLINGDEV_MISSION_CONTROL_PASSWORD` - mission control password
### Optional Environment Variables ### Optional Environment Variables
* `SOLID_QUEUE_IN_PUMA` - Set to "true" to run Solid Queue workers inside Puma (default in development) * `SOLID_QUEUE_IN_PUMA` - Set to "true" to run Solid Queue workers inside Puma (default in development)
@@ -207,3 +212,6 @@ The application has been migrated from using vanilla JavaScript to Hotwired Stim
- `app/assets/javascripts/application.js` - Registers and loads all controllers - `app/assets/javascripts/application.js` - Registers and loads all controllers
The importmap configuration in `config/importmap.rb` handles the loading of all JavaScript dependencies including Stimulus controllers. The importmap configuration in `config/importmap.rb` handles the loading of all JavaScript dependencies including Stimulus controllers.
# Using Repomix with LLMs
`npx repomix app test`

View File

@@ -221,12 +221,26 @@ class TournamentsController < ApplicationController
end end
def index def index
if params[:search] # Simple manual pagination to avoid introducing a gem.
# @tournaments = Tournament.limit(200).search(params[:search]).order("date DESC") per_page = 20
@tournaments = Tournament.limit(200).search_date_name(params[:search]).order("date DESC") page = params[:page].to_i > 0 ? params[:page].to_i : 1
offset = (page - 1) * per_page
if params[:search].present?
tournaments = Tournament.search_date_name(params[:search]).to_a
else else
@tournaments = Tournament.all.sort_by{|t| t.days_until_start}.first(20) tournaments = Tournament.all.to_a
end end
# Sort by distance from today (closest first)
today = Date.today
tournaments.sort_by! { |t| (t.date - today).abs }
@total_count = tournaments.size
@total_pages = (@total_count / per_page.to_f).ceil
@page = page
@per_page = per_page
@tournaments = tournaments.slice(offset, per_page) || []
end end
def show def show
@@ -286,7 +300,7 @@ class TournamentsController < ApplicationController
def reset_bout_board def reset_bout_board
@tournament.reset_and_fill_bout_board @tournament.reset_and_fill_bout_board
redirect_to tournament_path(@tournament), notice: "Successfully reset the bout board." redirect_to tournament_path(@tournament), notice: "Successfully reset the bout board. Please have all mat table workers refresh their page."
end end
def generate_school_keys def generate_school_keys

View File

@@ -1,12 +1,9 @@
class AdvanceWrestlerJob < ApplicationJob class AdvanceWrestlerJob < ApplicationJob
queue_as :default 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)
# Add a small delay to increase chance of transaction commit
# without this some matches were getting a deserialization error when running the rake task
# to finish tournaments
sleep(0.5) unless Rails.env.test?
# Get tournament from wrestler # Get tournament from wrestler
tournament = wrestler.tournament tournament = wrestler.tournament

View File

@@ -1,5 +1,6 @@
class CalculateSchoolScoreJob < ApplicationJob class CalculateSchoolScoreJob < ApplicationJob
queue_as :default queue_as :default
limits_concurrency to: 1, key: ->(school) { "tournament:#{school.tournament_id}" }
# Need for TournamentJobStatusIntegrationTest # Need for TournamentJobStatusIntegrationTest
def self.perform_sync(school) def self.perform_sync(school)

View File

@@ -1,5 +1,6 @@
class GenerateTournamentMatchesJob < ApplicationJob class GenerateTournamentMatchesJob < ApplicationJob
queue_as :default queue_as :default
limits_concurrency to: 1, key: ->(tournament) { "tournament:#{tournament.id}" }
def perform(tournament) def perform(tournament)
# Log information about the job # Log information about the job

View File

@@ -1,5 +1,6 @@
class TournamentBackupJob < ApplicationJob class TournamentBackupJob < ApplicationJob
queue_as :default queue_as :default
limits_concurrency to: 1, key: ->(tournament, *) { "tournament:#{tournament.id}" }
def perform(tournament, reason = nil) def perform(tournament, reason = nil)
# Log information about the job # Log information about the job

View File

@@ -17,6 +17,7 @@ class TournamentCleanupJob < ApplicationJob
has_real_matches = tournament.matches.where(finished: 1).where.not(win_type: 'BYE').exists? has_real_matches = tournament.matches.where(finished: 1).where.not(win_type: 'BYE').exists?
if has_real_matches if has_real_matches
tournament.tournament_backups.destroy_all
# 1. Remove all school delegates # 1. Remove all school delegates
tournament.schools.each do |school| tournament.schools.each do |school|

View File

@@ -1,5 +1,6 @@
class WrestlingdevImportJob < ApplicationJob class WrestlingdevImportJob < ApplicationJob
queue_as :default queue_as :default
limits_concurrency to: 1, key: ->(tournament, *) { "tournament:#{tournament.id}" }
def perform(tournament, import_data = nil) def perform(tournament, import_data = nil)
# Log information about the job # Log information about the job

View File

@@ -1,6 +1,20 @@
class Ability class Ability
include CanCan::Ability include CanCan::Ability
def school_permission_key_check(school_permission_key)
# Can read school if tournament is public or a valid school permission key is provided
can :read, School do |school|
school.tournament.is_public ||
(school_permission_key.present? && school.permission_key == school_permission_key)
end
# Can manage school if a valid school permission key is provided
# school_permission_key comes from app/controllers/application_controller.rb
can :manage, School do |school|
(school_permission_key.present? && school.permission_key == school_permission_key)
end
end
def initialize(user, school_permission_key = nil) def initialize(user, school_permission_key = nil)
if user if user
# LOGGED IN USER PERMISSIONS # LOGGED IN USER PERMISSIONS
@@ -46,6 +60,8 @@ class Ability
school.tournament.delegates.map(&:user_id).include?(user.id) || school.tournament.delegates.map(&:user_id).include?(user.id) ||
school.tournament.user_id == user.id school.tournament.user_id == user.id
end end
school_permission_key_check(school_permission_key)
else else
# NON LOGGED IN USER PERMISSIONS # NON LOGGED IN USER PERMISSIONS
@@ -58,18 +74,7 @@ class Ability
# SCHOOL PERMISSIONS # SCHOOL PERMISSIONS
# wrestler permissions are included with school permissions # wrestler permissions are included with school permissions
school_permission_key_check(school_permission_key)
# Can read school if tournament is public or a valid school permission key is provided
can :read, School do |school|
school.tournament.is_public ||
(school_permission_key.present? && school.permission_key == school_permission_key)
end
# Can read school if a valid school permission key is provided
# school_permission_key comes from app/controllers/application_controller.rb
can :manage, School do |school|
(school_permission_key.present? && school.permission_key == school_permission_key)
end
end end
end end
end end

View File

@@ -1,6 +1,6 @@
class Mat < ApplicationRecord class Mat < ApplicationRecord
belongs_to :tournament belongs_to :tournament
has_many :matches, dependent: :destroy has_many :matches, dependent: :nullify
has_many :mat_assignment_rules, dependent: :destroy has_many :mat_assignment_rules, dependent: :destroy
validates :name, presence: true validates :name, presence: true

View File

@@ -1,4 +1,6 @@
class Match < ApplicationRecord class Match < ApplicationRecord
include ActionView::RecordIdentifier
belongs_to :tournament, touch: true belongs_to :tournament, touch: true
belongs_to :weight, touch: true belongs_to :weight, touch: true
belongs_to :mat, touch: true, optional: true belongs_to :mat, touch: true, optional: true
@@ -10,7 +12,16 @@ class Match < ApplicationRecord
# Callback to update finished_at when a match is finished # Callback to update finished_at when a match is finished
before_save :update_finished_at before_save :update_finished_at
after_update :after_finished_actions, if: -> { # 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]
# 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
# jobs from after_update can cause jobs to run before the transaction commits,
# which leads to jobs observing stale data and not performing advancement.
after_commit :after_finished_actions, on: :update, if: -> {
saved_change_to_finished? || saved_change_to_finished? ||
saved_change_to_winner_id? || saved_change_to_winner_id? ||
saved_change_to_win_type? || saved_change_to_win_type? ||
@@ -30,7 +41,8 @@ class Match < ApplicationRecord
self.mat.assign_next_match self.mat.assign_next_match
end end
advance_wrestlers advance_wrestlers
calculate_school_points # School point calculation has move to the end of advance wrestler
# calculate_school_points
end end
end end
@@ -44,7 +56,7 @@ class Match < ApplicationRecord
errors.add(:winner_id, "cannot be blank") errors.add(:winner_id, "cannot be blank")
end end
if win_type == "Pin" and ! score.match(/^[0-5]?[0-9]:[0-5][0-9]/) 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 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]/) 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") errors.add(:score, "needs to be in Number-Number format when win type is Decision, Tech Fall, and Major example: 10-2")
@@ -189,6 +201,7 @@ class Match < ApplicationRecord
end end
def w1_bracket_name def w1_bracket_name
first_round = self.weight.matches.sort_by{|m| m.round}.first.round
return_string = "" return_string = ""
return_string_ending = "" return_string_ending = ""
if self.w1 and self.winner_id == self.w1 if self.w1 and self.winner_id == self.w1
@@ -196,7 +209,7 @@ class Match < ApplicationRecord
return_string_ending = return_string_ending + "</strong>" return_string_ending = return_string_ending + "</strong>"
end end
if self.w1 != nil if self.w1 != nil
if self.round == 1 if self.round == first_round
return_string = return_string + "#{wrestler1.long_bracket_name}" return_string = return_string + "#{wrestler1.long_bracket_name}"
else else
return_string = return_string + "#{wrestler1.short_bracket_name}" return_string = return_string + "#{wrestler1.short_bracket_name}"
@@ -208,6 +221,7 @@ class Match < ApplicationRecord
end end
def w2_bracket_name def w2_bracket_name
first_round = self.weight.matches.sort_by{|m| m.round}.first.round
return_string = "" return_string = ""
return_string_ending = "" return_string_ending = ""
if self.w2 and self.winner_id == self.w2 if self.w2 and self.winner_id == self.w2
@@ -215,7 +229,7 @@ class Match < ApplicationRecord
return_string_ending = return_string_ending + "</strong>" return_string_ending = return_string_ending + "</strong>"
end end
if self.w2 != nil if self.w2 != nil
if self.round == 1 if self.round == first_round
return_string = return_string + "#{wrestler2.long_bracket_name}" return_string = return_string + "#{wrestler2.long_bracket_name}"
else else
return_string = return_string + "#{wrestler2.short_bracket_name}" return_string = return_string + "#{wrestler2.short_bracket_name}"
@@ -328,4 +342,26 @@ class Match < ApplicationRecord
self.finished_at = Time.current.utc self.finished_at = Time.current.utc
end end
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.unfinished_matches.first,
next_match: mat.unfinished_matches.second,
show_next_bout_button: true
}
)
end
end
end end

View File

@@ -156,14 +156,14 @@ class Tournament < ApplicationRecord
def double_elim_number_of_wrestlers_error def double_elim_number_of_wrestlers_error
error_string = "" 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 > 32} 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| weights_with_too_many_wrestlers.each do |weight|
error_string = error_string + " The weight class #{weight.max} has more than 16 wrestlers." error_string = error_string + " The weight class #{weight.max} has more than 64 wrestlers."
end end
weight_with_too_few_wrestlers.each do |weight| 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
end end
return error_string return error_string

View File

@@ -2,9 +2,13 @@ class Wrestler < ApplicationRecord
belongs_to :school, touch: true belongs_to :school, touch: true
belongs_to :weight, touch: true belongs_to :weight, touch: true
has_one :tournament, through: :weight has_one :tournament, through: :weight
has_many :deductedPoints, class_name: "Teampointadjust", dependent: :destroy
## Matches association
# Rails associations expect only a single column so we cannot do a w1 OR w2
# So we have to create two associations and combine them with the all_matches method
has_many :matches_as_w1, ->(wrestler){ where(weight_id: wrestler.weight_id) }, class_name: 'Match', foreign_key: 'w1' has_many :matches_as_w1, ->(wrestler){ where(weight_id: wrestler.weight_id) }, class_name: 'Match', foreign_key: 'w1'
has_many :matches_as_w2, ->(wrestler){ where(weight_id: wrestler.weight_id) }, class_name: 'Match', foreign_key: 'w2' has_many :matches_as_w2, ->(wrestler){ where(weight_id: wrestler.weight_id) }, class_name: 'Match', foreign_key: 'w2'
has_many :deductedPoints, class_name: "Teampointadjust", dependent: :destroy ##
attr_accessor :poolAdvancePoints, :originalId, :swapId attr_accessor :poolAdvancePoints, :originalId, :swapId
validates :name, :weight_id, :school_id, presence: true validates :name, :weight_id, :school_id, presence: true

View File

@@ -8,16 +8,18 @@ class AdvanceWrestler
def advance def advance
# Use perform_later which will execute based on centralized adapter config # Use perform_later which will execute based on centralized adapter config
# This will be converted to inline execution in test environment by ActiveJob # 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 end
def advance_raw def advance_raw
@last_match.reload
@wrestler.reload
if @last_match && @last_match.finished? if @last_match && @last_match.finished?
pool_to_bracket_advancement if @tournament.tournament_type == "Pool to bracket" 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" 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" DoubleEliminationAdvance.new(@wrestler, @last_match).bracket_advancement if @tournament.tournament_type.include? "Regular Double Elimination"
end end
@wrestler.school.calculate_score
end end
def pool_to_bracket_advancement def pool_to_bracket_advancement

View File

@@ -6,11 +6,14 @@ class DoubleEliminationGenerateLoserNames
# Entry point: assign loser placeholders and advance any byes # Entry point: assign loser placeholders and advance any byes
def assign_loser_names def assign_loser_names
@tournament.weights.each do |weight| @tournament.weights.each do |weight|
# only assign loser names if there's conso matches to be had
if weight.calculate_bracket_size > 2
assign_loser_names_for_weight(weight) assign_loser_names_for_weight(weight)
advance_bye_matches_championship(weight) advance_bye_matches_championship(weight)
advance_bye_matches_consolation(weight) advance_bye_matches_consolation(weight)
end end
end end
end
private private
@@ -87,7 +90,7 @@ class DoubleEliminationGenerateLoserNames
end end
# 5th/6th place # 5th/6th place
if bracket_size >= 5 && num_placers >= 6 if bracket_size >= 5 && num_placers >= 6 && weight.wrestlers.size > 4
conso_semis = matches.select { |m| m.bracket_position == "Conso Semis" } conso_semis = matches.select { |m| m.bracket_position == "Conso Semis" }
.sort_by(&:bracket_position_number) .sort_by(&:bracket_position_number)
if conso_semis.size >= 2 if conso_semis.size >= 2
@@ -98,7 +101,7 @@ class DoubleEliminationGenerateLoserNames
end end
# 7th/8th place # 7th/8th place
if bracket_size >= 7 && num_placers >= 8 if bracket_size >= 7 && num_placers >= 8 && weight.wrestlers.size > 6
conso_quarters = matches.select { |m| m.bracket_position == "Conso Quarter" } conso_quarters = matches.select { |m| m.bracket_position == "Conso Quarter" }
.sort_by(&:bracket_position_number) .sort_by(&:bracket_position_number)
if conso_quarters.size >= 2 if conso_quarters.size >= 2

View File

@@ -41,7 +41,11 @@ class TournamentBackupService
mat_assignment_rules: @tournament.mat_assignment_rules.map do |rule| mat_assignment_rules: @tournament.mat_assignment_rules.map do |rule|
rule.attributes.merge( rule.attributes.merge(
mat: Mat.find_by(id: rule.mat_id)&.attributes.slice("name"), mat: Mat.find_by(id: rule.mat_id)&.attributes.slice("name"),
weight_classes: rule.weight_classes.map do |weight_id| # Emit the human-readable max values under a distinct key to avoid
# colliding with the raw DB-backed "weight_classes" attribute (which
# is stored as a comma-separated string). Using a different key
# prevents duplicate JSON keys when symbols and strings are both present.
"weight_class_maxes" => rule.weight_classes.map do |weight_id|
Weight.find_by(id: weight_id)&.max Weight.find_by(id: weight_id)&.max
end end
) )

View File

@@ -18,15 +18,15 @@ class TournamentSeeding
def random_seeding(wrestlers, bracket_size) def random_seeding(wrestlers, bracket_size)
half_of_bracket = bracket_size / 2 half_of_bracket = bracket_size / 2
available_bracket_lines = (1..bracket_size).to_a 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 # 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 = wrestlers.select{|w| w.bracket_line != nil }
wrestlers_with_bracket_lines.each do |wrestler| wrestlers_with_bracket_lines.each do |wrestler|
available_bracket_lines.delete(wrestler.bracket_line) available_bracket_lines.delete(wrestler.bracket_line)
first_half_available_bracket_lines.delete(wrestler.bracket_line)
end 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 } wrestlers_without_bracket_lines = wrestlers.select{|w| w.bracket_line == nil }
if @tournament.tournament_type == "Pool to bracket" if @tournament.tournament_type == "Pool to bracket"
wrestlers_without_bracket_lines.shuffle.each do |wrestler| wrestlers_without_bracket_lines.shuffle.each do |wrestler|
@@ -38,15 +38,10 @@ class TournamentSeeding
else else
# Iterrate over the list randomly # Iterrate over the list randomly
wrestlers_without_bracket_lines.shuffle.each do |wrestler| wrestlers_without_bracket_lines.shuffle.each do |wrestler|
if first_half_available_bracket_lines.size > 0 if available_bracket_lines_to_use.size > 0
random_available_bracket_line = first_half_available_bracket_lines.sample bracket_line_to_use = available_bracket_lines_to_use.first
wrestler.bracket_line = random_available_bracket_line wrestler.bracket_line = bracket_line_to_use
available_bracket_lines.delete(random_available_bracket_line) available_bracket_lines_to_use.delete(bracket_line_to_use)
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)
end end
end end
end end
@@ -81,4 +76,24 @@ class TournamentSeeding
end end
return wrestlers 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
end end

View File

@@ -41,7 +41,6 @@ class WrestlingdevImporter
@tournament.matches.destroy_all @tournament.matches.destroy_all
@tournament.mat_assignment_rules.destroy_all # Explicitly destroy rules (might be redundant if Mat cascades) @tournament.mat_assignment_rules.destroy_all # Explicitly destroy rules (might be redundant if Mat cascades)
@tournament.delegates.destroy_all @tournament.delegates.destroy_all
@tournament.tournament_backups.destroy_all
@tournament.tournament_job_statuses.destroy_all @tournament.tournament_job_statuses.destroy_all
# Note: Teampointadjusts are deleted via School/Wrestler cascade # Note: Teampointadjusts are deleted via School/Wrestler cascade
end end
@@ -87,16 +86,29 @@ class WrestlingdevImporter
mat_name = rule_attributes.dig("mat", "name") mat_name = rule_attributes.dig("mat", "name")
mat = Mat.find_by(name: mat_name, tournament_id: @tournament.id) mat = Mat.find_by(name: mat_name, tournament_id: @tournament.id)
# Map max values of weight_classes to their new IDs # Prefer the new "weight_class_maxes" key emitted by backups (human-readable
new_weight_classes = rule_attributes["weight_classes"].map do |max_value| # max values). If not present, fall back to the legacy "weight_classes"
# value which may be a comma-separated string or an array of IDs.
if rule_attributes.key?("weight_class_maxes") && rule_attributes["weight_class_maxes"].respond_to?(:map)
new_weight_classes = rule_attributes["weight_class_maxes"].map do |max_value|
Weight.find_by(max: max_value, tournament_id: @tournament.id)&.id Weight.find_by(max: max_value, tournament_id: @tournament.id)&.id
end.compact end.compact
elsif rule_attributes["weight_classes"].is_a?(Array)
# Already an array of IDs
new_weight_classes = rule_attributes["weight_classes"].map(&:to_i)
elsif rule_attributes["weight_classes"].is_a?(String)
# Comma-separated IDs stored in the DB column; split into integers.
new_weight_classes = rule_attributes["weight_classes"].to_s.split(",").map(&:strip).reject(&:empty?).map(&:to_i)
else
new_weight_classes = []
end
# Extract bracket_positions and rounds # Extract bracket_positions and rounds (leave as-is; model will coerce if needed)
bracket_positions = rule_attributes["bracket_positions"] bracket_positions = rule_attributes["bracket_positions"]
rounds = rule_attributes["rounds"] rounds = rule_attributes["rounds"]
rule_attributes.except!("id", "mat", "tournament_id", "weight_classes") # Remove any keys we don't want to mass-assign (including both old/new weight keys)
rule_attributes.except!("id", "mat", "tournament_id", "weight_classes", "weight_class_maxes")
MatAssignmentRule.create( MatAssignmentRule.create(
rule_attributes.merge( rule_attributes.merge(

View File

@@ -19,7 +19,7 @@
<a class="dropdown-toggle" data-toggle="dropdown" href="#"><%= current_user.email %> <a class="dropdown-toggle" data-toggle="dropdown" href="#"><%= current_user.email %>
<span class="caret"></span></a> <span class="caret"></span></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><%= link_to "Log out", logout_path, method: :delete %></li> <li><%= button_to "Log out", logout_path, method: :delete, class: 'btn btn-link', form: { class: 'navbar-logout-form' } %></li>
<li><%= link_to "Edit profile", edit_user_path(current_user) %></li> <li><%= link_to "Edit profile", edit_user_path(current_user) %></li>
<li><%= link_to "My tournaments and schools", "/static_pages/my_tournaments" %></li> <li><%= link_to "My tournaments and schools", "/static_pages/my_tournaments" %></li>
</ul> </ul>

View File

@@ -43,7 +43,7 @@
<li><%= link_to "Deduct Team Points" , "/tournaments/#{@tournament.id}/teampointadjust" %></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 "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 'Manage Backups', tournament_tournament_backups_path(@tournament) %></li>
<li><%= link_to "Reset Bout Board", reset_bout_board_tournament_path(@tournament), method: :post, data: { confirm: "Are you sure you want to reset the bout board?" } %></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>
<% if can? :destroy, @tournament %> <% if can? :destroy, @tournament %>
<li><%= link_to "Tournament Delegation" , "/tournaments/#{@tournament.id}/delegate" %></li> <li><%= link_to "Tournament Delegation" , "/tournaments/#{@tournament.id}/delegate" %></li>
<% end %> <% end %>
@@ -55,13 +55,13 @@
<% end %> <% end %>
<% end %> <% end %>
<li><strong>Time Savers</strong></li> <li><strong>Time Savers</strong></li>
<li><%= link_to "Create Boys High School Weights (106-285)" , "/tournaments/#{@tournament.id}/create_custom_weights?customValue=#{Weight::HS_WEIGHT_CLASSES}",data: { confirm: 'Are you sure? This will delete all current weights.' } %></li> <li><%= link_to "Create Boys High School Weights (106-285)" , "/tournaments/#{@tournament.id}/create_custom_weights?customValue=#{Weight::HS_WEIGHT_CLASSES}",data: { turbo_method: :get, turbo_confirm: 'Are you sure? This will delete all current weights.' } %></li>
<li><%= link_to "Create Girls High School Weights (100-235)" , "/tournaments/#{@tournament.id}/create_custom_weights?customValue=#{Weight::HS_GIRLS_WEIGHT_CLASSES}",data: { confirm: 'Are you sure? This will delete all current weights.' } %></li> <li><%= link_to "Create Girls High School Weights (100-235)" , "/tournaments/#{@tournament.id}/create_custom_weights?customValue=#{Weight::HS_GIRLS_WEIGHT_CLASSES}",data: { turbo_method: :get, turbo_confirm: 'Are you sure? This will delete all current weights.' } %></li>
<li><%= link_to "Create Boys Middle School Weights (80-245)" , "/tournaments/#{@tournament.id}/create_custom_weights?customValue=#{Weight::MS_WEIGHT_CLASSES}",data: { confirm: 'Are you sure? This will delete all current weights.' } %></li> <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: { 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><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: { confirm: 'Are you sure? This will delete all current matches.' } %></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> <li><%= link_to "Export Data" , "/tournaments/#{@tournament.id}/export?print=true", target: :_blank %></li>
</ul> </ul>
<% end %> <% end %>

View File

@@ -23,7 +23,7 @@
<td><%= Array(rule.rounds).join(", ") %></td> <td><%= Array(rule.rounds).join(", ") %></td>
<td> <td>
<%= link_to '', edit_tournament_mat_assignment_rule_path(@tournament, rule), class: "fas fa-edit" %> <%= link_to '', edit_tournament_mat_assignment_rule_path(@tournament, rule), class: "fas fa-edit" %>
<%= link_to '', tournament_mat_assignment_rule_path(@tournament, rule), method: :delete, data: { confirm: "Are you sure?" }, class: "fas fa-trash-alt" %> <%= link_to '', tournament_mat_assignment_rule_path(@tournament, rule), data: { turbo_method: :delete, turbo_confirm: "Are you sure?" }, class: "fas fa-trash-alt" %>
</td> </td>
</tr> </tr>
<% end %> <% end %>

View File

@@ -0,0 +1,37 @@
<% @mat = mat %>
<% @match = local_assigns[:match] || mat.unfinished_matches.first %>
<% @next_match = local_assigns[:next_match] || mat.unfinished_matches.second %>
<% @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 %>

View File

@@ -1,9 +1,12 @@
<h3>Mat <%= @mat.name %></h3> <h3>Mat <%= @mat.name %></h3>
<h3>Tournament: <%= @mat.tournament.name %></h3> <h3>Tournament: <%= @mat.tournament.name %></h3>
<% if @match %> <%= turbo_stream_from @mat %>
<%= render 'matches/matchstats' %>
<% else %>
<p>No matches assigned to this mat.</p>
<% end %>
<%= 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 %>

View File

@@ -76,8 +76,12 @@
<% delete_wrestler_path_with_key = wrestler_path(wrestler) %> <% 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? %> <% 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 edit_wrestler_path_with_key, class: "text-decoration-none" do %>
<%= link_to '', delete_wrestler_path_with_key, method: :delete, data: { confirm: "Are you sure you want to delete #{wrestler.name}? This will delete all of his matches." }, class: "fas fa-trash-alt" %> <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> </td>
<% end %> <% end %>
</tr> </tr>
@@ -101,8 +105,12 @@
<% delete_wrestler_path_with_key = wrestler_path(wrestler) %> <% 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? %> <% 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 edit_wrestler_path_with_key, class: "text-decoration-none" do %>
<%= link_to '', delete_wrestler_path_with_key, method: :delete, data: { confirm: "Are you sure you want to delete #{wrestler.name}? This will delete all of his matches." }, class: "fas fa-trash-alt" %> <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> </td>
<% end %> <% end %>
</tr> </tr>
@@ -111,6 +119,6 @@
</tbody> </tbody>
</table> </table>
<% if can? :manage, @school %> <%# if can? :manage, @school %>
<%= render 'baums_roster_import' %> <%#= render 'baums_roster_import' %>
<% end %> <%# end %>

View File

@@ -24,7 +24,7 @@
<td> <td>
<%= link_to '', edit_tournament_path(tournament), :class=>"fas fa-edit" %> <%= link_to '', edit_tournament_path(tournament), :class=>"fas fa-edit" %>
<% if can? :destroy, tournament %> <% if can? :destroy, tournament %>
<%= link_to '', tournament, method: :delete, data: { confirm: "Are you sure you want to delete #{tournament.name}?" }, :class=>"fas fa-trash-alt" %> <%= link_to '', tournament, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{tournament.name}?" }, :class=>"fas fa-trash-alt" %>
<% end %> <% end %>
</td> </td>
<% end %> <% end %>

View File

@@ -8,7 +8,7 @@ and will also delete all of your current data. It's best to use the create backu
<tr> <tr>
<th>Backup Created At</th> <th>Backup Created At</th>
<th>Backup Reason</th> <th>Backup Reason</th>
<th><%= link_to ' Create New Backup', tournament_tournament_backups_path(@tournament), method: :post, class: 'fas fa-plus'%></th> <th><%= link_to ' Create New Backup', tournament_tournament_backups_path(@tournament), data: { turbo_method: :post }, class: 'fas fa-plus'%></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -19,20 +19,20 @@ and will also delete all of your current data. It's best to use the create backu
</td> </td>
<td><%= backup.backup_reason.presence || 'No reason provided' %></td> <td><%= backup.backup_reason.presence || 'No reason provided' %></td>
<td> <td>
<%= link_to '', restore_tournament_tournament_backup_path(@tournament, backup), method: :post, data: { confirm: "This will restore the backup from #{backup.created_at.strftime('%Y-%m-%d %H:%M:%S')}. It will delete all current data from the tournament in order to restore the backup." }, class: 'fas fa-undo-alt text-warning', title: 'Restore Backup' %> <%= link_to '', restore_tournament_tournament_backup_path(@tournament, backup), data: { turbo_method: :post, turbo_confirm: "This will restore the backup from #{backup.created_at.strftime('%Y-%m-%d %H:%M:%S')}. It will delete all current data from the tournament in order to restore the backup." }, class: 'fas fa-undo-alt text-warning', title: 'Restore Backup' %>
<%= link_to '', tournament_tournament_backup_path(@tournament, backup), method: :delete, data: { confirm: 'Are you sure you want to delete this backup?' }, class: 'fas fa-trash-alt', title: 'Delete Backup' %> <%= link_to '', tournament_tournament_backup_path(@tournament, backup), data: { turbo_method: :delete, turbo_confirm: 'Are you sure you want to delete this backup?' }, class: 'fas fa-trash-alt', title: 'Delete Backup' %>
</td> </td>
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>
</table> </table>
<br><br> <br><br>
<h3>Import Manual Backup</h3> <% if ENV["RAILS_ENV"] == "development" %>
<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| %> <%= form_for(:tournament, url: import_manual_tournament_tournament_backups_path(@tournament)) do |f| %>
<div class="field"> <div class="field">
<%= f.label 'Import text' %><br> <%= f.label 'Import text' %><br>
<%= f.text_area :import_text, cols: "30", rows: "20" %> <%= f.text_area :import_text, cols: "30", rows: "20" %>
</div> </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.' } %> <%= 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 %> <% end %>

View File

@@ -2,7 +2,11 @@
<div class="round"> <div class="round">
<div class="game"> <div class="game">
<div class="game-top "><%= match.w1_bracket_name.html_safe %> <span></span></div> <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> <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 class="game-bottom "><%= match.w2_bracket_name.html_safe %><span></span></div>
</div> </div>
</div> </div>

View File

@@ -11,7 +11,7 @@ table.smallText tr td { font-size: 10px; }
min-width: 150px; min-width: 150px;
min-height: 50px; min-height: 50px;
/*background-color: #ddd;*/ /*background-color: #ddd;*/
border: 1px solid #ddd; border: 1px solid #000; /* Dark border so boxes stay visible when printed */
margin: 5px; margin: 5px;
} }
@@ -56,7 +56,7 @@ table.smallText tr td { font-size: 10px; }
} }
.game-top { .game-top {
border-bottom:1px solid #ddd; border-bottom:1px solid #000;
padding: 2px; padding: 2px;
min-height: 12px; min-height: 12px;
} }
@@ -77,13 +77,13 @@ table.smallText tr td { font-size: 10px; }
} }
.bracket-winner { .bracket-winner {
border-bottom:1px solid #ddd; border-bottom:1px solid #000;
padding: 2px; padding: 2px;
min-height: 12px; min-height: 12px;
} }
.game-bottom { .game-bottom {
border-top:1px solid #ddd; border-top:1px solid #000;
padding: 2px; padding: 2px;
min-height: 12px; min-height: 12px;
} }

View File

@@ -2,7 +2,11 @@
<% @round_matches.sort_by{|m| m.bracket_position_number}.each do |match| %> <% @round_matches.sort_by{|m| m.bracket_position_number}.each do |match| %>
<div class="game"> <div class="game">
<div class="game-top "><%= match.w1_bracket_name.html_safe %> <span></span></div> <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 %>&nbsp;</div>
<% else %>
<div class="bout-number"><%= link_to match.bout_number, spectate_match_path(match) %> <%= match.bracket_score_string %>&nbsp;</div> <div class="bout-number"><%= link_to match.bout_number, spectate_match_path(match) %> <%= match.bracket_score_string %>&nbsp;</div>
<% end %>
<div class="game-bottom "><%= match.w2_bracket_name.html_safe %><span></span></div> <div class="game-bottom "><%= match.w2_bracket_name.html_safe %><span></span></div>
</div> </div>
<% end %> <% end %>

View File

@@ -42,7 +42,7 @@
<% @users_delegates.each do |delegate| %> <% @users_delegates.each do |delegate| %>
<tr> <tr>
<td><%= delegate.user.email %></td> <td><%= delegate.user.email %></td>
<td><%= link_to 'Remove permissions', "/tournaments/#{@tournament.id}/#{delegate.id}/remove_delegate", method: :delete, confirm: 'Are you sure?', :class=>"btn btn-danger btn-sm" %></td> <td><%= link_to 'Remove permissions', "/tournaments/#{@tournament.id}/#{delegate.id}/remove_delegate", data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, :class=>"btn btn-danger btn-sm" %></td>
</tr> </tr>
<% end %> <% end %>

View File

@@ -1,34 +1,35 @@
<h1>Upcoming Tournaments</h1> <%= form_tag(tournaments_path, :method => "get", id: "search-form") do %> <h1>Upcoming Tournaments</h1>
<%= form_tag(tournaments_path, :method => "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Tournaments" %> <%= text_field_tag :search, params[:search], placeholder: "Search Tournaments" %>
<%= submit_tag "Search" %> <%= submit_tag "Search" %>
<% end %> <% end %>
<p>Search by name or date YYYY-MM-DD</p> <p>Search by name or date YYYY-MM-DD</p>
<script>
// $(document).ready(function() {
// $('#tournamentList').dataTable();
// pagingType: "bootstrap";
// } );
</script>
<br> <br>
<table class="table table-hover table-condensed" id="tournamentList"> <table class="table table-hover table-condensed" id="tournamentList">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Date</th> <th>Date</th>
<th><% if user_signed_in? %><%= link_to ' New Tournament', new_tournament_path, :class=>"fas fa-plus" %></th><% end %> <th>
<% if user_signed_in? %>
<%= link_to ' New Tournament', new_tournament_path, :class=>"fas fa-plus" %>
<% end %>
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<% @tournaments.each do |tournament| %> <% @tournaments.each do |tournament| %>
<tr> <tr>
<td><%= link_to "#{tournament.name}", tournament %></td> <td><%= link_to tournament.name, tournament %></td>
<td><%= tournament.date %></td> <td><%= tournament.date %></td>
<td> <td>
<% if can? :manage, tournament %> <% if can? :manage, tournament %>
<%= link_to '', edit_tournament_path(tournament), :class=>"fas fa-edit" %> <%= link_to '', edit_tournament_path(tournament), :class=>"fas fa-edit" %>
<% if can? :destroy, tournament %> <% if can? :destroy, tournament %>
<%= link_to '', tournament, method: :delete, data: { confirm: "Are you sure you want to delete #{tournament.name}?" }, :class=>"fas fa-trash-alt" %> <%= link_to '', tournament, data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete #{tournament.name}?" }, :class=>"fas fa-trash-alt" %>
<% end %> <% end %>
<% end %> <% end %>
</td> </td>
@@ -37,6 +38,48 @@
</tbody> </tbody>
</table> </table>
<br> <%# Pagination controls %>
<% if @total_pages.present? && @total_pages > 1 %>
<nav aria-label="Tournaments pagination">
<ul class="pagination">
<%# Previous link %>
<% if @page > 1 %>
<li class="page-item">
<%= link_to 'Previous', tournaments_path(page: @page - 1, search: params[:search]), class: "page-link" %>
</li>
<% else %>
<li class="page-item disabled"><span class="page-link">Previous</span></li>
<% end %>
<%# Page number links (limit displayed pages for large counts) %>
<% 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, tournaments_path(page: p, search: params[:search]), class: "page-link" %></li>
<% end %>
<% end %>
<%# Next link %>
<% if @page < @total_pages %>
<li class="page-item">
<%= link_to 'Next', tournaments_path(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 %> tournaments
</p>
<% end %>

View File

@@ -34,3 +34,6 @@
<% end %> <% end %>
</tbody> </tbody>
</table> </table>
<br>
<p>Total matches without byes: <%= @matches.select{|m| m.loser1_name != 'BYE' and m.loser2_name != 'BYE'}.size %></p>
<p>Unfinished matches: <%= @matches.select{|m| m.finished != 1 and m.loser1_name != 'BYE' and m.loser2_name != 'BYE'}.size %></p>

View File

@@ -78,7 +78,7 @@
<tr> <tr>
<td><%= delegate.user.email %></td> <td><%= delegate.user.email %></td>
<td><%= delegate.school.name %></td> <td><%= delegate.school.name %></td>
<td><%= link_to 'Remove permissions', "/tournaments/#{@tournament.id}/#{delegate.id}/remove_school_delegate", method: :delete, confirm: 'Are you sure?', :class=>"btn btn-danger btn-sm" %></td> <td><%= link_to 'Remove permissions', "/tournaments/#{@tournament.id}/#{delegate.id}/remove_school_delegate", data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, :class=>"btn btn-danger btn-sm" %></td>
</tr> </tr>
<% end %> <% end %>

View File

@@ -70,9 +70,13 @@
</td> </td>
<td> <td>
<% if can? :manage, school %> <% 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 %> <% if can? :manage, @tournament %>
<%= link_to '', school, method: :delete, data: { 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 %>
<% end %> <% end %>
</td> </td>
@@ -105,8 +109,12 @@
<td><%= weight.bracket_size %></td> <td><%= weight.bracket_size %></td>
<% if can? :manage, @tournament %> <% if can? :manage, @tournament %>
<td> <td>
<%= link_to '', edit_weight_path(weight), :class=>"fas fa-edit" %> <%= link_to edit_weight_path(weight), class: "text-decoration-none" do %>
<%= link_to '', weight, method: :delete, data: { confirm: "Are you sure you want to delete the #{weight.max} weight class?" }, :class=>"fas fa-trash-alt" %> <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> </td>
<% end %> <% end %>
</tr> </tr>
@@ -130,8 +138,10 @@
<td><%= link_to "Mat #{mat.name}", mat %></td> <td><%= link_to "Mat #{mat.name}", mat %></td>
<% if can? :manage, @tournament %> <% if can? :manage, @tournament %>
<td> <td>
<%= link_to '', mat, method: :delete, data: { 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 %>
<%= link_to '', "/mats/#{mat.id}/assign_next_match", method: :post, :class=>"fas fa-solid fa-arrow-right" %> <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> </td>
<% end %> <% end %>
</tr> </tr>

View File

@@ -47,7 +47,7 @@
<% end %> <% end %>
</td> </td>
<td><%= point_adjustment.points %></td> <td><%= point_adjustment.points %></td>
<td><%= link_to 'Remove Point Adjustment', "/tournaments/#{@tournament.id}/#{point_adjustment.id}/remove_teampointadjust", method: :delete, confirm: 'Are you sure?', :class=>"btn btn-danger btn-sm" %></td> <td><%= link_to 'Remove Point Adjustment', "/tournaments/#{@tournament.id}/#{point_adjustment.id}/remove_teampointadjust", data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }, :class=>"btn btn-danger btn-sm" %></td>
</tr> </tr>
<% end %> <% end %>

View File

@@ -5,7 +5,23 @@
// } ); // } );
</script> </script>
<script> <script>
setTimeout("location.reload(true);",30000); const setUpMatchesRefresh = () => {
if (window.__upMatchesRefreshTimeout) {
clearTimeout(window.__upMatchesRefreshTimeout);
}
window.__upMatchesRefreshTimeout = setTimeout(() => {
window.location.reload(true);
}, 30000);
};
document.addEventListener("turbo:load", setUpMatchesRefresh);
// turbo:before-cache stops the timer refresh from occurring if you navigate away from up_matches
document.addEventListener("turbo:before-cache", () => {
if (window.__upMatchesRefreshTimeout) {
clearTimeout(window.__upMatchesRefreshTimeout);
window.__upMatchesRefreshTimeout = null;
}
});
</script> </script>
<br> <br>
<br> <br>
@@ -28,10 +44,34 @@
<% @mats.each.map do |m| %> <% @mats.each.map do |m| %>
<tr> <tr>
<td><%= m.name %></td> <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>
<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> <% if m.unfinished_matches.first %><strong><%=m.unfinished_matches.first.bout_number%></strong> (<%= m.unfinished_matches.first.bracket_position %>)<br>
<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> <%= m.unfinished_matches.first.weight_max %> lbs
<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> <br><%= m.unfinished_matches.first.w1_bracket_name %> vs. <br>
<%= 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.bracket_position %>)<br>
<%= m.unfinished_matches.second.weight_max %> lbs
<br><%= m.unfinished_matches.second.w1_bracket_name %> vs. <br>
<%= 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.bracket_position %>)<br>
<%= m.unfinished_matches.third.weight_max %> lbs
<br><%= m.unfinished_matches.third.w1_bracket_name %> vs. <br>
<%= 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.bracket_position %>)<br>
<%= m.unfinished_matches.fourth.weight_max %> lbs
<br><%= m.unfinished_matches.fourth.w1_bracket_name %> vs. <br>
<%= m.unfinished_matches.fourth.w2_bracket_name %>
<% end %>
</td>
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>

View File

@@ -1,6 +1,7 @@
<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>
<br> <br>
<%= form_tag @wrestlers_update_path do %>
<table class="table table-hover table-condensed"> <table class="table table-hover table-condensed">
<thead> <thead>
<tr> <tr>
@@ -13,7 +14,6 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<%= form_tag @wrestlers_update_path do %>
<% @wrestlers.sort_by{|w| [w.original_seed ? 0 : 1, w.original_seed || 0]}.each do |wrestler| %> <% @wrestlers.sort_by{|w| [w.original_seed ? 0 : 1, w.original_seed || 0]}.each do |wrestler| %>
<% if wrestler.weight_id == @weight.id %> <% if wrestler.weight_id == @weight.id %>
<tr> <tr>
@@ -35,7 +35,9 @@
<% end %></td> <% end %></td>
<% if can? :manage, @tournament %> <% if can? :manage, @tournament %>
<td> <td>
<%= link_to '', wrestler, method: :delete, data: { confirm: "Are you sure you want to delete #{wrestler.name}? THIS WILL DELETE ALL MATCHES." } , :class=>"fas fa-trash-alt" %> <%= 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> </td>
<% end %> <% end %>
</tr> </tr>

View File

@@ -1,4 +1,2 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
exec "./bin/rails", "server", *ARGV
# Start Rails server with defaults
exec "bin/rails", "server", *ARGV

View File

@@ -55,5 +55,7 @@ module Wrestling
# Set cache format version to a value supported by Rails 8.0 # Set cache format version to a value supported by Rails 8.0
# Valid values are 7.0 or 7.1 # Valid values are 7.0 or 7.1
config.active_support.cache_format_version = 7.1 config.active_support.cache_format_version = 7.1
config.load_defaults 8.0
end end
end end

View File

@@ -99,4 +99,7 @@ Rails.application.configure do
# Nobuild in development # Nobuild in development
config.assets.build_assets = false config.assets.build_assets = false
MissionControl::Jobs.http_basic_auth_user = "dev"
MissionControl::Jobs.http_basic_auth_password = "secret"
end end

View File

@@ -120,4 +120,7 @@ Rails.application.configure do
config.assets.compile = true config.assets.compile = true
# Generate digests for assets URLs. # Generate digests for assets URLs.
config.assets.digest = true config.assets.digest = true
MissionControl::Jobs.http_basic_auth_user = ENV["WRESTLINGDEV_MISSION_CONTROL_USER"]
MissionControl::Jobs.http_basic_auth_password =ENV["WRESTLINGDEV_MISSION_CONTROL_PASSWORD"]
end end

View File

@@ -0,0 +1,7 @@
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = "1.0"
# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path

View File

@@ -1,6 +1,7 @@
Wrestling::Application.routes.draw do Wrestling::Application.routes.draw do
# Mount Action Cable server # Mount Action Cable server
mount ActionCable.server => '/cable' mount ActionCable.server => '/cable'
mount MissionControl::Jobs::Engine, at: "/jobs"
resources :mats resources :mats
post "mats/:id/assign_next_match" => "mats#assign_next_match", :as => :assign_next_match post "mats/:id/assign_next_match" => "mats#assign_next_match", :as => :assign_next_match

View File

@@ -0,0 +1,10 @@
class IncreaseSolidQueueJobArgumentsLimit < ActiveRecord::Migration[8.0]
def up
# Allow large payloads (e.g., pasted import text) to be enqueued without blowing up MySQL's TEXT limit (~64KB).
change_column :solid_queue_jobs, :arguments, :text, limit: 16.megabytes - 1
end
def down
change_column :solid_queue_jobs, :arguments, :text
end
end

View File

@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_04_04_153529) do ActiveRecord::Schema[8.0].define(version: 2025_05_04_190000) do
create_table "solid_queue_blocked_executions", force: :cascade do |t| create_table "solid_queue_blocked_executions", force: :cascade do |t|
t.bigint "job_id", null: false t.bigint "job_id", null: false
t.string "queue_name", null: false t.string "queue_name", null: false
@@ -41,7 +41,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_04_153529) do
create_table "solid_queue_jobs", force: :cascade do |t| create_table "solid_queue_jobs", force: :cascade do |t|
t.string "queue_name", null: false t.string "queue_name", null: false
t.string "class_name", null: false t.string "class_name", null: false
t.text "arguments" t.text "arguments", limit: 16777215
t.integer "priority", default: 0, null: false t.integer "priority", default: 0, null: false
t.string "active_job_id" t.string "active_job_id"
t.datetime "scheduled_at" t.datetime "scheduled_at"

View File

@@ -25,9 +25,11 @@ docker-compose -f ${project_dir}/deploy/docker-compose-test.yml run --rm app bin
docker-compose -f ${project_dir}/deploy/docker-compose-test.yml run --rm app bin/rails db:migrate:queue docker-compose -f ${project_dir}/deploy/docker-compose-test.yml run --rm app bin/rails db:migrate:queue
docker-compose -f ${project_dir}/deploy/docker-compose-test.yml run --rm app bin/rails db:migrate:cable docker-compose -f ${project_dir}/deploy/docker-compose-test.yml run --rm app bin/rails db:migrate:cable
# Start all services (will start app and others, db is already running) echo "Stopping all services..."
docker-compose -f ${project_dir}/deploy/docker-compose-test.yml down
echo "Starting application services..." echo "Starting application services..."
docker-compose -f ${project_dir}/deploy/docker-compose-test.yml up -d docker-compose -f ${project_dir}/deploy/docker-compose-test.yml up --force-recreate --remove-orphans -d
# DISABLE_DATABASE_ENVIRONMENT_CHECK=1 is needed because this is "destructive" action on production # DISABLE_DATABASE_ENVIRONMENT_CHECK=1 is needed because this is "destructive" action on production
echo Resetting the db with seed data echo Resetting the db with seed data

View File

@@ -26,6 +26,8 @@ services:
- WRESTLINGDEV_INFLUXDB_HOST=influxdb - WRESTLINGDEV_INFLUXDB_HOST=influxdb
- WRESTLINGDEV_INFLUXDB_PORT=8086 - WRESTLINGDEV_INFLUXDB_PORT=8086
- SOLID_QUEUE_IN_PUMA=true - SOLID_QUEUE_IN_PUMA=true
- WRESTLINGDEV_MISSION_CONTROL_USER=dev
- WRESTLINGDEV_MISSION_CONTROL_PASSWORD=secret
networks: networks:
database: database:
caching: caching:

View File

@@ -0,0 +1,160 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: mariadb-replica-watcher
labels:
app: wrestlingdev
component: mariadb-watcher
spec:
replicas: 1
selector:
matchLabels:
app: wrestlingdev
component: mariadb-watcher
template:
metadata:
labels:
app: wrestlingdev
component: mariadb-watcher
spec:
containers:
- name: replica-watcher
image: mariadb:10.3
env:
- name: MARIADB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: wrestlingdev-secrets
key: dbpassword
- name: MYSQL_REPLICATION_USER
valueFrom:
secretKeyRef:
name: wrestlingdev-secrets
key: replication_user
- name: MYSQL_REPLICATION_PASSWORD
valueFrom:
secretKeyRef:
name: wrestlingdev-secrets
key: replication_password
- name: MASTER_SERVICE_HOST
valueFrom:
secretKeyRef:
name: wrestlingdev-secrets
key: replication_host
- name: REPLICA_SERVICE_HOST
value: "wrestlingdev-mariadb"
- name: DB_NAME
value: "wrestlingdev"
command:
- bash
- -c
- |
set -euo pipefail
LOG=/var/log/replica-watcher.log
echo "replica-watcher starting: $(date -u)" >>"$LOG"
trim() { sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'; }
get_val() {
grep -m1 -E "^[[:space:]]*$1[[:space:]]*:" \
| sed -E "s/^[[:space:]]*$1[[:space:]]*:[[:space:]]*(.*)$/\1/" \
| tr -d '\r' \
| xargs
}
# initial wait
sleep 120
while true; do
echo "$(date -u) Checking SHOW SLAVE STATUS" | tee -a "$LOG"
SLAVE_RAW=$(mysql --protocol=TCP -h "$REPLICA_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" -e "SHOW SLAVE STATUS\\G" 2>>"$LOG" || true)
NEED=0
if [ -z "$SLAVE_RAW" ]; then
echo "SHOW SLAVE STATUS is empty (replication not configured / not running) -> will rebootstrap" | tee -a "$LOG"
NEED=1
else
SLAVE_IO=$(echo "$SLAVE_RAW" | get_val "Slave_IO_Running")
SLAVE_SQL=$(echo "$SLAVE_RAW" | get_val "Slave_SQL_Running")
LAST_IO_ERRNO=$(echo "$SLAVE_RAW" | get_val "Last_IO_Errno")
LAST_SQL_ERRNO=$(echo "$SLAVE_RAW" | get_val "Last_SQL_Errno")
LAST_IO_ERR=$(echo "$SLAVE_RAW" | get_val "Last_IO_Error")
LAST_SQL_ERR=$(echo "$SLAVE_RAW" | get_val "Last_SQL_Error")
echo "Slave IO='${SLAVE_IO:-}' Slave SQL='${SLAVE_SQL:-}'" | tee -a "$LOG"
echo "Last_IO_Errno='${LAST_IO_ERRNO:-}' Last_SQL_Errno='${LAST_SQL_ERRNO:-}'" | tee -a "$LOG"
echo "Last_IO_Error='${LAST_IO_ERR:-}' Last_SQL_Error='${LAST_SQL_ERR:-}'" | tee -a "$LOG"
if [ "${SLAVE_IO:-}" = "Yes" ] && [ "${SLAVE_SQL:-}" = "Yes" ] \
&& { [ -z "${LAST_IO_ERR:-}" ] || [ "${LAST_IO_ERR,,}" = "no error" ]; } \
&& { [ -z "${LAST_SQL_ERR:-}" ] || [ "${LAST_SQL_ERR,,}" = "no error" ]; } \
&& { [ -z "${LAST_IO_ERRNO:-}" ] || [ "${LAST_IO_ERRNO:-0}" = "0" ]; } \
&& { [ -z "${LAST_SQL_ERRNO:-}" ] || [ "${LAST_SQL_ERRNO:-0}" = "0" ]; }; then
echo "Both slave threads running and no replication errors -> no action" | tee -a "$LOG"
else
NOT_RUNNING=0
[ "${SLAVE_IO:-No}" != "Yes" ] && NOT_RUNNING=1
[ "${SLAVE_SQL:-No}" != "Yes" ] && NOT_RUNNING=1
HAS_ERROR=0
[ -n "${LAST_IO_ERRNO:-}" ] && [ "${LAST_IO_ERRNO:-0}" != "0" ] && HAS_ERROR=1
[ -n "${LAST_SQL_ERRNO:-}" ] && [ "${LAST_SQL_ERRNO:-0}" != "0" ] && HAS_ERROR=1
ERR_TEXT="$(printf '%s %s' "${LAST_IO_ERR:-}" "${LAST_SQL_ERR:-}" | tr '[:upper:]' '[:lower:]' | trim)"
[ -n "$ERR_TEXT" ] && [ "$ERR_TEXT" != "no error" ] && HAS_ERROR=1
echo "Decision: NOT_RUNNING=$NOT_RUNNING HAS_ERROR=$HAS_ERROR" | tee -a "$LOG"
[ $NOT_RUNNING -eq 1 ] || [ $HAS_ERROR -eq 1 ] && NEED=1 || echo "Threads healthy -> no action" | tee -a "$LOG"
fi
fi
if [ $NEED -eq 1 ]; then
echo "$(date -u) Starting rebootstrap flow" | tee -a "$LOG"
MASTER_STATUS=$(mysql --protocol=TCP -h "$MASTER_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" -sse "SHOW MASTER STATUS;" 2>>"$LOG" || true)
MASTER_LOG_FILE=$(echo "$MASTER_STATUS" | awk '{print $1}' | trim || true)
MASTER_LOG_POS=$(echo "$MASTER_STATUS" | awk '{print $2}' | trim || true)
if [ -z "$MASTER_LOG_FILE" ] || [ -z "$MASTER_LOG_POS" ]; then
echo "Failed to get master position from $MASTER_SERVICE_HOST" | tee -a "$LOG"
sleep 120; continue
fi
echo "Master position: ${MASTER_LOG_FILE}:${MASTER_LOG_POS}" | tee -a "$LOG"
echo "Stopping slave on replica host" | tee -a "$LOG"
mysql --protocol=TCP -h "$REPLICA_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" -e "STOP SLAVE;" >>"$LOG" 2>&1 || true
DUMP_FILE="/tmp/${DB_NAME}_backup.sql"
echo "Dumping ${DB_NAME} from master ${MASTER_SERVICE_HOST}" | tee -a "$LOG"
if command -v timeout >/dev/null 2>&1; then
if ! timeout 300 mysqldump --protocol=TCP -h "$MASTER_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" --single-transaction "$DB_NAME" \
| tee "$DUMP_FILE" >/dev/null 2>>"$LOG"; then
echo "Dump FAILED; aborting this cycle" | tee -a "$LOG"; sleep 120; continue
fi
else
if ! mysqldump --protocol=TCP -h "$MASTER_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" --single-transaction "$DB_NAME" \
| tee "$DUMP_FILE" >/dev/null 2>>"$LOG"; then
echo "Dump FAILED; aborting this cycle" | tee -a "$LOG"; sleep 120; continue
fi
fi
ls -lh $DUMP_FILE
echo "Ensuring database '$DB_NAME' exists on replica" | tee -a "$LOG"
mysql --protocol=TCP -h "$REPLICA_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" \
-e "CREATE DATABASE IF NOT EXISTS \`$DB_NAME\`;" >>"$LOG" 2>&1
echo "Importing dump into replica host" | tee -a "$LOG"
if ! cat "$DUMP_FILE" | mysql --protocol=TCP -h "$REPLICA_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" "$DB_NAME" >>"$LOG" 2>&1; then
echo "Import FAILED; aborting this cycle (replication will not be reconfigured)" | tee -a "$LOG"
sleep 120; continue
fi
echo "Import completed successfully" | tee -a "$LOG"
echo "Reconfiguring replication to ${MASTER_SERVICE_HOST}:${MASTER_LOG_FILE}:${MASTER_LOG_POS}" | tee -a "$LOG"
mysql --protocol=TCP -h "$REPLICA_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" -e "RESET SLAVE ALL;" >>"$LOG" 2>&1 || true
mysql --protocol=TCP -h "$REPLICA_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" -e "CHANGE MASTER TO MASTER_HOST='${MASTER_SERVICE_HOST}', MASTER_USER='${MYSQL_REPLICATION_USER}', MASTER_PASSWORD='${MYSQL_REPLICATION_PASSWORD}', MASTER_LOG_FILE='${MASTER_LOG_FILE}', MASTER_LOG_POS=${MASTER_LOG_POS}; START SLAVE;" >>"$LOG" 2>&1 || true
echo "SHOW SLAVE STATUS after rebootstrap:" | tee -a "$LOG"
mysql --protocol=TCP -h "$REPLICA_SERVICE_HOST" -uroot -p"$MARIADB_ROOT_PASSWORD" -e "SHOW SLAVE STATUS\\G" >>"$LOG" 2>&1 || true
fi
echo "Sleeping 120s before next check" | tee -a "$LOG"
sleep 120
done
restartPolicy: Always

View File

@@ -214,6 +214,6 @@ data:
# if you want to ignore dbs to replicate # if you want to ignore dbs to replicate
# replicate-ignore-db=wrestlingtourney-queue # replicate-ignore-db=wrestlingtourney-queue
# if you only want to replicate certain dbs # if you only want to replicate certain dbs
replicate-do-db=wrestlingtourney replicate-do-db=wrestlingdev
# /etc/mysql/mariadb.conf.d/70-mysettings.cnf # /etc/mysql/mariadb.conf.d/70-mysettings.cnf

View File

@@ -56,6 +56,16 @@ spec:
secretKeyRef: secretKeyRef:
name: wrestlingdev-secrets name: wrestlingdev-secrets
key: dbpassword key: dbpassword
- name: REPLICATION_USER
valueFrom:
secretKeyRef:
name: wrestlingdev-secrets
key: replication_user
- name: REPLICATION_PASSWORD
valueFrom:
secretKeyRef:
name: wrestlingdev-secrets
key: replication_password
ports: ports:
- containerPort: 3306 - containerPort: 3306
name: mariadb name: mariadb
@@ -64,12 +74,36 @@ spec:
mountPath: /var/lib/mysql mountPath: /var/lib/mysql
- name: mysettings-config-volume - name: mysettings-config-volume
mountPath: /etc/mysql/mariadb.conf.d mountPath: /etc/mysql/mariadb.conf.d
# lifecycle: create replication user with proper privileges if it doesn't exist
lifecycle:
postStart:
exec:
command:
- sh
- -c
- |
# Wait up to 60s for mysqld to be available
for i in $(seq 1 60); do
if mysqladmin ping -uroot -p"$MARIADB_ROOT_PASSWORD" --silent; then
echo "mysqld is up"
break
fi
sleep 1
done
echo "Ensuring replication user ${REPLICATION_USER} exists and has REPLICATION SLAVE privileges"
# Create the replication user if it doesn't exist and grant replication privileges.
# Use CREATE USER IF NOT EXISTS so the command is idempotent.
mysql -uroot -p"$MARIADB_ROOT_PASSWORD" -e "CREATE USER IF NOT EXISTS '${REPLICATION_USER}'@'%' IDENTIFIED BY '${REPLICATION_PASSWORD}';" 2>/dev/null || true
mysql -uroot -p"$MARIADB_ROOT_PASSWORD" -e "GRANT REPLICATION SLAVE ON *.* TO '${REPLICATION_USER}'@'%';" 2>/dev/null || true
mysql -uroot -p"$MARIADB_ROOT_PASSWORD" -e "FLUSH PRIVILEGES;" 2>/dev/null || true
echo "Replication user ready (errors ignored to avoid blocking startup)"
# resources: # resources:
# limits: # limits:
# memory: "512Mi" # memory: "512Mi"
# requests: # requests:
# memory: "256Mi" # memory: "256Mi"
# cpu: "0.2"
- image: jcwimer/mariadb-rclone-backup-docker:10.3 - image: jcwimer/mariadb-rclone-backup-docker:10.3
name: mariadb-backup name: mariadb-backup
env: env:
@@ -204,5 +238,14 @@ data:
performance_schema=ON performance_schema=ON
innodb_log_file_size=32M innodb_log_file_size=32M
table_open_cache=4000 table_open_cache=4000
expire_logs_days=7
# master slave
server_id=1 # Unique server ID for the master
log_bin=mysql-bin # Enable binary logging
binlog_format=ROW # Recommended format for replication (ROW, STATEMENT, or MIXED)
log_slave_updates=ON # Ensure any changes replicated to the master are also logged to the binary log (useful for multi-source replication)
sync_binlog=1 # Ensures binary logs are synchronized with disk after each transaction for data safety
expire_logs_days=7 # Optional: Number of days to retain binary logs (helps with cleanup)
# /etc/mysql/mariadb.conf.d/70-mysettings.cnf # /etc/mysql/mariadb.conf.d/70-mysettings.cnf

View File

@@ -1,42 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: wrestlingdev-memcached
labels:
app: wrestlingdev
spec:
ports:
- port: 11211
selector:
app: wrestlingdev
tier: memcached
clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wrestlingdev-memcached-deployment
labels:
app: wrestlingdev
spec:
replicas: 1
selector:
matchLabels:
app: wrestlingdev
template:
metadata:
labels:
app: wrestlingdev
tier: memcached
spec:
containers:
- name: wrestlingdev-memcached
image: memcached
ports:
- containerPort: 11211
resources:
limits:
memory: "64Mi"
# requests:
# memory: "64Mi"
# cpu: "0.1"

View File

@@ -109,6 +109,16 @@ spec:
secretKeyRef: secretKeyRef:
name: wrestlingdev-secrets name: wrestlingdev-secrets
key: influxdb_port key: influxdb_port
- name: WRESTLINGDEV_MISSION_CONTROL_USER
valueFrom:
secretKeyRef:
name: wrestlingdev-secrets
key: mission_control_user
- name: WRESTLINGDEV_MISSION_CONTROL_PASSWORD
valueFrom:
secretKeyRef:
name: wrestlingdev-secrets
key: mission_control_password
# resources: # resources:
# limits: # limits:
# memory: "768Mi" # memory: "768Mi"
@@ -122,25 +132,27 @@ spec:
initialDelaySeconds: 180 initialDelaySeconds: 180
periodSeconds: 20 periodSeconds: 20
timeoutSeconds: 10 timeoutSeconds: 10
# --- ---
# apiVersion: autoscaling/v2beta1 apiVersion: autoscaling/v2beta1
# kind: HorizontalPodAutoscaler kind: HorizontalPodAutoscaler
# metadata: metadata:
# name: wrestlingdev-app-deployment-autoscale name: wrestlingdev-app-autoscale
# spec: spec:
# scaleTargetRef: scaleTargetRef:
# apiVersion: apps/v1 apiVersion: apps/v1
# kind: Deployment kind: StatefulSet
# name: wrestlingdev-app-deployment name: wrestlingdev-app
# minReplicas: 2 minReplicas: 2
# maxReplicas: 5 maxReplicas: 5
# metrics: metrics:
# - type: Resource - type: Resource
# resource: resource:
# name: cpu name: cpu
# targetAverageUtilization: 75 targetAverageUtilization: 75
# - type: Resource - type: Resource
# resource: resource:
# name: memory name: memory
# targetAverageValue: 100Mi target:
type: Utilization
averageUtilization: 80

View File

@@ -14,6 +14,14 @@ stringData:
gmailpassword: PUT_EMAIL_PASSWORD_HERE # gmail password gmailpassword: PUT_EMAIL_PASSWORD_HERE # gmail password
gmailemail: PUT EMAIL ADDRESS HERE gmailemail: PUT EMAIL ADDRESS HERE
passenger_pool_size: "2" passenger_pool_size: "2"
# Replication credentials (create a dedicated user for replication)
replication_user: replica_user_here
replication_password: PUT_REPLICATION_PASSWORD_HERE
# Replication host used by the replica to connect to the master
replication_host: wrestlingdev-mariadb
# Mission Control Credentials
mission_control_user: PUT_MISSION_CONTROL_USERNAME_HERE
mission_control_password: PUT_MISSION_CONTROL_PASSWORD_HERE
# OPTIONAL # OPTIONAL
# DELETE THESE LINES IF YOU'RE NOT USING THEM # DELETE THESE LINES IF YOU'RE NOT USING THEM
influxdb_database: PUT INFLUXDB DATABASE NAME HERE influxdb_database: PUT INFLUXDB DATABASE NAME HERE

View File

@@ -4,7 +4,7 @@ FROM ruby:3.2.0
ARG USER_ID=1000 ARG USER_ID=1000
ARG GROUP_ID=1000 ARG GROUP_ID=1000
RUN apt-get -qq update \ RUN apt-get -qq update --fix-missing \
&& apt-get -qq install -y \ && apt-get -qq install -y \
build-essential \ build-essential \
sqlite3 \ sqlite3 \

View File

@@ -6,7 +6,7 @@ ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini RUN chmod +x /tini
RUN apt-get -qq update \ RUN apt-get -qq update --fix-missing \
&& DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \ && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
build-essential \ build-essential \
openssl \ openssl \

View File

@@ -27,18 +27,19 @@ namespace :tournament do
) )
GenerateTournamentMatches.new(@tournament).generate GenerateTournamentMatches.new(@tournament).generate
sleep(10) sleep(60)
while @tournament.reload.curently_generating_matches == 1 while @tournament.reload.curently_generating_matches == 1
puts "Waiting for tournament to finish generating matches..." puts "Waiting for tournament to finish generating matches..."
sleep(5) sleep(5)
@tournament.reload
end end
sleep(10)
@tournament.reload # Ensure matches association is fresh before iterating @tournament.reload # Ensure matches association is fresh before iterating
@tournament.matches.sort_by(&:bout_number).each do |match| @tournament.matches.reload.sort_by(&:bout_number).each do |match|
match.reload if match.reload.loser1_name != "BYE" and match.reload.loser2_name != "BYE" and match.reload.finished != 1
if match.loser1_name != "BYE" and match.loser2_name != "BYE"
# Wait until both wrestlers are assigned # Wait until both wrestlers are assigned
while match.w1.nil? || match.w2.nil? while (match.w1.nil? || match.w2.nil?)
puts "Waiting for wrestlers in match #{match.bout_number}..." puts "Waiting for wrestlers in match #{match.bout_number}..."
sleep(5) # Wait for 5 seconds before checking again sleep(5) # Wait for 5 seconds before checking again
match.reload match.reload
@@ -77,6 +78,8 @@ namespace :tournament do
# Mark match as finished # Mark match as finished
match.finished = 1 match.finished = 1
match.save! match.save!
# sleep to prevent mysql locks when assign_next_match to a mat runs
sleep(0.5)
end end
end end
end end

1
public/ads.txt Normal file
View File

@@ -0,0 +1 @@
google.com, pub-6845455733812572, DIRECT, f08c47fec0942fa0

View File

@@ -357,7 +357,7 @@ Some Guy
@school.wrestlers.each do |wrestler| @school.wrestlers.each do |wrestler|
# Check only for the DELETE link, specifying 'data-method="delete"' to exclude profile links # Check only for the DELETE link, specifying 'data-method="delete"' to exclude profile links
assert_select "a[href=?][data-method=delete]", wrestler_path(wrestler), count: 1 assert_select "a[href=?][data-turbo-method=delete]", wrestler_path(wrestler), count: 1
# Check edit link # Check edit link
assert_select "a[href=?]", edit_wrestler_path(wrestler), count: 1 assert_select "a[href=?]", edit_wrestler_path(wrestler), count: 1
@@ -373,12 +373,26 @@ Some Guy
success success
end end
test "logged in user without delegation can get show page when using valid school_permission_key" do
sign_in_non_owner
@tournament.update(is_public: false)
get_show(school_permission_key: @school_permission_key)
success
end
test "non logged in user cannot get show page when using invalid school_permission_key" do test "non logged in user cannot get show page when using invalid school_permission_key" do
@tournament.update(is_public: false) @tournament.update(is_public: false)
get_show(school_permission_key: "invalid-key") get_show(school_permission_key: "invalid-key")
redirect redirect
end end
test "logged in user without delegation can edit school with valid school_permission_key" do
sign_in_non_owner
@tournament.update(is_public: false)
get_edit(school_permission_key: @school_permission_key)
success
end
test "non logged in user can edit school with valid school_permission_key" do test "non logged in user can edit school with valid school_permission_key" do
@tournament.update(is_public: false) @tournament.update(is_public: false)
get_edit(school_permission_key: @school_permission_key) get_edit(school_permission_key: @school_permission_key)

View File

@@ -1084,4 +1084,55 @@ class TournamentsControllerTest < ActionController::TestCase
assert_match(/#{match.bout_number}/, response.body, "Bout number #{match.bout_number} is missing from the bracket page") assert_match(/#{match.bout_number}/, response.body, "Bout number #{match.bout_number} is missing from the bracket page")
end end
end end
test "index sorts tournaments by date closest to today" do
today = Date.today
t_today = Tournament.create!(name: "Closest Today", address: "123 Test St", director: "Director", director_email: "today@example.com", date: today, tournament_type: "Pool to bracket", is_public: true)
t_minus1 = Tournament.create!(name: "Minus 1", address: "123 Test St", director: "Director", director_email: "m1@example.com", date: today - 1, tournament_type: "Pool to bracket", is_public: true)
t_plus1 = Tournament.create!(name: "Plus 1", address: "123 Test St", director: "Director", director_email: "p1@example.com", date: today + 1, tournament_type: "Pool to bracket", is_public: true)
t_plus2 = Tournament.create!(name: "Plus 2", address: "123 Test St", director: "Director", director_email: "p2@example.com", date: today + 2, tournament_type: "Pool to bracket", is_public: true)
t_minus3 = Tournament.create!(name: "Minus 3", address: "123 Test St", director: "Director", director_email: "m3@example.com", date: today - 3, tournament_type: "Pool to bracket", is_public: true)
t_plus10 = Tournament.create!(name: "Plus 10", address: "123 Test St", director: "Director", director_email: "p10@example.com", date: today + 10, tournament_type: "Pool to bracket", is_public: true)
created = [t_today, t_minus1, t_plus1, t_plus2, t_minus3, t_plus10]
# Hit index
get :index
assert_response :success
# From the controller result, select only the tournaments we just created and verify their relative order
results = assigns(:tournaments).select { |t| created.map(&:id).include?(t.id) }
expected_order = created.sort_by { |t| (t.date - today).abs }.map(&:id)
# Basic ordering assertions
assert_equal expected_order, results.map(&:id), "Created tournaments should be ordered by distance from today"
assert_equal t_today.id, results.first.id, "The tournament dated today should be first (closest)"
assert_equal t_plus10.id, results.last.id, "The farthest tournament should appear last"
# Relative order checks (smaller distance should appear before larger distance)
assert results.index { |r| r.id == t_minus1.id } < results.index { |r| r.id == t_plus2.id }, "t_minus1 (distance 1) should appear before t_plus2 (distance 2)"
assert results.index { |r| r.id == t_plus2.id } < results.index { |r| r.id == t_minus3.id }, "t_plus2 (distance 2) should appear before t_minus3 (distance 3)"
end
test "index paginates tournaments with page param and exposes total_count" do
initial_count = Tournament.count
# Create 25 tournaments to ensure we exceed the per_page (20)
25.times do |i|
Tournament.create!(name: "Paginate Test #{i}", address: "1 Paginate Rd", director: "Dir", director_email: "paginate#{i}@example.com", date: Date.today + i, tournament_type: "Pool to bracket", is_public: true)
end
expected_total = initial_count + 25
# Page 1
get :index, params: { page: 1 }
assert_response :success
assert_equal expected_total, assigns(:total_count), "total_count should reflect all tournaments"
assert_equal 20, assigns(:tournaments).size, "first page should contain 20 tournaments"
# Page 2
get :index, params: { page: 2 }
assert_response :success
assert_equal expected_total, assigns(:total_count), "total_count should remain the same on subsequent pages"
expected_page2_size = expected_total - 20
# If there are more than 20 initial fixtures, expected_page2_size might be > 20; clamp to per_page logic:
expected_page2_display = [expected_page2_size, 20].min
assert_equal expected_page2_display, assigns(:tournaments).size, "second page should contain the remaining tournaments (or up to per_page)"
end
end end

View File

@@ -2,26 +2,4 @@
# This model requires tournament, job_name, and status fields # This model requires tournament, job_name, and status fields
queued_job:
tournament: one
job_name: "Test Queued Job"
status: "Queued"
details: "Test job details"
running_job:
tournament: one
job_name: "Test Running Job"
status: "Running"
details: "Test running job details"
errored_job:
tournament: one
job_name: "Test Errored Job"
status: "Errored"
details: "Test error message"
another_tournament_job:
tournament: two
job_name: "Another Tournament Job"
status: "Running"
details: "Different tournament test"

View File

@@ -2,336 +2,376 @@ require 'test_helper'
class DoubleEliminationSixtyFourManEightPlacesRunThrough < ActionDispatch::IntegrationTest class DoubleEliminationSixtyFourManEightPlacesRunThrough < ActionDispatch::IntegrationTest
def setup def setup
create_double_elim_tournament_single_weight(62, "Regular Double Elimination 1-8")
@matches = @tournament.matches.reload
end end
def simulate_match(winner_name, match) def winner_by_name(winner_name,match)
wrestler = @tournament.wrestlers.find_by(name: winner_name) wrestler = @tournament.weights.first.wrestlers.select{|w| w.name == winner_name}.first
match.update!( match.winner_id = wrestler.id
winner_id: wrestler.id, match.finished = 1
finished: 1, match.win_type = "Decision"
win_type: "Decision", match.score = "1-0"
score: "1-0" match.save
)
end end
test "32 man double elimination place 1-8" do test "64 man double elimination place 1-8" do
create_double_elim_tournament_single_weight(62, "Regular Double Elimination 1-8") create_double_elim_tournament_single_weight(62, "Regular Double Elimination 1-8")
matches = @tournament.matches.reload matches = @tournament.matches.reload
# BYEs are automatically advanced
bracket_r64 = matches.select{|m| m.bracket_position == "Bracket Round of 64"} bracket_r64 = matches.reload.select { |m| m.bracket_position == "Bracket Round of 64" }.sort_by(&:bracket_position_number)
winner_by_name("Test32", bracket_r64.select{|m| m.bracket_position_number == 2}.first) # winner_by_name("Test1", bracket_r64.select{|m| m.bracket_position_number == 1}.first) # seed 1 vs 64 (BYE)
winner_by_name("Test48", bracket_r64.select{|m| m.bracket_position_number == 3}.first) winner_by_name("Test32", bracket_r64.select{|m| m.bracket_position_number == 2}.first) # seed 32 vs seed 33
winner_by_name("Test16", bracket_r64.select{|m| m.bracket_position_number == 4}.first) winner_by_name("Test17", bracket_r64.select{|m| m.bracket_position_number == 3}.first) # seed 17 vs seed 48
winner_by_name("Test49", bracket_r64.select{|m| m.bracket_position_number == 4}.first) # seed 16 vs seed 49
# Match 1 - seed 1 vs seed 64 (BYE) winner_by_name("Test9", bracket_r64.select{|m| m.bracket_position_number == 5}.first) # seed 9 vs seed 56
assert matches_r64[0].wrestler1.bracket_line == 1 winner_by_name("Test24", bracket_r64.select{|m| m.bracket_position_number == 6}.first) # seed 24 vs seed 41
assert matches_r64[0].loser2_name == "BYE" winner_by_name("Test25", bracket_r64.select{|m| m.bracket_position_number == 7}.first) # seed 25 vs seed 40
winner_by_name("Test8", bracket_r64.select{|m| m.bracket_position_number == 8}.first) # seed 8 vs seed 57
# Match 2 - seed 32 vs seed 33 winner_by_name("Test5", bracket_r64.select{|m| m.bracket_position_number == 9}.first) # seed 5 vs seed 60
assert matches_r64[1].wrestler1.bracket_line == 32 winner_by_name("Test37", bracket_r64.select{|m| m.bracket_position_number == 10}.first) # seed 28 vs seed 37
assert matches_r64[1].wrestler2.bracket_line == 33 winner_by_name("Test21", bracket_r64.select{|m| m.bracket_position_number == 11}.first) # seed 21 vs seed 44
winner_by_name("Test12", bracket_r64.select{|m| m.bracket_position_number == 12}.first) # seed 12 vs seed 53
# Match 3 - seed 17 vs seed 48 winner_by_name("Test13", bracket_r64.select{|m| m.bracket_position_number == 13}.first) # seed 13 vs seed 52
assert matches_r64[2].wrestler1.bracket_line == 17 winner_by_name("Test20", bracket_r64.select{|m| m.bracket_position_number == 14}.first) # seed 20 vs 45
assert matches_r64[2].wrestler2.bracket_line == 48 winner_by_name("Test29", bracket_r64.select{|m| m.bracket_position_number == 15}.first) # seed 29 vs 36
winner_by_name("Test4", bracket_r64.select{|m| m.bracket_position_number == 16}.first) # seed 4 vs 61
# Match 4 - seed 16 vs seed 49 winner_by_name("Test3", bracket_r64.select{|m| m.bracket_position_number == 17}.first) # seed 3 vs 62
assert matches_r64[3].wrestler1.bracket_line == 16 winner_by_name("Test30", bracket_r64.select{|m| m.bracket_position_number == 18}.first) # seed 30 vs 35
assert matches_r64[3].wrestler2.bracket_line == 49 winner_by_name("Test19", bracket_r64.select{|m| m.bracket_position_number == 19}.first) # seed 19 vs 46
winner_by_name("Test14", bracket_r64.select{|m| m.bracket_position_number == 20}.first) # seed 14 vs 51
# Match 5 - seed 9 vs seed 56 winner_by_name("Test11", bracket_r64.select{|m| m.bracket_position_number == 21}.first) # seed 11 vs 54
assert matches_r64[4].wrestler1.bracket_line == 9 winner_by_name("Test22", bracket_r64.select{|m| m.bracket_position_number == 22}.first) # seed 22 vs 43
assert matches_r64[4].wrestler2.bracket_line == 56 winner_by_name("Test27", bracket_r64.select{|m| m.bracket_position_number == 23}.first) # seed 27 vs 38
winner_by_name("Test6", bracket_r64.select{|m| m.bracket_position_number == 24}.first) # seed 6 vs 59
# Match 6 - seed 24 vs seed 41 winner_by_name("Test7", bracket_r64.select{|m| m.bracket_position_number == 25}.first) # seed 7 vs 58
assert matches_r64[5].wrestler1.bracket_line == 24 winner_by_name("Test26", bracket_r64.select{|m| m.bracket_position_number == 26}.first) # seed 26 vs 39
assert matches_r64[5].wrestler2.bracket_line == 41 winner_by_name("Test23", bracket_r64.select{|m| m.bracket_position_number == 27}.first) # seed 23 vs 42
winner_by_name("Test10", bracket_r64.select{|m| m.bracket_position_number == 28}.first) # seed 10 vs 55
# Match 7 - seed 25 vs seed 40 winner_by_name("Test15", bracket_r64.select{|m| m.bracket_position_number == 29}.first) # seed 15 vs 50
assert matches_r64[6].wrestler1.bracket_line == 25 winner_by_name("Test18", bracket_r64.select{|m| m.bracket_position_number == 30}.first) # seed 18 vs 47
assert matches_r64[6].wrestler2.bracket_line == 40 winner_by_name("Test31", bracket_r64.select{|m| m.bracket_position_number == 31}.first) # seed 31 vs 34
# BYE's auto move
# Match 8 - seed 8 vs seed 57 # winner_by_name("Test2", bracket_r64.select{|m| m.bracket_position_number == 32}.first) # seed 2 vs seed 63 (BYE)
assert matches_r64[7].wrestler1.bracket_line == 8
assert matches_r64[7].wrestler2.bracket_line == 57
# Match 9 - seed 5 vs seed 60
assert matches_r64[8].wrestler1.bracket_line == 5
assert matches_r64[8].wrestler2.bracket_line == 60
# Match 10 - seed 28 vs seed 37
assert matches_r64[9].wrestler1.bracket_line == 28
assert matches_r64[9].wrestler2.bracket_line == 37
# Match 11 - seed 21 vs seed 44
assert matches_r64[10].wrestler1.bracket_line == 21
assert matches_r64[10].wrestler2.bracket_line == 44
# Match 12 - seed 12 vs seed 53
assert matches_r64[11].wrestler1.bracket_line == 12
assert matches_r64[11].wrestler2.bracket_line == 53
# Match 13 - seed 13 vs seed 52
assert matches_r64[12].wrestler1.bracket_line == 13
assert matches_r64[12].wrestler2.bracket_line == 52
# Match 14 - seed 20 vs seed 45
assert matches_r64[13].wrestler1.bracket_line == 20
assert matches_r64[13].wrestler2.bracket_line == 45
# Match 15 - seed 29 vs seed 36
assert matches_r64[14].wrestler1.bracket_line == 29
assert matches_r64[14].wrestler2.bracket_line == 36
# Match 16 - seed 4 vs seed 61
assert matches_r64[15].wrestler1.bracket_line == 4
assert matches_r64[15].wrestler2.bracket_line == 61
# Match 17 - seed 3 vs seed 62
assert matches_r64[16].wrestler1.bracket_line == 3
assert matches_r64[16].wrestler2.bracket_line == 62
# Match 18 - seed 30 vs seed 35
assert matches_r64[17].wrestler1.bracket_line == 30
assert matches_r64[17].wrestler2.bracket_line == 35
# Match 19 - seed 19 vs seed 46
assert matches_r64[18].wrestler1.bracket_line == 19
assert matches_r64[18].wrestler2.bracket_line == 46
# Match 20 - seed 14 vs seed 51
assert matches_r64[19].wrestler1.bracket_line == 14
assert matches_r64[19].wrestler2.bracket_line == 51
# Match 21 - seed 11 vs seed 54
assert matches_r64[20].wrestler1.bracket_line == 11
assert matches_r64[20].wrestler2.bracket_line == 54
# Match 22 - seed 22 vs seed 43
assert matches_r64[21].wrestler1.bracket_line == 22
assert matches_r64[21].wrestler2.bracket_line == 43
# Match 23 - seed 27 vs seed 38
assert matches_r64[22].wrestler1.bracket_line == 27
assert matches_r64[22].wrestler2.bracket_line == 38
# Match 24 - seed 6 vs seed 59
assert matches_r64[23].wrestler1.bracket_line == 6
assert matches_r64[23].wrestler2.bracket_line == 59
# Match 25 - seed 7 vs seed 58
assert matches_r64[24].wrestler1.bracket_line == 7
assert matches_r64[24].wrestler2.bracket_line == 58
# Match 26 - seed 26 vs seed 39
assert matches_r64[25].wrestler1.bracket_line == 26
assert matches_r64[25].wrestler2.bracket_line == 39
# Match 27 - seed 23 vs seed 42
assert matches_r64[26].wrestler1.bracket_line == 23
assert matches_r64[26].wrestler2.bracket_line == 42
# Match 28 - seed 10 vs seed 55
assert matches_r64[27].wrestler1.bracket_line == 10
assert matches_r64[27].wrestler2.bracket_line == 55
# Match 29 - seed 15 vs seed 50
assert matches_r64[28].wrestler1.bracket_line == 15
assert matches_r64[28].wrestler2.bracket_line == 50
# Match 30 - seed 18 vs seed 47
assert matches_r64[29].wrestler1.bracket_line == 18
assert matches_r64[29].wrestler2.bracket_line == 47
# Match 31 - seed 31 vs seed 34
assert matches_r64[30].wrestler1.bracket_line == 31
assert matches_r64[30].wrestler2.bracket_line == 34
# Match 32 - seed 2 vs seed 63 (BYE)
assert matches_r64[31].wrestler1.bracket_line == 2
assert matches_r64[31].loser2_name == "BYE"
bracket_r32 = matches.select{|m| m.bracket_position == "Bracket Round of 32"} bracket_r32 = matches.select{|m| m.bracket_position == "Bracket Round of 32"}
assert bracket_r32.select{|m| m.bracket_position_number == 1}.first.reload.wrestler1.name == "Test1"
assert bracket_r32.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test32"
winner_by_name("Test1", bracket_r32.select{|m| m.bracket_position_number == 1}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test17"
assert bracket_r32.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test49"
winner_by_name("Test17", bracket_r32.select{|m| m.bracket_position_number == 2}.first) winner_by_name("Test17", bracket_r32.select{|m| m.bracket_position_number == 2}.first)
winner_by_name("Test9", bracket_r32.select{|m| m.bracket_position_number == 3}.first) assert bracket_r32.select{|m| m.bracket_position_number == 3}.first.reload.wrestler1.name == "Test9"
winner_by_name("Test25", bracket_r32.select{|m| m.bracket_position_number == 4}.first) assert bracket_r32.select{|m| m.bracket_position_number == 3}.first.reload.wrestler2.name == "Test24"
winner_by_name("Test24", bracket_r32.select{|m| m.bracket_position_number == 3}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 4}.first.reload.wrestler1.name == "Test25"
assert bracket_r32.select{|m| m.bracket_position_number == 4}.first.reload.wrestler2.name == "Test8"
winner_by_name("Test8", bracket_r32.select{|m| m.bracket_position_number == 4}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 5}.first.reload.wrestler1.name == "Test5"
assert bracket_r32.select{|m| m.bracket_position_number == 5}.first.reload.wrestler2.name == "Test37"
winner_by_name("Test5", bracket_r32.select{|m| m.bracket_position_number == 5}.first) winner_by_name("Test5", bracket_r32.select{|m| m.bracket_position_number == 5}.first)
winner_by_name("Test12", bracket_r32.select{|m| m.bracket_position_number == 6}.first) assert bracket_r32.select{|m| m.bracket_position_number == 6}.first.reload.wrestler1.name == "Test21"
winner_by_name("Test20", bracket_r32.select{|m| m.bracket_position_number == 7}.first) assert bracket_r32.select{|m| m.bracket_position_number == 6}.first.reload.wrestler2.name == "Test12"
winner_by_name("Test21", bracket_r32.select{|m| m.bracket_position_number == 6}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 7}.first.reload.wrestler1.name == "Test13"
assert bracket_r32.select{|m| m.bracket_position_number == 7}.first.reload.wrestler2.name == "Test20"
winner_by_name("Test13", bracket_r32.select{|m| m.bracket_position_number == 7}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 8}.first.reload.wrestler1.name == "Test29"
assert bracket_r32.select{|m| m.bracket_position_number == 8}.first.reload.wrestler2.name == "Test4"
winner_by_name("Test4", bracket_r32.select{|m| m.bracket_position_number == 8}.first) winner_by_name("Test4", bracket_r32.select{|m| m.bracket_position_number == 8}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 9}.first.reload.wrestler1.name == "Test3"
assert bracket_r32.select{|m| m.bracket_position_number == 9}.first.reload.wrestler2.name == "Test30"
winner_by_name("Test3", bracket_r32.select{|m| m.bracket_position_number == 9}.first) winner_by_name("Test3", bracket_r32.select{|m| m.bracket_position_number == 9}.first)
winner_by_name("Test14", bracket_r32.select{|m| m.bracket_position_number == 10}.first) assert bracket_r32.select{|m| m.bracket_position_number == 10}.first.reload.wrestler1.name == "Test19"
assert bracket_r32.select{|m| m.bracket_position_number == 10}.first.reload.wrestler2.name == "Test14"
winner_by_name("Test19", bracket_r32.select{|m| m.bracket_position_number == 10}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 11}.first.reload.wrestler1.name == "Test11"
assert bracket_r32.select{|m| m.bracket_position_number == 11}.first.reload.wrestler2.name == "Test22"
winner_by_name("Test11", bracket_r32.select{|m| m.bracket_position_number == 11}.first) winner_by_name("Test11", bracket_r32.select{|m| m.bracket_position_number == 11}.first)
winner_by_name("Test6", bracket_r32.select{|m| m.bracket_position_number == 12}.first) assert bracket_r32.select{|m| m.bracket_position_number == 12}.first.reload.wrestler1.name == "Test27"
assert bracket_r32.select{|m| m.bracket_position_number == 12}.first.reload.wrestler2.name == "Test6"
winner_by_name("Test27", bracket_r32.select{|m| m.bracket_position_number == 12}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 13}.first.reload.wrestler1.name == "Test7"
assert bracket_r32.select{|m| m.bracket_position_number == 13}.first.reload.wrestler2.name == "Test26"
winner_by_name("Test7", bracket_r32.select{|m| m.bracket_position_number == 13}.first) winner_by_name("Test7", bracket_r32.select{|m| m.bracket_position_number == 13}.first)
winner_by_name("Test10", bracket_r32.select{|m| m.bracket_position_number == 14}.first) assert bracket_r32.select{|m| m.bracket_position_number == 14}.first.reload.wrestler1.name == "Test23"
winner_by_name("Test18", bracket_r32.select{|m| m.bracket_position_number == 15}.first) assert bracket_r32.select{|m| m.bracket_position_number == 14}.first.reload.wrestler2.name == "Test10"
winner_by_name("Test23", bracket_r32.select{|m| m.bracket_position_number == 14}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 15}.first.reload.wrestler1.name == "Test15"
assert bracket_r32.select{|m| m.bracket_position_number == 15}.first.reload.wrestler2.name == "Test18"
winner_by_name("Test15", bracket_r32.select{|m| m.bracket_position_number == 15}.first)
assert bracket_r32.select{|m| m.bracket_position_number == 16}.first.reload.wrestler1.name == "Test31"
assert bracket_r32.select{|m| m.bracket_position_number == 16}.first.reload.wrestler2.name == "Test2"
winner_by_name("Test2", bracket_r32.select{|m| m.bracket_position_number == 16}.first)
bracket_r16 = matches.reload.select{|m| m.bracket_position == "Bracket Round of 16"}.sort_by{|m| m.bracket_position_number} conso_r32_1 = matches.reload.select { |m| m.bracket_position == "Conso Round of 32.1" }.sort_by(&:bracket_position_number)
assert bracket_r16.select{|m| m.bracket_position_number == 1}.first.reload.wrestler1.name == "Test1" assert conso_r32_1[0].loser1_name == "BYE"
assert bracket_r16.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test17" assert conso_r32_1[0].wrestler2.name == "Test33"
assert bracket_r16.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test9" # BYE's auto move
assert bracket_r16.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test25" assert conso_r32_1[1].wrestler1.name == "Test48"
assert bracket_r16.select{|m| m.bracket_position_number == 3}.first.reload.wrestler1.name == "Test5" assert conso_r32_1[1].wrestler2.name == "Test16"
assert bracket_r16.select{|m| m.bracket_position_number == 3}.first.reload.wrestler2.name == "Test12" winner_by_name("Test16", conso_r32_1[1])
assert bracket_r16.select{|m| m.bracket_position_number == 4}.first.reload.wrestler1.name == "Test20" assert conso_r32_1[2].wrestler1.name == "Test56"
assert bracket_r16.select{|m| m.bracket_position_number == 4}.first.reload.wrestler2.name == "Test4" assert conso_r32_1[2].wrestler2.name == "Test41"
assert bracket_r16.select{|m| m.bracket_position_number == 5}.first.reload.wrestler1.name == "Test3" winner_by_name("Test56", conso_r32_1[2])
assert bracket_r16.select{|m| m.bracket_position_number == 5}.first.reload.wrestler2.name == "Test14" assert conso_r32_1[3].wrestler1.name == "Test40"
assert bracket_r16.select{|m| m.bracket_position_number == 6}.first.reload.wrestler1.name == "Test11" assert conso_r32_1[3].wrestler2.name == "Test57"
assert bracket_r16.select{|m| m.bracket_position_number == 6}.first.reload.wrestler2.name == "Test6" winner_by_name("Test57", conso_r32_1[3])
assert bracket_r16.select{|m| m.bracket_position_number == 7}.first.reload.wrestler1.name == "Test7" assert conso_r32_1[4].wrestler1.name == "Test60"
assert bracket_r16.select{|m| m.bracket_position_number == 7}.first.reload.wrestler2.name == "Test10" assert conso_r32_1[4].wrestler2.name == "Test28"
assert bracket_r16.select{|m| m.bracket_position_number == 8}.first.reload.wrestler1.name == "Test18" winner_by_name("Test28", conso_r32_1[4])
assert bracket_r16.select{|m| m.bracket_position_number == 8}.first.reload.wrestler2.name == "Test2" assert conso_r32_1[5].wrestler1.name == "Test44"
winner_by_name("Test1", bracket_r16.select{|m| m.bracket_position_number == 1}.first) assert conso_r32_1[5].wrestler2.name == "Test53"
winner_by_name("Test25", bracket_r16.select{|m| m.bracket_position_number == 2}.first) winner_by_name("Test53", conso_r32_1[5])
winner_by_name("Test5", bracket_r16.select{|m| m.bracket_position_number == 3}.first) assert conso_r32_1[6].wrestler1.name == "Test52"
winner_by_name("Test4", bracket_r16.select{|m| m.bracket_position_number == 4}.first) assert conso_r32_1[6].wrestler2.name == "Test45"
winner_by_name("Test3", bracket_r16.select{|m| m.bracket_position_number == 5}.first) winner_by_name("Test45", conso_r32_1[6])
winner_by_name("Test11", bracket_r16.select{|m| m.bracket_position_number == 6}.first) assert conso_r32_1[7].wrestler1.name == "Test36"
winner_by_name("Test10", bracket_r16.select{|m| m.bracket_position_number == 7}.first) assert conso_r32_1[7].wrestler2.name == "Test61"
winner_by_name("Test2", bracket_r16.select{|m| m.bracket_position_number == 8}.first) winner_by_name("Test61", conso_r32_1[7])
assert conso_r32_1[8].wrestler1.name == "Test62"
assert conso_r32_1[8].wrestler2.name == "Test35"
winner_by_name("Test35", conso_r32_1[8])
assert conso_r32_1[9].wrestler1.name == "Test46"
assert conso_r32_1[9].wrestler2.name == "Test51"
winner_by_name("Test51", conso_r32_1[9])
assert conso_r32_1[10].wrestler1.name == "Test54"
assert conso_r32_1[10].wrestler2.name == "Test43"
winner_by_name("Test43", conso_r32_1[10])
assert conso_r32_1[11].wrestler1.name == "Test38"
assert conso_r32_1[11].wrestler2.name == "Test59"
winner_by_name("Test59", conso_r32_1[11])
assert conso_r32_1[12].wrestler1.name == "Test58"
assert conso_r32_1[12].wrestler2.name == "Test39"
winner_by_name("Test39", conso_r32_1[12])
assert conso_r32_1[13].wrestler1.name == "Test42"
assert conso_r32_1[13].wrestler2.name == "Test55"
winner_by_name("Test55", conso_r32_1[13])
assert conso_r32_1[14].wrestler1.name == "Test50"
assert conso_r32_1[14].wrestler2.name == "Test47"
winner_by_name("Test47", conso_r32_1[14])
assert conso_r32_1[15].wrestler1.name == "Test34"
assert conso_r32_1[15].loser2_name == "BYE"
# Auto BYE
conso_r16_1 = matches.reload.select{|m| m.bracket_position == "Conso Round of 16.1"}.sort_by{|m| m.bracket_position_number} conso_r32_2 = matches.reload.select { |m| m.bracket_position == "Conso Round of 32.2" }.sort_by(&:bracket_position_number)
assert conso_r16_1.select{|m| m.bracket_position_number == 1}.first.reload.loser1_name == "BYE" assert conso_r32_2[0].wrestler1.name == "Test31"
assert conso_r16_1.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test16" assert conso_r32_2[0].wrestler2.name == "Test33"
assert conso_r16_1.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test24" winner_by_name("Test31", conso_r32_2[0])
assert conso_r16_1.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test8" assert conso_r32_2[1].wrestler1.name == "Test18"
assert conso_r16_1.select{|m| m.bracket_position_number == 3}.first.reload.wrestler1.name == "Test28" assert conso_r32_2[1].wrestler2.name == "Test16"
assert conso_r16_1.select{|m| m.bracket_position_number == 3}.first.reload.wrestler2.name == "Test21" winner_by_name("Test16", conso_r32_2[1])
assert conso_r16_1.select{|m| m.bracket_position_number == 4}.first.reload.wrestler1.name == "Test13" assert conso_r32_2[2].wrestler1.name == "Test10"
assert conso_r16_1.select{|m| m.bracket_position_number == 4}.first.reload.wrestler2.name == "Test29" assert conso_r32_2[2].wrestler2.name == "Test56"
assert conso_r16_1.select{|m| m.bracket_position_number == 5}.first.reload.wrestler1.name == "Test30" winner_by_name("Test10", conso_r32_2[2])
assert conso_r16_1.select{|m| m.bracket_position_number == 5}.first.reload.wrestler2.name == "Test19" assert conso_r32_2[3].wrestler1.name == "Test26"
assert conso_r16_1.select{|m| m.bracket_position_number == 6}.first.reload.wrestler1.name == "Test22" assert conso_r32_2[3].wrestler2.name == "Test57"
assert conso_r16_1.select{|m| m.bracket_position_number == 6}.first.reload.wrestler2.name == "Test27" winner_by_name("Test26", conso_r32_2[3])
assert conso_r16_1.select{|m| m.bracket_position_number == 7}.first.reload.wrestler1.name == "Test26" assert conso_r32_2[4].wrestler1.name == "Test6"
assert conso_r16_1.select{|m| m.bracket_position_number == 7}.first.reload.wrestler2.name == "Test23" assert conso_r32_2[4].wrestler2.name == "Test28"
assert conso_r16_1.select{|m| m.bracket_position_number == 8}.first.reload.wrestler1.name == "Test15" winner_by_name("Test6", conso_r32_2[4])
assert conso_r16_1.select{|m| m.bracket_position_number == 8}.first.reload.loser2_name == "BYE" assert conso_r32_2[5].wrestler1.name == "Test22"
winner_by_name("Test8", conso_r16_1.select{|m| m.bracket_position_number == 2}.first) assert conso_r32_2[5].wrestler2.name == "Test53"
winner_by_name("Test21", conso_r16_1.select{|m| m.bracket_position_number == 3}.first) winner_by_name("Test22", conso_r32_2[5])
winner_by_name("Test29", conso_r16_1.select{|m| m.bracket_position_number == 4}.first) assert conso_r32_2[6].wrestler1.name == "Test14"
winner_by_name("Test19", conso_r16_1.select{|m| m.bracket_position_number == 5}.first) assert conso_r32_2[6].wrestler2.name == "Test45"
winner_by_name("Test22", conso_r16_1.select{|m| m.bracket_position_number == 6}.first) winner_by_name("Test45", conso_r32_2[6])
winner_by_name("Test23", conso_r16_1.select{|m| m.bracket_position_number == 7}.first) assert conso_r32_2[7].wrestler1.name == "Test30"
assert conso_r32_2[7].wrestler2.name == "Test61"
winner_by_name("Test30", conso_r32_2[7])
assert conso_r32_2[8].wrestler1.name == "Test29"
assert conso_r32_2[8].wrestler2.name == "Test35"
winner_by_name("Test35", conso_r32_2[8])
assert conso_r32_2[9].wrestler1.name == "Test20"
assert conso_r32_2[9].wrestler2.name == "Test51"
winner_by_name("Test20", conso_r32_2[9])
assert conso_r32_2[10].wrestler1.name == "Test12"
assert conso_r32_2[10].wrestler2.name == "Test43"
winner_by_name("Test43", conso_r32_2[10])
assert conso_r32_2[11].wrestler1.name == "Test37"
assert conso_r32_2[11].wrestler2.name == "Test59"
winner_by_name("Test59", conso_r32_2[11])
assert conso_r32_2[12].wrestler1.name == "Test25"
assert conso_r32_2[12].wrestler2.name == "Test39"
winner_by_name("Test25", conso_r32_2[12])
assert conso_r32_2[13].wrestler1.name == "Test9"
assert conso_r32_2[13].wrestler2.name == "Test55"
winner_by_name("Test9", conso_r32_2[13])
assert conso_r32_2[14].wrestler1.name == "Test49"
assert conso_r32_2[14].wrestler2.name == "Test47"
winner_by_name("Test47", conso_r32_2[14])
assert conso_r32_2[15].wrestler1.name == "Test32"
assert conso_r32_2[15].wrestler2.name == "Test34"
winner_by_name("Test32", conso_r32_2[15])
conso_r16_2 = matches.reload.select{|m| m.bracket_position == "Conso Round of 16.2"}.sort_by{|m| m.bracket_position_number} bracket_r16 = matches.reload.select { |m| m.bracket_position == "Bracket Round of 16" }.sort_by(&:bracket_position_number)
assert conso_r16_2.select{|m| m.bracket_position_number == 1}.first.reload.wrestler1.name == "Test18" assert bracket_r16[0].wrestler1.name == "Test1"
assert conso_r16_2.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test16" assert bracket_r16[0].wrestler2.name == "Test17"
assert conso_r16_2.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test7" winner_by_name("Test1", bracket_r16[0])
assert conso_r16_2.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test8" assert bracket_r16[1].wrestler1.name == "Test24"
assert conso_r16_2.select{|m| m.bracket_position_number == 3}.first.reload.wrestler1.name == "Test6" assert bracket_r16[1].wrestler2.name == "Test8"
assert conso_r16_2.select{|m| m.bracket_position_number == 3}.first.reload.wrestler2.name == "Test21" winner_by_name("Test8", bracket_r16[1])
assert conso_r16_2.select{|m| m.bracket_position_number == 4}.first.reload.wrestler1.name == "Test14" assert bracket_r16[2].wrestler1.name == "Test5"
assert conso_r16_2.select{|m| m.bracket_position_number == 4}.first.reload.wrestler2.name == "Test29" assert bracket_r16[2].wrestler2.name == "Test21"
assert conso_r16_2.select{|m| m.bracket_position_number == 5}.first.reload.wrestler1.name == "Test20" winner_by_name("Test5", bracket_r16[2])
assert conso_r16_2.select{|m| m.bracket_position_number == 5}.first.reload.wrestler2.name == "Test19" assert bracket_r16[3].wrestler1.name == "Test13"
assert conso_r16_2.select{|m| m.bracket_position_number == 6}.first.reload.wrestler1.name == "Test12" assert bracket_r16[3].wrestler2.name == "Test4"
assert conso_r16_2.select{|m| m.bracket_position_number == 6}.first.reload.wrestler2.name == "Test22" winner_by_name("Test4", bracket_r16[3])
assert conso_r16_2.select{|m| m.bracket_position_number == 7}.first.reload.wrestler1.name == "Test9" assert bracket_r16[4].wrestler1.name == "Test3"
assert conso_r16_2.select{|m| m.bracket_position_number == 7}.first.reload.wrestler2.name == "Test23" assert bracket_r16[4].wrestler2.name == "Test19"
assert conso_r16_2.select{|m| m.bracket_position_number == 8}.first.reload.wrestler1.name == "Test17" winner_by_name("Test3", bracket_r16[4])
assert conso_r16_2.select{|m| m.bracket_position_number == 8}.first.reload.wrestler2.name == "Test15" assert bracket_r16[5].wrestler1.name == "Test11"
winner_by_name("Test16", conso_r16_2.select{|m| m.bracket_position_number == 1}.first) assert bracket_r16[5].wrestler2.name == "Test27"
winner_by_name("Test8", conso_r16_2.select{|m| m.bracket_position_number == 2}.first) winner_by_name("Test27", bracket_r16[5])
winner_by_name("Test6", conso_r16_2.select{|m| m.bracket_position_number == 3}.first) assert bracket_r16[6].wrestler1.name == "Test7"
winner_by_name("Test29", conso_r16_2.select{|m| m.bracket_position_number == 4}.first) assert bracket_r16[6].wrestler2.name == "Test23"
winner_by_name("Test20", conso_r16_2.select{|m| m.bracket_position_number == 5}.first) winner_by_name("Test23", bracket_r16[6])
winner_by_name("Test12", conso_r16_2.select{|m| m.bracket_position_number == 6}.first) assert bracket_r16[7].wrestler1.name == "Test15"
winner_by_name("Test23", conso_r16_2.select{|m| m.bracket_position_number == 7}.first) assert bracket_r16[7].wrestler2.name == "Test2"
winner_by_name("Test17", conso_r16_2.select{|m| m.bracket_position_number == 8}.first) winner_by_name("Test2", bracket_r16[7])
conso_r8_1 = matches.reload.select{|m| m.bracket_position == "Conso Round of 8.1"}.sort_by{|m| m.bracket_position_number} quarters = matches.reload.select { |m| m.bracket_position == "Quarter" }.sort_by(&:bracket_position_number)
assert conso_r8_1.select{|m| m.bracket_position_number == 1}.first.reload.wrestler1.name == "Test16" assert_equal "Test1", quarters[0].reload.wrestler1.name
assert conso_r8_1.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test8" assert_equal "Test8", quarters[0].reload.wrestler2.name
assert conso_r8_1.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test6" assert_equal "Test5", quarters[1].reload.wrestler1.name
assert conso_r8_1.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test29" assert_equal "Test4", quarters[1].reload.wrestler2.name
assert conso_r8_1.select{|m| m.bracket_position_number == 3}.first.reload.wrestler1.name == "Test20" assert_equal "Test3", quarters[2].reload.wrestler1.name
assert conso_r8_1.select{|m| m.bracket_position_number == 3}.first.reload.wrestler2.name == "Test12" assert_equal "Test27", quarters[2].reload.wrestler2.name
assert conso_r8_1.select{|m| m.bracket_position_number == 4}.first.reload.wrestler1.name == "Test23" assert_equal "Test23", quarters[3].reload.wrestler1.name
assert conso_r8_1.select{|m| m.bracket_position_number == 4}.first.reload.wrestler2.name == "Test17" assert_equal "Test2", quarters[3].reload.wrestler2.name
winner_by_name("Test8", conso_r8_1.select{|m| m.bracket_position_number == 1}.first) winner_by_name("Test1", quarters[0])
winner_by_name("Test6", conso_r8_1.select{|m| m.bracket_position_number == 2}.first) winner_by_name("Test5", quarters[1])
winner_by_name("Test20", conso_r8_1.select{|m| m.bracket_position_number == 3}.first) winner_by_name("Test3", quarters[2])
winner_by_name("Test17", conso_r8_1.select{|m| m.bracket_position_number == 4}.first) winner_by_name("Test2", quarters[3])
quarters = matches.reload.select{|m| m.bracket_position == "Quarter"}.sort_by{|m| m.bracket_position_number} conso_r16_1 = matches.reload.select { |m| m.bracket_position == "Conso Round of 16.1" }.sort_by(&:bracket_position_number)
assert quarters.select{|m| m.bracket_position_number == 1}.first.reload.wrestler1.name == "Test1" assert_equal "Test31", conso_r16_1[0].reload.wrestler1.name
assert quarters.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test25" assert_equal "Test16", conso_r16_1[0].reload.wrestler2.name
assert quarters.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test5" assert_equal "Test10", conso_r16_1[1].reload.wrestler1.name
assert quarters.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test4" assert_equal "Test26", conso_r16_1[1].reload.wrestler2.name
assert quarters.select{|m| m.bracket_position_number == 3}.first.reload.wrestler1.name == "Test3" assert_equal "Test6", conso_r16_1[2].reload.wrestler1.name
assert quarters.select{|m| m.bracket_position_number == 3}.first.reload.wrestler2.name == "Test11" assert_equal "Test22", conso_r16_1[2].reload.wrestler2.name
assert quarters.select{|m| m.bracket_position_number == 4}.first.reload.wrestler1.name == "Test10" assert_equal "Test45", conso_r16_1[3].reload.wrestler1.name
assert quarters.select{|m| m.bracket_position_number == 4}.first.reload.wrestler2.name == "Test2" assert_equal "Test30", conso_r16_1[3].reload.wrestler2.name
winner_by_name("Test1", quarters.select{|m| m.bracket_position_number == 1}.first) assert_equal "Test35", conso_r16_1[4].reload.wrestler1.name
winner_by_name("Test5", quarters.select{|m| m.bracket_position_number == 2}.first) assert_equal "Test20", conso_r16_1[4].reload.wrestler2.name
winner_by_name("Test11", quarters.select{|m| m.bracket_position_number == 3}.first) assert_equal "Test43", conso_r16_1[5].reload.wrestler1.name
winner_by_name("Test2", quarters.select{|m| m.bracket_position_number == 4}.first) assert_equal "Test59", conso_r16_1[5].reload.wrestler2.name
assert_equal "Test25", conso_r16_1[6].reload.wrestler1.name
assert_equal "Test9", conso_r16_1[6].reload.wrestler2.name
assert_equal "Test47", conso_r16_1[7].reload.wrestler1.name
assert_equal "Test32", conso_r16_1[7].reload.wrestler2.name
winner_by_name("Test16", conso_r16_1[0])
winner_by_name("Test10", conso_r16_1[1])
winner_by_name("Test6", conso_r16_1[2])
winner_by_name("Test30", conso_r16_1[3])
winner_by_name("Test35", conso_r16_1[4])
winner_by_name("Test43", conso_r16_1[5])
winner_by_name("Test25", conso_r16_1[6])
winner_by_name("Test32", conso_r16_1[7])
conso_r8_2 = matches.reload.select{|m| m.bracket_position == "Conso Round of 8.2"}.sort_by{|m| m.bracket_position_number} conso_r16_2 = matches.reload.select { |m| m.bracket_position == "Conso Round of 16.2" }.sort_by(&:bracket_position_number)
assert conso_r8_2.select{|m| m.bracket_position_number == 1}.first.reload.wrestler1.name == "Test25" assert_equal "Test17", conso_r16_2[0].reload.wrestler1.name
assert conso_r8_2.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test8" assert_equal "Test16", conso_r16_2[0].reload.wrestler2.name
assert conso_r8_2.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test4" assert_equal "Test24", conso_r16_2[1].reload.wrestler1.name
assert conso_r8_2.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test6" assert_equal "Test10", conso_r16_2[1].reload.wrestler2.name
assert conso_r8_2.select{|m| m.bracket_position_number == 3}.first.reload.wrestler1.name == "Test3" assert_equal "Test21", conso_r16_2[2].reload.wrestler1.name
assert conso_r8_2.select{|m| m.bracket_position_number == 3}.first.reload.wrestler2.name == "Test20" assert_equal "Test6", conso_r16_2[2].reload.wrestler2.name
assert conso_r8_2.select{|m| m.bracket_position_number == 4}.first.reload.wrestler1.name == "Test10" assert_equal "Test13", conso_r16_2[3].reload.wrestler1.name
assert conso_r8_2.select{|m| m.bracket_position_number == 4}.first.reload.wrestler2.name == "Test17" assert_equal "Test30", conso_r16_2[3].reload.wrestler2.name
winner_by_name("Test8", conso_r8_2.select{|m| m.bracket_position_number == 1}.first) assert_equal "Test19", conso_r16_2[4].reload.wrestler1.name
winner_by_name("Test6", conso_r8_2.select{|m| m.bracket_position_number == 2}.first) assert_equal "Test35", conso_r16_2[4].reload.wrestler2.name
winner_by_name("Test3", conso_r8_2.select{|m| m.bracket_position_number == 3}.first) assert_equal "Test11", conso_r16_2[5].reload.wrestler1.name
winner_by_name("Test10", conso_r8_2.select{|m| m.bracket_position_number == 4}.first) assert_equal "Test43", conso_r16_2[5].reload.wrestler2.name
assert_equal "Test7", conso_r16_2[6].reload.wrestler1.name
assert_equal "Test25", conso_r16_2[6].reload.wrestler2.name
assert_equal "Test15", conso_r16_2[7].reload.wrestler1.name
assert_equal "Test32", conso_r16_2[7].reload.wrestler2.name
winner_by_name("Test16", conso_r16_2[0])
winner_by_name("Test10", conso_r16_2[1])
winner_by_name("Test6", conso_r16_2[2])
winner_by_name("Test30", conso_r16_2[3])
winner_by_name("Test35", conso_r16_2[4])
winner_by_name("Test43", conso_r16_2[5])
winner_by_name("Test25", conso_r16_2[6])
winner_by_name("Test32", conso_r16_2[7])
quarters_conso = matches.reload.select{|m| m.bracket_position == "Conso Quarter"}.sort_by{|m| m.bracket_position_number} conso_r8_1 = matches.reload.select { |m| m.bracket_position == "Conso Round of 8.1" }.sort_by(&:bracket_position_number)
assert quarters_conso.select{|m| m.bracket_position_number == 1}.first.reload.wrestler1.name == "Test8" assert_equal "Test16", conso_r8_1[0].reload.wrestler1.name
assert quarters_conso.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test6" assert_equal "Test10", conso_r8_1[0].reload.wrestler2.name
assert quarters_conso.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test3" assert_equal "Test6", conso_r8_1[1].reload.wrestler1.name
assert quarters_conso.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test10" assert_equal "Test30", conso_r8_1[1].reload.wrestler2.name
winner_by_name("Test8", quarters_conso.select{|m| m.bracket_position_number == 1}.first) assert_equal "Test35", conso_r8_1[2].reload.wrestler1.name
winner_by_name("Test3", quarters_conso.select{|m| m.bracket_position_number == 2}.first) assert_equal "Test43", conso_r8_1[2].reload.wrestler2.name
assert_equal "Test25", conso_r8_1[3].reload.wrestler1.name
assert_equal "Test32", conso_r8_1[3].reload.wrestler2.name
winner_by_name("Test16", conso_r8_1[0])
winner_by_name("Test6", conso_r8_1[1])
winner_by_name("Test35", conso_r8_1[2])
winner_by_name("Test25", conso_r8_1[3])
semis = matches.reload.select{|m| m.bracket_position == "Semis"}.sort_by{|m| m.bracket_position_number} conso_r8_2 = matches.reload.select { |m| m.bracket_position == "Conso Round of 8.2" }.sort_by(&:bracket_position_number)
assert semis.select{|m| m.bracket_position_number == 1}.first.reload.wrestler1.name == "Test1" assert_equal "Test23", conso_r8_2[0].reload.wrestler1.name
assert semis.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test5" assert_equal "Test16", conso_r8_2[0].reload.wrestler2.name
assert semis.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test11" assert_equal "Test27", conso_r8_2[1].reload.wrestler1.name
assert semis.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test2" assert_equal "Test6", conso_r8_2[1].reload.wrestler2.name
winner_by_name("Test5", semis.select{|m| m.bracket_position_number == 1}.first) assert_equal "Test4", conso_r8_2[2].reload.wrestler1.name
winner_by_name("Test2", semis.select{|m| m.bracket_position_number == 2}.first) assert_equal "Test35", conso_r8_2[2].reload.wrestler2.name
assert_equal "Test8", conso_r8_2[3].reload.wrestler1.name
assert_equal "Test25", conso_r8_2[3].reload.wrestler2.name
winner_by_name("Test16", conso_r8_2[0])
winner_by_name("Test6", conso_r8_2[1])
winner_by_name("Test35", conso_r8_2[2])
winner_by_name("Test25", conso_r8_2[3])
semis_conso = matches.reload.select{|m| m.bracket_position == "Conso Semis"}.sort_by{|m| m.bracket_position_number} conso_quarters = matches.reload.select { |m| m.bracket_position == "Conso Quarter" }.sort_by(&:bracket_position_number)
assert semis_conso.select{|m| m.bracket_position_number == 1}.first.reload.wrestler1.name == "Test11" assert conso_quarters.select { |m| m.bracket_position_number == 1 }.first.reload.wrestler1.name == "Test16"
assert semis_conso.select{|m| m.bracket_position_number == 1}.first.reload.wrestler2.name == "Test8" assert conso_quarters.select { |m| m.bracket_position_number == 1 }.first.reload.wrestler2.name == "Test6"
assert semis_conso.select{|m| m.bracket_position_number == 2}.first.reload.wrestler1.name == "Test1" winner_by_name("Test16", conso_quarters.select { |m| m.bracket_position_number == 1 }.first)
assert semis_conso.select{|m| m.bracket_position_number == 2}.first.reload.wrestler2.name == "Test3" assert conso_quarters.select { |m| m.bracket_position_number == 2 }.first.reload.wrestler1.name == "Test35"
winner_by_name("Test11", semis_conso.select{|m| m.bracket_position_number == 1}.first) assert conso_quarters.select { |m| m.bracket_position_number == 2 }.first.reload.wrestler2.name == "Test25"
winner_by_name("Test3", semis_conso.select{|m| m.bracket_position_number == 2}.first) winner_by_name("Test35", conso_quarters.select { |m| m.bracket_position_number == 2 }.first)
first_finals = matches.select{|m| m.bracket_position == "1/2"}.first semis = matches.reload.select { |m| m.bracket_position == "Semis" }.sort_by(&:bracket_position_number)
third_finals = matches.select{|m| m.bracket_position == "3/4"}.first assert_equal "Test1", semis[0].reload.wrestler1.name
fifth_finals = matches.select{|m| m.bracket_position == "5/6"}.first assert_equal "Test5", semis[0].reload.wrestler2.name
seventh_finals = matches.select{|m| m.bracket_position == "7/8"}.first assert_equal "Test3", semis[1].reload.wrestler1.name
assert_equal "Test2", semis[1].reload.wrestler2.name
winner_by_name("Test5", semis[0])
winner_by_name("Test2", semis[1])
assert first_finals.reload.wrestler1.name == "Test5" semis_conso = matches.reload.select { |m| m.bracket_position == "Conso Semis" }.sort_by(&:bracket_position_number)
assert first_finals.reload.wrestler2.name == "Test2" bout0_names = [semis_conso[0].reload.wrestler1&.name, semis_conso[0].reload.wrestler2&.name]
bout1_names = [semis_conso[1].reload.wrestler1&.name, semis_conso[1].reload.wrestler2&.name]
assert (bout0_names & ["Test1","Test16"]).size == 2
assert (bout1_names & ["Test3","Test35"]).size == 2
winner_by_name("Test1", semis_conso[0])
winner_by_name("Test3", semis_conso[1])
assert third_finals.reload.wrestler1.name == "Test11" # --- PLACEMENT MATCHES ---
assert third_finals.reload.wrestler2.name == "Test3" first_finals = matches.reload.find { |m| m.bracket_position == "1/2" }
third_finals = matches.reload.find { |m| m.bracket_position == "3/4" }
fifth_finals = matches.reload.find { |m| m.bracket_position == "5/6" }
seventh_finals = matches.reload.find { |m| m.bracket_position == "7/8" }
assert fifth_finals.reload.wrestler1.name == "Test8" # 1st/2nd: winners of semis
assert fifth_finals.reload.wrestler2.name == "Test1" assert_equal "Test5", first_finals.reload.wrestler1.name
assert_equal "Test2", first_finals.reload.wrestler2.name
assert seventh_finals.reload.wrestler1.name == "Test6" # 3rd/4th: winners of Conso Semis (we set winners to champs losers 1 and 3)
assert seventh_finals.reload.wrestler2.name == "Test10" assert_equal "Test1", third_finals.reload.wrestler1.name
assert_equal "Test3", third_finals.reload.wrestler2.name
# 5th/6th: losers of Conso Semis (the CQ winners we defeated there)
assert_equal "Test16", fifth_finals.reload.wrestler1.name
assert_equal "Test35", fifth_finals.reload.wrestler2.name
# 7th/8th: losers of Conso Quarters (the two who didnt advance: 6 and 25)
assert_equal "Test6", seventh_finals.reload.wrestler1.name
assert_equal "Test25", seventh_finals.reload.wrestler2.name
# DEBUG # DEBUG
# matches.sort_by{|m| m.bout_number}.each do |match| # matches.sort_by{|m| m.bout_number}.each do |match|
# match.reload # match.reload
# puts "Round #{match.round} #{match.w1_bracket_name} vs #{match.w2_bracket_name}" # puts "Bracket Position: #{match.bracket_position} Round: #{match.round} #{match.w1_bracket_name} vs #{match.w2_bracket_name}"
# end # end
end end
end

View File

@@ -0,0 +1,55 @@
require 'test_helper'
class RandomSeedingTest < ActionDispatch::IntegrationTest
def setup
end
def clean_up_original_seeds(tournament)
tournament.wrestlers.each do |wrestler|
wrestler.original_seed = nil
wrestler.save
end
tournament.wrestlers.reload.each do |wrestler|
wrestler.bracket_line = nil
wrestler.save
end
GenerateTournamentMatches.new(tournament).generate
end
test "There are no double byes in a double elimination tournament round 1" do
create_double_elim_tournament_single_weight(18, "Regular Double Elimination 1-8")
clean_up_original_seeds(@tournament)
round_one_matches = @tournament.matches.reload.select{|m| m.round == 1}
assert round_one_matches.select{|m| m.w1.nil? and m.w2.nil? }.size == 0
end
test "There are the same number of matches in the top half and bottom half of a double elimination tournament in round 1" do
create_double_elim_tournament_single_weight(18, "Regular Double Elimination 1-8")
clean_up_original_seeds(@tournament)
round_one_matches = @tournament.matches.reload.select{|m| m.round == 1}
# 32 man bracket there are 16 matches so top half is bracket_position_number 1-8 and bottom is 9-16
round_one_top_half = round_one_matches.select{|m| !m.w1.nil? and !m.w2.nil? and m.bracket_position_number < 9}
round_one_bottom_half = round_one_matches.select{|m| !m.w1.nil? and !m.w2.nil? and m.bracket_position_number > 8}
assert round_one_top_half.size == round_one_bottom_half.size
end
test "There are the same number of matches in the top half and bottom half of a double elimination tournament in round 1 in a 6 man bracket" do
create_double_elim_tournament_single_weight(6, "Regular Double Elimination 1-8")
clean_up_original_seeds(@tournament)
round_one_matches = @tournament.matches.reload.select{|m| m.round == 1}
# 8 man bracket there are 4 matches so top half is bracket_position_number 1-2 and bottom is 3-4
round_one_top_half = round_one_matches.select{|m| !m.w1.nil? and !m.w2.nil? and m.bracket_position_number < 3}
round_one_bottom_half = round_one_matches.select{|m| !m.w1.nil? and !m.w2.nil? and m.bracket_position_number > 2}
assert round_one_top_half.size == round_one_bottom_half.size
end
test "There are no double byes in a double elimination tournament in a 6 man bracket" do
create_double_elim_tournament_single_weight(6, "Regular Double Elimination 1-8")
clean_up_original_seeds(@tournament)
round_one_matches = @tournament.matches.reload.select{|m| m.round == 1}
conso_round_one_matches = @tournament.matches.reload.select{|m| m.bracket_position == "Conso Quarter"}
assert round_one_matches.select{|m| m.w1.nil? and m.w2.nil? }.size == 0
assert conso_round_one_matches.select{|m| m.loser1_name == "BYE" and m.loser2_name == "BYE" }.size == 0
end
end

View File

@@ -8,7 +8,7 @@ class MatTest < ActiveSupport::TestCase
test "Mat validations" do test "Mat validations" do
mat = Mat.new mat = Mat.new
assert_not mat.valid? assert_not mat.valid?
assert_equal [:name], mat.errors.attribute_names assert_equal [:tournament, :name], mat.errors.attribute_names
end end
end end

View File

@@ -0,0 +1,68 @@
require 'test_helper'
class MatchBroadcastTest < ActiveSupport::TestCase
include ActionView::RecordIdentifier
test "broadcasts to old and new mats when mat changes" do
create_double_elim_tournament_single_weight_1_6(4)
mat1 = @tournament.mats.create!(name: "Mat 1")
mat2 = @tournament.mats.create!(name: "Mat 2")
@tournament.matches.update_all(mat_id: nil)
match = @tournament.matches.first
# Set an initial mat
match.update!(mat: mat1)
stream1 = stream_name_for(mat1)
stream2 = stream_name_for(mat2)
# Clear the stream so we can test changes from this state
clear_streams(stream1, stream2)
# Update the mat and check the stream
match.update!(mat: mat2)
assert_equal [mat1.id, mat2.id], match.saved_change_to_mat_id
assert_equal 1, broadcasts_for(stream1).size
assert_equal 1, broadcasts_for(stream2).size
assert_includes broadcasts_for(stream2).last, dom_id(mat2, :current_match)
end
test "broadcasts when a match is removed from a mat" do
create_double_elim_tournament_single_weight_1_6(4)
mat = @tournament.mats.create!(name: "Mat 1")
@tournament.matches.update_all(mat_id: nil)
match = @tournament.matches.first
# Set an initial mat
match.update!(mat: mat)
stream = stream_name_for(mat)
# Clear the stream so we can test changes from this state
clear_streams(stream)
# Update the mat and check the stream
match.update!(mat: nil)
assert_equal [mat.id, nil], match.saved_change_to_mat_id
assert_equal 1, broadcasts_for(stream).size
assert_includes broadcasts_for(stream).last, dom_id(mat, :current_match)
end
private
def broadcasts_for(stream)
ActionCable.server.pubsub.broadcasts(stream)
end
def clear_streams(*streams)
ActionCable.server.pubsub.clear
streams.each do |stream|
broadcasts_for(stream).clear
end
end
def stream_name_for(streamable)
Turbo::StreamsChannel.send(:stream_name_from, [streamable])
end
end

View File

@@ -8,7 +8,7 @@ class SchoolTest < ActiveSupport::TestCase
test "School validations" do test "School validations" do
school = School.new school = School.new
assert_not school.valid? assert_not school.valid?
assert_equal [:name], school.errors.attribute_names assert_equal [:tournament, :name], school.errors.attribute_names
end end
end end

View File

@@ -14,77 +14,92 @@ class TournamentTest < ActiveSupport::TestCase
# Pool to bracket match_generation_error # Pool to bracket match_generation_error
test "Tournament pool to bracket match generation errors with less than two wrestlers" do test "Tournament pool to bracket match generation errors with less than two wrestlers" do
number_of_wrestlers=1 create_a_tournament_with_single_weight("Pool to bracket", 1)
create_a_tournament_with_single_weight("Pool to bracket", number_of_wrestlers) assert_match("There is a tournament error.", @tournament.match_generation_error)
assert @tournament.match_generation_error != nil
end end
test "Tournament pool to bracket match generation errors with more than 24 wrestlers" do test "Tournament pool to bracket match generation errors with more than 24 wrestlers" do
number_of_wrestlers=25 create_a_tournament_with_single_weight("Pool to bracket", 25)
create_a_tournament_with_single_weight("Pool to bracket", number_of_wrestlers) assert_match("There is a tournament error.", @tournament.match_generation_error)
assert @tournament.match_generation_error != nil
end end
test "Tournament pool to bracket no match generation errors with 24 wrestlers" do test "Tournament pool to bracket no match generation errors with 24 wrestlers" do
number_of_wrestlers=24 create_a_tournament_with_single_weight("Pool to bracket", 24)
create_a_tournament_with_single_weight("Pool to bracket", number_of_wrestlers) assert_nil @tournament.match_generation_error
assert @tournament.match_generation_error == nil
end end
test "Tournament pool to bracket no match generation errors with 2 wrestlers" do test "Tournament pool to bracket no match generation errors with 2 wrestlers" do
number_of_wrestlers=2 create_a_tournament_with_single_weight("Pool to bracket", 2)
create_a_tournament_with_single_weight("Pool to bracket", number_of_wrestlers) assert_nil @tournament.match_generation_error
assert @tournament.match_generation_error == nil
end end
# Modified 16 Man Double Elimination match_generation_error # Modified 16 Man Double Elimination match_generation_error
test "TournamentModified 16 Man Double Elimination match generation errors with less than 12 wrestlers" do test "Tournament modified 16 man double elimination match generation errors with less than 12 wrestlers" do
number_of_wrestlers=11 create_a_tournament_with_single_weight("Modified 16 Man Double Elimination", 11)
create_a_tournament_with_single_weight("Modified 16 Man Double Elimination", number_of_wrestlers) assert_match("There is a tournament error.", @tournament.match_generation_error)
assert @tournament.match_generation_error != nil
end end
test "Tournament Modified 16 Man Double Elimination match generation errors with more than 16 wrestlers" do test "Tournament modified 16 man double elimination match generation errors with more than 16 wrestlers" do
number_of_wrestlers=17 create_a_tournament_with_single_weight("Modified 16 Man Double Elimination", 17)
create_a_tournament_with_single_weight("Modified 16 Man Double Elimination", number_of_wrestlers) assert_match("There is a tournament error.", @tournament.match_generation_error)
assert @tournament.match_generation_error != nil
end end
test "Tournament Modified 16 Man Double Elimination no match generation errors with 16 wrestlers" do test "Tournament modified 16 man double elimination no match generation errors with 16 wrestlers" do
number_of_wrestlers=16 create_a_tournament_with_single_weight("Modified 16 Man Double Elimination", 16)
create_a_tournament_with_single_weight("Modified 16 Man Double Elimination", number_of_wrestlers) assert_nil @tournament.match_generation_error
assert @tournament.match_generation_error == nil
end end
test "Tournament Modified 16 Man Double Elimination no match generation errors with 12 wrestlers" do test "Tournament modified 16 man double elimination no match generation errors with 12 wrestlers" do
number_of_wrestlers=12 create_a_tournament_with_single_weight("Modified 16 Man Double Elimination", 12)
create_a_tournament_with_single_weight("Modified 16 Man Double Elimination", number_of_wrestlers) assert_nil @tournament.match_generation_error
assert @tournament.match_generation_error == nil
end end
# Double Elimination match_generation_error # Double Elimination match_generation_error
test "Tournament Double Elimination 1-8 match generation errors with less than 4 wrestlers" do test "Tournament regular double elimination 1-8 match generation errors with less than 2 wrestlers" do
number_of_wrestlers=3 create_a_tournament_with_single_weight("Regular Double Elimination 1-8", 1)
create_a_tournament_with_single_weight("Double Elimination 1-8", number_of_wrestlers) assert_match("There is a tournament error.", @tournament.match_generation_error)
assert @tournament.match_generation_error != nil
end end
test "Tournament Double Elimination 1-8 match generation errors with more than 64 wrestlers" do test "Tournament regular double elimination 1-8 match generation errors with more than 64 wrestlers" do
number_of_wrestlers=65 create_a_tournament_with_single_weight("Regular Double Elimination 1-8", 65)
create_a_tournament_with_single_weight("Double Elimination 1-8", number_of_wrestlers) assert_match("There is a tournament error.", @tournament.match_generation_error)
assert @tournament.match_generation_error != nil
end end
test "Tournament Double Elimination 1-8 no match generation errors with 16 wrestlers" do test "Tournament regular double elimination 1-8 no match generation errors with 64 wrestlers" do
number_of_wrestlers=16 create_a_tournament_with_single_weight("Regular Double Elimination 1-8", 64)
create_a_tournament_with_single_weight("Double Elimination 1-8", number_of_wrestlers) assert_nil @tournament.match_generation_error
assert @tournament.match_generation_error == nil
end end
test "Tournament Double Elimination 1-8 no match generation errors with 4 wrestlers" do test "Tournament regular double elimination 1-8 no match generation errors with 2 wrestlers" do
number_of_wrestlers=4 create_a_tournament_with_single_weight("Regular Double Elimination 1-8", 2)
create_a_tournament_with_single_weight("Double Elimination 1-8", number_of_wrestlers) assert_nil @tournament.match_generation_error
assert @tournament.match_generation_error == nil end
test "Tournament match generation errors when a wrestler seed exceeds bracket size" do
create_a_tournament_with_single_weight("Pool to bracket", 4)
@tournament.weights.first.wrestlers.first.update!(original_seed: 8)
assert_match("There is a tournament error.", @tournament.match_generation_error)
assert_match("greater than the amount of wrestlers", @tournament.match_generation_error)
end
test "Tournament match generation errors when duplicate original seeds are present" do
create_a_tournament_with_single_weight("Pool to bracket", 4)
wrestlers = @tournament.weights.first.wrestlers.order(:id)
wrestlers.first.update!(original_seed: 2)
wrestlers.second.update!(original_seed: 2)
assert_match("There is a tournament error.", @tournament.match_generation_error)
assert_match("seeded 2", @tournament.match_generation_error)
end
test "Tournament match generation errors when original seeds are out of order" do
create_a_tournament_with_single_weight("Pool to bracket", 4)
wrestlers = @tournament.weights.first.wrestlers.order(:id)
wrestlers.first.update!(original_seed: 1)
wrestlers.second.update!(original_seed: 3)
wrestlers.third.update!(original_seed: nil)
wrestlers.fourth.update!(original_seed: nil)
assert_match("There is a tournament error.", @tournament.match_generation_error)
assert_match("out-of-order seeds", @tournament.match_generation_error)
end end
## End match_generation_error tests ## End match_generation_error tests

View File

@@ -8,7 +8,7 @@ class WeightTest < ActiveSupport::TestCase
test "Weight validations" do test "Weight validations" do
weight = Weight.new weight = Weight.new
assert_not weight.valid? assert_not weight.valid?
assert_equal [:max], weight.errors.attribute_names assert_equal [:tournament, :max], weight.errors.attribute_names
end end
end end

View File

@@ -8,7 +8,7 @@ class WrestlerTest < ActiveSupport::TestCase
test "Wrestler validations" do test "Wrestler validations" do
wrestler = Wrestler.new wrestler = Wrestler.new
assert_not wrestler.valid? assert_not wrestler.valid?
assert_equal [:name, :weight_id, :school_id], wrestler.errors.attribute_names assert_equal [:school, :weight, :name, :weight_id, :school_id], wrestler.errors.attribute_names
end end
end end

View File

@@ -0,0 +1,29 @@
require "test_helper"
class MatsCurrentMatchPartialTest < ActionView::TestCase
include ActionView::RecordIdentifier
test "renders current match contents when assigned" do
create_double_elim_tournament_single_weight_1_6(4)
mat = @tournament.mats.create!(name: "Mat 1")
match = @tournament.matches.first
match.update!(mat: mat)
render partial: "mats/current_match", locals: { mat: mat }
assert_includes rendered, "Bout"
assert_includes rendered, match.bout_number.to_s
assert_includes rendered, mat.name
end
test "renders friendly message when no matches assigned" do
create_double_elim_tournament_single_weight_1_6(4)
mat = @tournament.mats.create!(name: "Mat 1")
@tournament.matches.update_all(mat_id: nil)
render partial: "mats/current_match", locals: { mat: mat }
assert_includes rendered, "No matches assigned to this mat."
end
end