Olympic Script

In 2012, I participated in an office pool for the 2012 Olympics. The pool was simple, we picked countries in a round robin format. The top 5 countries by medal score (explained below) from the 2008 Olympics were excluded for balance purposes. The person with the highest medal score won. Medal score was calculated by counting a bronze medal at 1 point, a sliver as 2 points, and a gold as 3 points. Tallying up the points by hand was tedious, each person was earning medals from 20 countries! But the London Olympics had a website which updated daily with the medal counts.

I created a simple script in Python which took the webpage, divided the countries out by who had picked them, and then totaled the medal score by country and by person. It's ugly and has several bugs (for example, I never got it to successfully download the page by itself, I always had to save the file to my desktop and point the script at it. I also never cleaned the formatting up beyond a simple, 1980's style output. It looked something like this:

Player: Jonathan, 80
Player: Tom, 76
....
....

It was ugly, but it got the job done and it only took me 20 minutes one morning to write. The code is appended below in it's entirety, bugs and all. There are notes to explain interesting bits. The HTML processing libraries that I used (Beautiful Soup 4 and Mako) were current then, but I haven't kept up on them so I don't know if I would select the same tools today. I would, however, definitely still use JSON. It is, by far, the most human-friendly means of storing structured data in text.

from bs4 import BeautifulSoup
from mako.template import Template
import json
 
class Player():
    def __init__(self, name="", countries = [], dct={}): #This and the following if block
        if '__Player__' in dct: #are used to read a saved Player
            self.name = dct["name"] #That meant I didn't have to
            self.country_names = dct["countries"] #re-enter all 205 countries each morning.
        else:
            self.name = name
            self.country_names = countries
        self.country_objects = []
 
    def test_country(self, country): #All functions starting with test exist to
        if country in self.country_names: #make sure I haven't made a dumb mistake.
            print self.country_names
            return True
        else:
            print self.country_names
            return False

    def add_country(self, country):
        """I made country names and country objects different. That was for storage purposes. I only wanted to store a list of
names in the JSON file. This code takes that list of names and adds a corresponding list of objects which can do all the things
I wanted countries to be able to do."""
        if country.name in self.country_names:
            if country in self.country_objects:
                print "%s is added to %s's country lists." % (country.name, self.name) #Print statements for debugging
                return True
            else:
                self.country_objects.append(country)
                print "%s is added to %s's country lists." % (country.name, self.name)
                return True
        else:
            self.country_names.append(country.name)
            self.country_objects.append(country)
            print self.country_names
            return True
 
    def points(self):
        """Making countries objects let me use this slick 1-liner to calculate each player's medal score."""
        return sum(country.points() for country in self.country_objects)
 
    def save(self):
        print self.country_names #For debugging
        return json.dumps({"__Player__":"", "name":self.name,
                           "countries":self.country_names})
 
    def __repr__(self):
        return "%s, %s" % (self.name, self.points())
 
 
class PlayerList():
    def __init__(self, players=[]):
        self.players = players
##        output = Template("""<ol>
##% for player in ${player_list}:
##    <li>${player}</li>
##% endfor
##</ol>""") #I tried to make a pretty, HTML version. Probably could have, but it was too finicky.
 
    def sort(self): #Returns the list of players, sorted from high score to low score.
        return sorted(self.players, key=lambda players: players.points(),
                      reverse=True)
 
    def add_player(self, player):
        self.players.append(player)
 
    def autoassign_country(self, country): #Works with the JSON to assign countries automatically.
        for player in self.players:
            if player.test_country(country.name) is True:
                print player.country_names
                player.add_country(country)
                return player.name
        return "No Player"
 
    def manualassign_country(self, player, country): #Used to add countries the first time they actually earned a medal
        ret = self.players[player].add_country(country)
        if ret is True:
            return self.players[player]
        return "Player Not Found"
 
    def save(self):
        with open("data.json", 'w') as f: #Creates the JSON file and saves it.
            for player in self.players:
                f.write(player.save())
                f.write("\n")
 
    def __repr__(self):
##        return output.render(player_list = self.sort())
        return self.sort()
 
class Country():
    def __init__(self, name, gold, silver, bronze):
        self.name = name
        self.gold = gold
        self.silver = silver
        self.bronze = bronze
 
    def points(self):
        return (self.gold * 3) + (self.silver * 2) + (self.bronze * 1)
 
    def __repr__(self):
        return self.name
 
#This was a different approach to the JSON Data storage than I ended up using, I left it here to snatch code from as needed.
def create_players(player_list, json=''):
    if json is True:
        #Do json stuff
        pass
    else:
        excluded = Player("Excluded", [])
        player_list.add_player(excluded)
        doug = Player("Doug", [])
        player_list.add_player(doug)
        ted = Player("Ted", [])
        player_list.add_player(ted)
        adam = Player("Adam", [])
        player_list.add_player(adam)
        trish = Player("Trish", [])
        player_list.add_player(trish)
        tyler = Player("Tyler", [])
        player_list.add_player(tyler)
        jon = Player("Jon", [])
        player_list.add_player(jon)
        angie = Player("Angie", [])
        player_list.add_player(angie)
        tom = Player("Tom", [])
        player_list.add_player(tom)
        andy = Player("Andy", [])
        player_list.add_player(andy)
        stacey = Player("Stacey", [])
        player_list.add_player(stacey)
 
 
def main():
    soup = BeautifulSoup(open("Medal Count - Olympic Medal Standings - Official Results   London 2012.htm").read())
    tbody = soup.table.tbody
    rows = tbody.contents
 
    player_list = PlayerList([]) #Create the players list
    create_players(player_list)
 
    countries = [] #Create an empty list for unprocessed countries
 
    for row in rows: #Create Country objects for every country with a medal
        name = row.find('span', 'countryName').string
        gold = int(row.find('dd', 'gold').string)
        silver = int(row.find('dd', 'silver').string)
        bronze = int(row.find('dd', 'bronze').string)
        countries.append(Country(name, gold, silver, bronze))
 
    for country in countries: #Try to assign each country to a player.
        ret = player_list.autoassign_country(country)
        if ret != "No Player":
            print "%s assigned to %s." % (country, ret)
        else:
            print "%s is unassigned, whose is it?" % (country,)
            print player_list.players
            p = input("Enter player's list index or -1 to exit (base zero list):")
            if p == -1:
                print "Exiting"
                player_list.save()
                import sys
                sys.exit()
            ret = player_list.manualassign_country(p, country)
            if ret != "Player Not found":
                print "%s assigned to %s." % (country, ret)
            else:
                print "Error, check your entry and try again later"
 
    player_list.save()
    import sys
    sys.exit()
 
if __name__ == "__main__":
    main()
Comments