-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Replace matchmaking logic with new strategies * Introduce minitest and deprecate rspec * Update README to reflect the major changes
- Loading branch information
Showing
78 changed files
with
1,239 additions
and
1,099 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,3 +26,5 @@ | |
|
||
/app/assets/builds/* | ||
!/app/assets/builds/.keep | ||
|
||
.irb_history |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,130 +1,17 @@ | ||
# Double Up | ||
|
||
This application is intended to be a self-hosted version of Donut. | ||
## About | ||
|
||
## System Requirements | ||
Double Up started simply as a self-hosted version of a Slack app called Donut. Like Donut, Double Up's purpose is to connect individuals in a serendipitous way, but as far as I'm aware, that's where the comparison stops. At Test Double, we're a remote company. A remote consulting company, so there is another step removed for some of us as we help different clients from each other. | ||
|
||
This application uses Ruby 3.0 | ||
We needed something that promoted getting together. The requirements were minimal, so we took a stab at doing it ourselves. It was very barebones initially, but it worked. It was set up in a way that used a designated channel as opt-in, so if you joined that channel, you'd be in the list of participants waiting for Double Up to match you. It didn't even have a name at that time either. | ||
|
||
## Dev Setup | ||
For awhile, it worked as expected. Take a certain number of people from a list randomly and then move onto the rest until everyone was in a group. Time revealed that basic randomness wasn't exactly what we wanted, however. We began noticing that some people were in the same group for consecutive weeks. There's nothing exactly wrong with that, conceptually, but we wanted to regularly meet up and connect with a variety of others at Test Double. | ||
|
||
Install dependencies with `bin/bundle install` and then run the app with `bin/dev`. If you want to receive a command from Slack, `ngrok http 3000` is helpful to run first to get a domain you can provide your instance of rails via `NGROK_DOMAIN`. For example, | ||
Eventually, the app was named Double Up officially and we've been (in short bursts of motivation and capacity) modifying it to better meet our needs and promote connectedness within our organization. | ||
|
||
```bash | ||
$ NGROK_DOMAIN=1fa7-63-99-55-76.ngrok.io bin/dev | ||
``` | ||
## Further reading | ||
|
||
## App Setup Instructions | ||
|
||
1. Follow instructions and [create a new slack app](https://api.slack.com/authentication/basics) | ||
2. Add the following scopes to your slack app: `users:read`, `mpim:write`, `im:write`, `chat:write`, `channels:join`, `channels:manage`, `groups:write`, `groups:read`, `mpim:read`, `im:read`, and `channels:read`. | ||
3. Create a new app in Heroku or your hosting service of choice. Go to wherever you can add environment variables. | ||
4. Set `MIN_GROUP_SIZE` to 3. | ||
5. Get your secret key base and the oauth token for your newly created slack app. Store those as `SLACK_SIGNING_SECRET` and `SLACK_OAUTH_TOKEN`. | ||
6. Create Slash Command called `doubleup` with a URL path of `/command/handle`. | ||
7. Deploy app to Heroku (or your hosting service of choice). | ||
8. If using Heroku, install Heroku Scheduler. | ||
9. Schedule `rake create_groups` to run every day at a time of your choice. If you have a hosting service that will allow you to run cron jobs, you can remove the code mentioned above that checks that day and week and just use cron format. If using Heroku, just run daily and it will quit if it's not the right day 🙂 | ||
10. PROFIT! | ||
|
||
## Matchmaking Approach | ||
|
||
Some inspiration was taken from the following two problems | ||
|
||
- [Stable Marriage Problem](https://en.wikipedia.org/wiki/Stable_marriage_problem) | ||
- [Stable Roommates Problem](https://en.wikipedia.org/wiki/Stable_roommates_problem) | ||
|
||
Some requirements this approach had to solve for | ||
|
||
1. Individuals can join or leave a slack channel at any time to opt-in or opt-out respectively | ||
2. Repeats for each cycle should be rare for a particular group | ||
3. Duplicates should be rare between the various types of groups for which an individual is being matched | ||
|
||
The approach starts with the a list of participants for a particular grouping, with the goal being that every participant is included in a single group. Each group size for a grouping can be configured, with a default of 2. For each participant, a score, starting at 0, is calculated for every other participant based upon a few factors. | ||
|
||
1. Has this other participant been recently paired up with the current participant in this grouping? | ||
2. Has this other participant been recently paired up with the current participant in any other grouping? | ||
|
||
The score for the other participants is incremented by 1 for each grouping where "Yes" is the answer to those questions. That is repeated for each participant until eventually everyone has a score for every other participant. | ||
|
||
Once scored, a threshold of 0 is set and for each participant, a random other participant is selected that has a score of 0. After running through each, the threshold is incremented by 1 if any unmatched participants exist. That repeats until eventually everyone is found within a particular group. | ||
|
||
### Example | ||
|
||
That was as fun to write as it was to read, so here is an example, hopefully breaking it down more practically. | ||
|
||
Let's say we have Frodo, Sam, Pippin, and Meriadoc as participants in the grouping, Second Breakfast. In addition, let's say that we want "recent" to mean 1 group ago. | ||
|
||
**First Time Matchmaking** | ||
|
||
The first time the matchmaking runs, the scoring will conceptually look like | ||
|
||
``` | ||
Frodo => { | ||
Sam with a score of 0 | ||
Pippin with a score of 0 | ||
Meriadoc with a score of 0 | ||
} | ||
Sam => { | ||
Frodo with a score of 0 | ||
Pippin with a score of 0 | ||
Meriadoc with a score of 0 | ||
} | ||
Pippin => { | ||
Frodo with a score of 0 | ||
Sam with a score of 0 | ||
Meriadoc with a score of 0 | ||
} | ||
Meriadoc => { | ||
Frodo with a score of 0 | ||
Sam with a score of 0 | ||
Pippin with a score of 0 | ||
} | ||
``` | ||
|
||
Since everyone has a score of 0 for everyone else, the matchmaking is simple. Someone is randomly selected for Frodo, so let's say, Pippin. Next up is Sam and he is detected to be ungrouped, so Meriadoc is "randomly" selected. Once we get to Pippin, we detect Pippin as being grouped, then Meriadoc finally is detected as grouped. | ||
|
||
``` | ||
Group 1 is Frodo & Pippin | ||
Group 2 is Sam & Meriadoc | ||
``` | ||
|
||
**Second Time Matchmaking** | ||
|
||
Here is where things get interesting. The scores change because the most recent group for each person will increase the score by 1. | ||
|
||
``` | ||
Frodo => { | ||
Sam with a score of 0 | ||
Pippin with a score of 1 | ||
Meriadoc with a score of 0 | ||
} | ||
Sam => { | ||
Frodo with a score of 0 | ||
Pippin with a score of 0 | ||
Meriadoc with a score of 1 | ||
} | ||
Pippin => { | ||
Frodo with a score of 1 | ||
Sam with a score of 0 | ||
Meriadoc with a score of 0 | ||
} | ||
Meriadoc => { | ||
Frodo with a score of 0 | ||
Sam with a score of 1 | ||
Pippin with a score of 0 | ||
} | ||
``` | ||
|
||
Now when we look for a pair for Frodo, Pippin is excluded since his score is greater than the starting threshold of 0. Frodo cannot experience a repeat pair in this way, so he's paired up with Sam. | ||
|
||
That concept continues until everyone is matched. | ||
|
||
Once we get to the third time matchmaking, the score is reset from the first time because that was 2 times ago. If we wanted to simulate ensuring everyone gets paired up before repeating, then we could check 2 groups back, ensuring the score reset happens after everyone has been paired already, like round-robin. | ||
- [Development setup](docs/development_setup) | ||
- [Matchmaking strategies](docs/matchmaking_strategies) | ||
- [Usage & setup](docs/usage_and_setup) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.