Skip to content

Commit ca66a9e

Browse files
committed
feat: implement inhouse integration
1 parent e0c28a3 commit ca66a9e

13 files changed

+907
-24
lines changed

app/models/inhouse.rb

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@
33
# Represents an internal practice session where an organization's own players
44
# compete against each other in a controlled environment.
55
#
6-
# An inhouse goes through three phases:
6+
# An inhouse goes through four phases:
77
# waiting — lobby open, players joining
8-
# in_progress — teams balanced, games being played
8+
# draft — captain draft in progress (picks 1-2-2-2-1)
9+
# in_progress — teams set, games being played
910
# done — session closed
1011
#
1112
class Inhouse < ApplicationRecord
13+
PICK_ORDER = %w[blue red red blue blue red red blue].freeze
14+
1215
# Associations
1316
belongs_to :organization
1417
belongs_to :created_by, class_name: 'User', foreign_key: :created_by_user_id
18+
belongs_to :blue_captain, class_name: 'Player', optional: true
19+
belongs_to :red_captain, class_name: 'Player', optional: true
1520
has_many :inhouse_participations, dependent: :destroy
1621
has_many :players, through: :inhouse_participations
1722

1823
# Enum
19-
enum :status, { waiting: 'waiting', in_progress: 'in_progress', done: 'done' }, prefix: false
24+
enum :status, { waiting: 'waiting', draft: 'draft', in_progress: 'in_progress', done: 'done' }, prefix: false
2025

2126
# Scopes
22-
scope :active, -> { where(status: %w[waiting in_progress]) }
27+
scope :active, -> { where(status: %w[waiting draft in_progress]) }
2328
scope :history, -> { where(status: 'done') }
2429
scope :recent, -> { order(created_at: :desc) }
2530

@@ -28,13 +33,29 @@ class Inhouse < ApplicationRecord
2833

2934
validate :valid_status_transition, on: :update
3035

36+
# Returns which team should pick next during draft ('blue' or 'red').
37+
# Returns nil if draft is not active or all picks are done.
38+
def current_pick_team
39+
return nil unless draft?
40+
return nil if draft_pick_number.nil?
41+
return nil if draft_pick_number >= PICK_ORDER.size
42+
43+
PICK_ORDER[draft_pick_number]
44+
end
45+
46+
# True when all 8 non-captain picks have been made.
47+
def draft_complete?
48+
draft_pick_number.to_i >= PICK_ORDER.size
49+
end
50+
3151
private
3252

3353
def valid_status_transition
3454
return unless status_changed?
3555

3656
allowed = {
37-
'waiting' => %w[in_progress done],
57+
'waiting' => %w[draft in_progress done],
58+
'draft' => %w[in_progress done],
3859
'in_progress' => %w[done],
3960
'done' => []
4061
}

app/models/inhouse_queue.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# frozen_string_literal: true
2+
3+
# Represents a role-based queue for an inhouse session.
4+
# Players join the queue by role (max 2 per role, 10 total).
5+
# Once full and checked-in, a coach starts the session from this queue.
6+
#
7+
# Lifecycle: open → check_in → closed
8+
#
9+
class InhouseQueue < ApplicationRecord
10+
ROLES = %w[top jungle mid adc support].freeze
11+
MAX_SLOTS = 10 # 5 roles × 2 players
12+
13+
belongs_to :organization
14+
belongs_to :created_by, class_name: 'User', foreign_key: :created_by_user_id
15+
has_many :inhouse_queue_entries, dependent: :destroy
16+
has_many :players, through: :inhouse_queue_entries
17+
18+
enum :status, { open: 'open', check_in: 'check_in', closed: 'closed' }, prefix: false
19+
20+
scope :active, -> { where(status: %w[open check_in]) }
21+
scope :recent, -> { order(created_at: :desc) }
22+
23+
validates :status, presence: true, inclusion: { in: statuses.keys }
24+
25+
def full?
26+
inhouse_queue_entries.size >= MAX_SLOTS
27+
end
28+
29+
def slots_for_role(role)
30+
inhouse_queue_entries.where(role: role).count
31+
end
32+
33+
def checked_in_entries
34+
inhouse_queue_entries.where(checked_in: true)
35+
end
36+
37+
def serialize(detailed: false)
38+
result = {
39+
id: id,
40+
status: status,
41+
check_in_deadline: check_in_deadline,
42+
total_entries: inhouse_queue_entries.size,
43+
total_slots: MAX_SLOTS,
44+
full: full?,
45+
created_at: created_at
46+
}
47+
48+
merge_detailed_fields(result) if detailed
49+
result
50+
end
51+
52+
private
53+
54+
def merge_detailed_fields(result)
55+
loaded = inhouse_queue_entries.includes(:player)
56+
by_role = loaded.group_by(&:role)
57+
result[:entries_by_role] = ROLES.index_with { |role| (by_role[role] || []).map(&:serialize) }
58+
result[:entries] = loaded.map(&:serialize)
59+
end
60+
end

app/models/inhouse_queue_entry.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# frozen_string_literal: true
2+
3+
# A single player slot in an InhouseQueue.
4+
# Tracks role, tier at join time, and check-in status.
5+
class InhouseQueueEntry < ApplicationRecord
6+
belongs_to :inhouse_queue
7+
belongs_to :player
8+
9+
validates :role, presence: true, inclusion: { in: InhouseQueue::ROLES }
10+
validates :player_id, uniqueness: { scope: :inhouse_queue_id, message: 'is already in this queue' }
11+
12+
def serialize
13+
{
14+
id: id,
15+
player_id: player_id,
16+
player_name: player&.summoner_name,
17+
role: role,
18+
tier_snapshot: tier_snapshot,
19+
checked_in: checked_in,
20+
checked_in_at: checked_in_at
21+
}
22+
end
23+
end

app/models/organization.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class Organization < ApplicationRecord
4747

4848
# New tier-based associations
4949
has_many :scrims, dependent: :destroy
50-
has_many :inhouses, dependent: :destroy
50+
has_many :inhouses, dependent: :destroy
51+
has_many :inhouse_queues, dependent: :destroy
5152
has_many :competitive_matches, dependent: :destroy
5253
has_many :messages, dependent: :destroy
5354
has_many :saved_builds, dependent: :destroy

0 commit comments

Comments
 (0)