# PyCon US 2019 Books Giveaway 🎁

Last updated: May 5th, 2019

This code aims to find winners for the books giveway that RMOTR is running at PyCon US 2019 in Cleveland. To participate follow instructions described in this blog post, or find the RMOTR folks around the conference and in the Startup Row Booth on Friday, May 3rd.

We're looking for participants that tweeted with the #BooksGiveawayRMOTR hashtag. Using the Twitter API that should be fairly easy. Let's take a look at the code

In [1]:
import os

In [2]:
client = twitter.Api(
consumer_key=os.environ['CONSUMER_KEY'],
consumer_secret=os.environ['CONSUMER_SECRET'],
access_token_key=os.environ['ACCESS_TOKEN_KEY'],
access_token_secret=os.environ['ACCESS_TOKEN_SECRET'],
)

In [3]:
os.environ['CONSUMER_KEY']

Out[3]:
'hyo6w5ew77IVnk844ECSJCAgw'
In [9]:
participants = {}
processed_tweets = 0
term = '#BooksGiveawayRMOTR'

# initialize max_tweet_id as a very big number
max_tweet_id = 1_000 ** 1_000

# iterate through all tweet pages containing the term
while True:
tweets_qs = client.GetSearch(
term=term,
count=100,  # max allowed
max_id=max_tweet_id,
)

# don't count retweets or replies containing the term
new_tweets = [t for t in tweets_qs
if not t.retweeted_status and not t.in_reply_to_status_id]

for tweet in new_tweets:
user_id = tweet.user.id
participants.setdefault(user_id, {
'retweets': 0,
'likes': 0,
'tweets': 0
})
participants[user_id]['retweets'] += tweet.retweet_count
participants[user_id]['likes'] += tweet.favorite_count
participants[user_id]['tweets'] += 1
processed_tweets += 1

lowest_id = min([t.id for t in tweets_qs])
max_tweet_id = lowest_id - 1

if len(tweets_qs) != 100:
# we've reached the last page
break

In [10]:
len(participants)

Out[10]:
35
In [11]:
participants[list(participants)[0]]

Out[11]:
{'username': 'thesomisetty', 'retweets': 0, 'likes': 0, 'tweets': 1}

Alright! We now have our participants and the number of intereactions (retweets, likes) for their tweets.

## Calculate the weighted selection¶

As we said, we want to give people with the most interactions more chances of winning. For that we've came up with this weighted selection formula:

$$\large score = 3R + L + T$$

Where R is the amount of retweets the participant's tweets had (sum of all retweets in all tweets containing the term), and L is the amount of likes it had. A retweet weights three times as much as a like does. The T at the end stands for amount of tweets containing the term. That means, if you tweet more than once with the same hashtag you will have more chances of winning even if you don't get any interaction on them.

In [12]:
for user_id, user_info in participants.items():
user_info['score'] = 3 * user_info['retweets'] + user_info['likes'] + user_info['tweets']

In [13]:
participants[list(participants)[0]]

Out[13]:
{'username': 'thesomisetty',
'retweets': 0,
'likes': 0,
'tweets': 1,
'score': 1}

The last step is to do the actual selection. As we computed the score for each participant, we now know how much weight the user should have in the selection. To simulate that, we can simply pick a list and append the user's username as many times as the score tells us. If we then do a random.choice() from that list, users that were appended more times to the list will have more chances of winning.

In [14]:
selection_bag = []

for user_id, user_info in participants.items():
for ticket in range(user_info['score']):

In [15]:
len(selection_bag)

Out[15]:
232

_Note: For crazy big amount of participants or interactions this method might end up with a huge selection_bag list. But, for the purpose of this game it does its job.

In [17]:
import random

In [21]:
winners = []

In [23]:
for i in range(10):
user = random.choice(selection_bag)
while user in winners:
user = random.choice(selection_bag)
winners.append(user)

winners

Out[23]:
['tacosdedatos',
'ErSanyamKhurana',
'johann2357',
'GabrielleRab',
'bilal414',
'DevPranav',
'aruntonic',
'iris9112',
'martinzugnoni',
'shcheklein']
In [24]:
print(winners)

['tacosdedatos', 'ErSanyamKhurana', 'johann2357', 'GabrielleRab', 'bilal414', 'DevPranav', 'aruntonic', 'iris9112', 'martinzugnoni', 'shcheklein']


👆this line will be executed on Friday, May 3rd at 5:30PM to know the real winners!