Class: SpreeCmCommissioner::WaitingGuestsCaller
- Inherits:
-
BaseInteractor
- Object
- BaseInteractor
- SpreeCmCommissioner::WaitingGuestsCaller
- Defined in:
- app/interactors/spree_cm_commissioner/waiting_guests_caller.rb
Constant Summary collapse
- MIN_WAIT_TO_ENTER_SECONDS =
2 min floor — Waiting Room step (full journey, more uncertainty)
120- MIN_QUEUE_TO_ENTER_SECONDS =
1 min floor — Queue step (position assigned, one caller cycle minimum)
60
Instance Method Summary collapse
- #call ⇒ Object
-
#calling_all(waiting_guests) ⇒ Object
For alert waiting guests to enter room, we just update :allow_to_enter_room_at.
-
#compute_avg_queue_to_enter_seconds(guests) ⇒ Object
Average queue wait (position_set_at → allow_to_enter_room_at) for just-called guests.
-
#compute_avg_wait_to_enter_seconds(guests) ⇒ Object
Average total wait (queued_at → allow_to_enter_room_at) for just-called guests.
- #current_date ⇒ Object
- #default_records_path(date) ⇒ Object
- #eligible_guests_in(records_path, limit) ⇒ Object
- #fetch_available_slots ⇒ Object
-
#fetch_long_waiting_guests(available_slots) ⇒ Object
This query requires an index; create it in Firebase beforehand.
- #fetch_max_sessions ⇒ Object
- #firestore ⇒ Object
- #lobby_data ⇒ Object
- #lobby_document ⇒ Object
-
#mark_as(full:, available_slots:, avg_wait_to_enter_seconds: nil, avg_queue_to_enter_seconds: nil) ⇒ Object
merge: true so we preserve the published ‘waiting_guests_records_path` on the lobby doc.
- #previous_date ⇒ Object
-
#previous_records_path ⇒ Object
Drain target is derived from the server date, never the (possibly stale) lobby pointer.
-
#records_path ⇒ Object
Published path is authoritative; fall back to the server’s own date if not yet published.
- #service_account ⇒ Object
Instance Method Details
#call ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 9 def call available_slots = fetch_available_slots return unless available_slots.positive? long_waiting_guests = fetch_long_waiting_guests(available_slots) calling_all(long_waiting_guests) mark_as( full: long_waiting_guests.size >= available_slots, available_slots: available_slots - long_waiting_guests.size, avg_wait_to_enter_seconds: compute_avg_wait_to_enter_seconds(long_waiting_guests), avg_queue_to_enter_seconds: compute_avg_queue_to_enter_seconds(long_waiting_guests) ) end |
#calling_all(waiting_guests) ⇒ Object
For alert waiting guests to enter room, we just update :allow_to_enter_room_at. App will listen to firebase & start refresh session token to enter room.
73 74 75 76 77 78 79 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 73 def calling_all(waiting_guests) waiting_guests.each do |document| data = document.data.dup data[:allow_to_enter_room_at] = Time.zone.now document.ref.update(data) end end |
#compute_avg_queue_to_enter_seconds(guests) ⇒ Object
Average queue wait (position_set_at → allow_to_enter_room_at) for just-called guests. Used for the Queue step ETA: how long after getting a position until I actually enter. Returns nil when none of the called guests had position_set_at (first run, or no prior stamp).
110 111 112 113 114 115 116 117 118 119 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 110 def compute_avg_queue_to_enter_seconds(guests) return nil if guests.empty? now = Time.zone.now positioned = guests.select { |doc| doc.data[:position_set_at] } return nil if positioned.empty? total = positioned.sum { |doc| (now - doc.data[:position_set_at].to_time).to_f } [(total / positioned.size).round, MIN_QUEUE_TO_ENTER_SECONDS].max end |
#compute_avg_wait_to_enter_seconds(guests) ⇒ Object
Average total wait (queued_at → allow_to_enter_room_at) for just-called guests. Used for the Waiting Room step ETA: how long until I get in from when I joined.
99 100 101 102 103 104 105 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 99 def compute_avg_wait_to_enter_seconds(guests) return nil if guests.empty? now = Time.zone.now total = guests.sum { |doc| (now - doc.data[:queued_at].to_time).to_f } [(total / guests.size).round, MIN_WAIT_TO_ENTER_SECONDS].max end |
#current_date ⇒ Object
81 82 83 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 81 def current_date Time.zone.now.strftime('%Y-%m-%d') end |
#default_records_path(date) ⇒ Object
67 68 69 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 67 def default_records_path(date) "waiting_guests/#{date}/records" end |
#eligible_guests_in(records_path, limit) ⇒ Object
49 50 51 52 53 54 55 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 49 def eligible_guests_in(records_path, limit) firestore.col(records_path) .where('allow_to_enter_room_at', '==', nil) .order('queued_at') .limit(limit) .get.to_a end |
#fetch_available_slots ⇒ Object
24 25 26 27 28 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 24 def fetch_available_slots max_sessions = fetch_max_sessions active_sessions = SpreeCmCommissioner::WaitingRoomSession.active.count max_sessions - active_sessions end |
#fetch_long_waiting_guests(available_slots) ⇒ Object
This query requires an index; create it in Firebase beforehand. Client must create waiting_guests documents with :queued_at and :allow_to_enter_room_at set to nil to allow filter + order queries.
Yesterday’s guests are always older than today’s, so fill from yesterday first, then use any leftover slots for today. This way no one queued before the midnight rollover gets skipped.
e.g. 5 slots, 2 waiting in yesterday -> take both, then take 3 from today.
36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 36 def fetch_long_waiting_guests(available_slots) previous_guests = eligible_guests_in(previous_records_path, available_slots) # Pre-flip window: the lobby pointer still points at yesterday, so both paths resolve to the # same partition — return now to avoid querying (and double-counting) it twice. return previous_guests if records_path == previous_records_path remaining_slots = available_slots - previous_guests.size return previous_guests if remaining_slots <= 0 previous_guests + eligible_guests_in(records_path, remaining_slots) end |
#fetch_max_sessions ⇒ Object
129 130 131 132 133 134 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 129 def fetch_max_sessions fetcher = SpreeCmCommissioner::WaitingRoomSystemMetadataFetcher.new(firestore: firestore) fetcher.load_document_data fetcher.max_sessions_count_with_min end |
#firestore ⇒ Object
136 137 138 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 136 def firestore @firestore ||= Google::Cloud::Firestore.new(project_id: service_account[:project_id], credentials: service_account) end |
#lobby_data ⇒ Object
121 122 123 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 121 def lobby_data @lobby_data ||= lobby_document.get.data end |
#lobby_document ⇒ Object
125 126 127 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 125 def lobby_document @lobby_document ||= firestore.col('waiting_rooms').doc('lobby') end |
#mark_as(full:, available_slots:, avg_wait_to_enter_seconds: nil, avg_queue_to_enter_seconds: nil) ⇒ Object
merge: true so we preserve the published ‘waiting_guests_records_path` on the lobby doc.
90 91 92 93 94 95 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 90 def mark_as(full:, available_slots:, avg_wait_to_enter_seconds: nil, avg_queue_to_enter_seconds: nil) data = { full: full, available_slots: available_slots } data[:avg_wait_to_enter_seconds] = avg_wait_to_enter_seconds if avg_wait_to_enter_seconds data[:avg_queue_to_enter_seconds] = avg_queue_to_enter_seconds if avg_queue_to_enter_seconds lobby_document.set(data, merge: true) end |
#previous_date ⇒ Object
85 86 87 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 85 def previous_date 1.day.ago.strftime('%Y-%m-%d') end |
#previous_records_path ⇒ Object
Drain target is derived from the server date, never the (possibly stale) lobby pointer.
63 64 65 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 63 def previous_records_path default_records_path(previous_date) end |
#records_path ⇒ Object
Published path is authoritative; fall back to the server’s own date if not yet published.
58 59 60 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 58 def records_path lobby_data&.dig(:waiting_guests_records_path).presence || default_records_path(current_date) end |
#service_account ⇒ Object
140 141 142 |
# File 'app/interactors/spree_cm_commissioner/waiting_guests_caller.rb', line 140 def service_account @service_account ||= Rails.application.credentials.cloud_firestore_service_account end |