Matching activists and organizers for 1 on 1 conversations
Full code for this project is on github
I’ve been organizing with my local SURJ chapter for a few years. Along with many community organizing groups, we use one-on-one conversations to get to know new members, to deepen connections between existing members, and overall build and strengthen our base of activists. (Here are some good resources about why and how to use one-on-ones in an organizing and power building context.)
This month we’re focusing on relationship building and putting more formal meetings on hold, so we had more folks to pair for conversations than usual. I love working on a project with direct practical impact (and in this case, possibly saving time for me, personally), so I thought it’d be interesting to do these matches programmatically. Plus I’d been looking for a reason to learn about the blastula
package in R.
As I started thinking about it, the project is really a system with a few pieces:
An input form for signing up for 1:1s
A list or database of those sign ups
A way to pull those sign ups into R
Actually matching folks based on some criteria
Sending some notification of those matches
Updating the sign up form to confirm that matches have been made
Setting Up Data Collection and Record Keeping
We’ve been starting to use Airtable as a light database option, so I built a sign up list and connected form there (#1 and #2 done).
After some trial and error with a package that’s no longer supported, I found the rtable package that’s basically a wrapper for the Airtable API, taking care of #3 and #6.
Creating Pairs
There are two sets of criteria that can inform these matches — amount of experience with the organization (are you a “newbie” or a “scoobie” as we’ve come to call those who have been around longer) and areas of interest. So far I’ve only tackled the first criteria, and I’m planning to come back to issue matching for v2. But even within this simpler matching there were a few considerations:
Participants could select if they’d prefer to talk to someone new, someone who’s been around longer, or “Pair me with anyone”.
Participants were asked how many conversation partners they wanted, so some members will get more than one match, but we don’t want any of the pairings to be duplicates
I expanded the sign up list so that every user had one record for every conversation they’re requesting (so if you signed up for 3 conversation, there are now 3 records for you). I then sorted the list by preference, and then by their level of engagement, and then by a random value for each user, so that the order is randomized within those with the same preference and engagement. I then split this list in half, randomly sort the second half, and then bind those two lists to create conversation pairs.
This could randomly result in duplicate pairings, so I put the above code inside a createPairs
function that is then called by a runAndCheckPairs
function. The runAndCheckPairs
will re-run the createPairs
function until there are no duplicates (and re-setting that random value every time to get new orders).
Sending Emails
The blastula
package is much easier than I anticipated! I created a simple sendMatchEmails
functions that iterates through the list of pairs, creates and sends one email to each pair so that they can easily hit reply to start the conversation. The script then updates Airtable that a match has been made.
Putting It All Together
I like using R Shiny as an easy way to create an interface for non-coders to use code I’ve written, so I dropped this all into a Shiny app that:
On mouse click, gets new, not-yet-matched records from Airtable, runs matches, and displays those pairs to the screen
User can then click another button to send emails to each pair, click “Run Matches” again to resort, and/or can download the pairs as a .csv.
Because the script updates Airtable after sending a match email, and an earlier set only pulls in records that have not had a match email sent, if a user sends emails and then clicks “Run Matches” again, it will not re-send to those users again, only any new sign ups will show up.
Next Steps
The script as is addresses some edge cases, but it could use more robust checks for if the number of “newbie” and “scoobie” conversations are balanced, and how to handle unevenness.
I plan to come back to consider the interest matching. So far I’ve thought of two approaches, both of which would involve creating some sort of match score for every possible pairing, ordering on that score, and then keeping track of the number of conversations each participant requested while creating the matches. I could try fuzzy string matching on the interests
field, and create a sameness score for each pair. Or I could create a weighted score based on how infrequently a certain issue is named by participants and matching the ‘harder’ to match issues first.