Skip to content

Commit

Permalink
Introduces new pairing flow.
Browse files Browse the repository at this point in the history
  • Loading branch information
toomuchpete committed Apr 2, 2024
1 parent 1732cef commit 61d586e
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 37 deletions.
7 changes: 5 additions & 2 deletions app/controllers/registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,10 @@ def populate_registration

@registration.notes = reg_params[:notes]
@registration.shirt_size = reg_params[:shirt_size]
if reg_params[:pair_id]
@registration.pair_id = reg_params[:pair_id].first
if reg_params[:pair_requested_user_id]
pair_user_id = reg_params[:pair_requested_user_id].reject {|x| x.blank?}.first
pc = PairingCoordinator.new(@registration.league)
pc.request_pair(@registration.user, pair_user_id)
end

if permitted_to? :manage, @registration.league
Expand All @@ -192,6 +194,7 @@ def populate_registration
def load_registration_from_params
begin
@registration = Registration.find(params[:id])
@league = @registration.league
rescue
redirect_to registrations_user_path(current_user), flash: {error: "Could not load registration for ID '#{params[:id]}'."}
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/invitation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def decline
self[:status] = 'declined'
save!

InvitationMailer.delay.pair_request_result(self._id.to_s)
#InvitationMailer.delay.pair_request_result(self._id.to_s)
end

def cancel
Expand Down
10 changes: 8 additions & 2 deletions app/models/league.rb
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,10 @@ def handle_accepted_invite(invitation)

sender_reg.pair = invitation.recipient
recipient_reg.pair = invitation.sender
sender_reg.save!
recipient_reg.save!

# We ignore validation errors because pairs can be confirmed before registation is complete which can lead to some odd errors
sender_reg.save(validate: false)
recipient_reg.save(validate: false)
end
end

Expand Down Expand Up @@ -326,6 +328,10 @@ def self.expire_stale_registrations
end
end

def invitations
Invitation.where(handler_class: "League", handler_id: _id)
end

private

def build_options_if_nil
Expand Down
8 changes: 8 additions & 0 deletions app/models/registration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,14 @@ def has_signed_waiver
end
end

def sent_invitations
league.invitations.where(sender: user)
end

def received_invitations
league.invitations.where(recipient: user)
end

private

def load_user_info
Expand Down
2 changes: 1 addition & 1 deletion app/views/_user_multiselect.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

- user_search_params = {}
- if local_assigns[:exclude].nil? == false
- user_search_params["exclude"] = exclude.map(&:_id)
- user_search_params["exclude"] = exclude


- content_for :page_scripts do
Expand Down
4 changes: 3 additions & 1 deletion app/views/dashboard/_tutorials.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
%p
If you're new around here, things might be a bit confusing the first time you register.
Rest assured that any problem you are facing, someone has likely experienced before.
%p
Email [email protected] if you need assistance.
%ul
- # %li= link_to 'Tutorial: Signing up for leagues.afdc.com', 'http://toomuchpete.clarify-it.com/d/fvm4pl'
- # %li= link_to 'Tutorial: Reset or change your password', 'http://toomuchpete.clarify-it.com/d/dh9xta'
- # %li= link_to 'Tutorial: Updating your gRank', 'http://toomuchpete.clarify-it.com/d/ddna6c'
- # %li= link_to 'Tutorial: Registering for a league', 'http://toomuchpete.clarify-it.com/d/9lhk4h'
%li= link_to 'Tutorial: Pairing with another player', 'https://docs.google.com/document/d/1SMOyrNxigKO__k5eSKrKo9DJ0W3YBEVHHbwnZAuSTrE/edit?usp=sharing'
- # %li= link_to 'Tutorial: Pairing with another player', 'https://docs.google.com/document/d/1SMOyrNxigKO__k5eSKrKo9DJ0W3YBEVHHbwnZAuSTrE/edit?usp=sharing'
1 change: 0 additions & 1 deletion app/views/dashboard/homepage.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
= render 'games'
.col-md-4
= render 'leagues'
= render 'tutorials'
%hr
= render 'sponsors'
- else
Expand Down
19 changes: 13 additions & 6 deletions app/views/invitation_mailer/pair_request.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@
You have received a request to pair with #{link_to(@invite.sender.name, user_url(@invite.sender))}
for #{link_to(@league.name, league_url(@league))}.

%p In order for this request to be honored by the league, you must confirm that you're okay with it.

%p If you'd rather not pair with this person, you can ignore or decline the invite.
%p
In order for this request to be honored by the league, you must also choose to pair with them.
If you'd rather not pair with this person, you can ignore or decline the invite.

- content_for :callout_box do
Click
=link_to "here", invitations_url
to manage your existing invitations
- if recipient_reg = @league.registration_for(@invite.recipient)
Click
=link_to "here", edit_registration_path(recipient_reg)
to edit your registration for
=@league.name
- else
Click
=link_to "here", register_league_path(@league)
to register for
=@league.name
12 changes: 6 additions & 6 deletions app/views/invitation_mailer/pair_request_result.html.haml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
%p #{@invite.sender.name}:
%p #{@invite.sender.name} & #{@invite.recipient.name}:

%p
Your request to pair with #{link_to(@invite.recipient.name, user_url(@invite.recipient))}
for #{link_to(@league.name, league_url(@league))} has been #{@invite.status}.
Nice job! You're paired up for #{link_to(@league.name, league_url(@league))}!

- content_for :callout_box do
Click
=link_to "here", invitations_url
to manage your existing invitations
%b REMEMBER:
%p
Pairing only counts if both players are in the league. Pairing with someone
on the waitlist does not guarantee that person a spot in the league.
4 changes: 2 additions & 2 deletions app/views/registration_mailer/registration_active.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
- content_for :callout_box do
%strong A note about pairs:
This league allows players to pair up to guarantee that they will be on the same team. The only way to ensure that your pair
request will be respected is if you <a href="https://docs.google.com/document/d/1yT_TgUjLfcgKBY-uCQD5xmwqL7vowbdOHhAM6Xy0J50/edit">use the official pairing system</a>.
Requests made to the commissioner or in the notes field of your registration cannot be guaranteed.
request will be respected is if you use the official pairing system. Requests made to the commissioner or in the notes field
of your registration cannot be guaranteed.
55 changes: 54 additions & 1 deletion app/views/registrations/_form.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
= f.select :gen_availability, [nil, '25%', '50%', '75%', '100%'], label: 'Availability'
- if @registration.league.created_at > Date.new(2019, 4, 11)
= f.select :shirt_size, [nil, 'XXL', 'XL', 'Large', 'Medium', 'Small', 'XS', 'XXS'], label: 'Unisex Shirt Size'
.control-group
.form-group
.controls
= f.check_box :eos_availability, value: 1, label: "Will attend end of season tourney"
- rank_max = 6 if (@registration.league.sport == 'goaltimate' && @registration.user.gender == 'female')
Expand All @@ -20,6 +20,39 @@
- if permitted_to?(:manage, @registration.league) && @registration.persisted?
= f.text_field :commish_rank, label: 'League Rank'
= f.select :player_strength, [nil, 'Runner', 'Thrower', 'Both']
- if @league.allow_pairs? && [email protected]?
- received_invites = @registration.received_invitations.outstanding
- if received_invites.count > 0
.form-group
%label.control-label Accept Pair Invite
.controls
- received_invites.each do |inv|
%label.radio
%input{type: 'radio', name: 'registration[pair_requested_user_id][]', class: 'received_invite_radio', value: inv.sender._id.to_s }
=inv.sender.name
.checkbox
%label{for: 'ignore_received_invites'}
%input{type: 'checkbox', id: 'ignore_received_invites', value: nil, checked: true}
None of the Above

- invited_user = @registration.sent_invitations.outstanding.first&.recipient
- exclusion_list = PairingCoordinator.new(@league).excluded_players
- exclusion_list.append(current_user._id.to_s)
= render partial: '/user_multiselect', locals: {form: f, fieldname: 'pair_requested_user_id', label: "Invite Pair", users: [invited_user], limit: 1, exclude: exclusion_list}

- if @registration.linked?
.form-group
%label.control-label Current Pair
- if @registration.cored?
%span.span3.uneditable-input n/a
.controls
%span.help-inline Cored Players Cannot Pair
- else
%span.span3.uneditable-input
= @registration.pair.name
.controls
%span.form-control.help-inline To remove a confirmed pair, email [email protected]

= f.text_area :notes, rows: 5
- if @registration.new_record?
.control-group
Expand All @@ -38,3 +71,23 @@
= f.submit "Register", disable_with: 'Registering...', class: "btn btn-primary"
- else
= f.submit "Update", disable_with: 'Updating...', class: "btn btn-primary"


- content_for :page_scripts do
:javascript
$(function(){
$('#ignore_received_invites').on('change', function(e){
if ($('#ignore_received_invites').is(':checked')) {
$('.received_invite_radio').prop('checked', false);
$('.user-multi-select .new-user').prop( "disabled", false);
}
});

$('.received_invite_radio').on('change', function(e){
if ($(e.target).is(':checked')) {
$('#ignore_received_invites').prop('checked', false);
$('.user-multi-select a.remove').click();
$('.user-multi-select .new-user').prop( "disabled", true);
}
});
});
14 changes: 9 additions & 5 deletions app/views/registrations/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,16 @@

%hr

%dt Pair:
%dd
- if @registration.pair_id
- if @registration.pair_id
%dt Pair:
%dd
= @registration.pair.name
- else
n/a
- elsif invite = Invitation.where(handler_id: @registration.league._id, sender: @registration.user, type: "pair", status: "new").first
%dt Requested Pair:
%dd
= invite.recipient.name



%dt Signup Time:
%dd= @registration.formatted_signup_timestamp
Expand Down
19 changes: 10 additions & 9 deletions config/authorization_rules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

# Things all users can do:
has_permission_on :users, to: [:index, :search, :show]
has_permission_on :invitations, to: [:index]
has_permission_on :teams, to: [:index, :search, :show, :view_roster]
has_permission_on :registration_groups, to: [:index]
has_permission_on :registrations, to: [:create]
Expand All @@ -38,14 +37,6 @@
if_attribute :_id => is { user._id }
end

has_permission_on :invitations, to: [:show, :accept, :decline] do
if_attribute recipient_id: is { user._id }
end

has_permission_on :invitations, to: [:show, :cancel] do
if_attribute sender_id: is { user._id }
end

has_permission_on :registrations, to: [:checkout, :show, :pay, :donate, :waitlist_authorize, :edit, :update, :cancel] do
if_attribute user_id: is { user._id }
end
Expand Down Expand Up @@ -116,6 +107,16 @@
includes :'spirit-manager'
includes :'covid-admin'

has_permission_on :invitations, to: [:index]

has_permission_on :invitations, to: [:show, :accept, :decline] do
if_attribute recipient_id: is { user._id }
end

has_permission_on :invitations, to: [:show, :cancel] do
if_attribute sender_id: is { user._id }
end

has_permission_on :global, :to => [:see_debug]

has_permission_on :users, :to => [:edit_avatar, :update_avatar, :destroy_avatar, :login_as, :edit_permissions]
Expand Down
68 changes: 68 additions & 0 deletions lib/afdc/pairing_coordinator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# TODO: Exclude already-paired players from search results

class PairingCoordinator
attr_reader :league

def initialize(league)
@league = league
end

def request_pair(source_player, target_player)
# puts "Requesting pair..."
src_user = load_user(source_player)
# puts "\t Source user loaded (#{source_player} => #{src_user&.name})"
tgt_user = load_user(target_player)
# puts "\t Source user loaded (#{target_player} => #{tgt_user&.name})"

return false if src_user.nil?

src_reg = @league.registration_for(src_user)
tgt_reg = @league.registration_for(tgt_user)

# Check if either player is already paired
return false if src_reg&.linked?
return false if tgt_reg&.linked?
# puts "\t Neither user is paired/cored yet"

# Cancel all outstanding sent pair requests:
src_reg.sent_invitations.outstanding.update_all(status: "canceled")
return true if tgt_user.nil?

# Accept outstanding pair invite from the target player, if it exists
if tgt_reg.present? && tgt_reg.sent_invitations.outstanding.where(recipient: src_user).exists?
tgt_reg.sent_invitations.outstanding.where(recipient: src_user).first.accept
# puts "\t Pair Successful!"
return true
end

if src_reg.sent_invitations.where(recipient: tgt_user).exists?
src_reg.sent_invitations.where(recipient: tgt_user).update(status: "sent")
# puts "\t Reusing old Invite"
return true
end
Invitation.create!(type: "pair", sender: src_user, recipient: tgt_user, handler: @league)
end

def excluded_players
cored_list = RegistrationGroup.where(league: @league).all.inject([]) {|list, grp| list + grp.member_ids}
captain_list = Team.where(league: @league).all.inject([]) {|list, team| list + team.captains.map(&:id)}
paired_list = @league.registrations.where(:pair_id.exists => true).all.inject([]) {|list, reg| list.append(reg.user_id)}
(cored_list + captain_list + paired_list).to_set.to_a.map(&:to_s)
end

private

def load_user(identifier)
if identifier.is_a?(User)
return identifier
end

if identifier.is_a?(Registration)
return identifier.user
end

if identifier.is_a?(String) or identifier.is_a?(BSON::ObjectId)
return User.find(identifier)
end
end
end

0 comments on commit 61d586e

Please sign in to comment.