Categories
Python

Writing a Raffle Script

Due to the COVID Pandemic, many things are … different. One thing that needed to be different this year was the way that students at my daughters middle school got to spend their ‘Hero Points’.

Hero Points are points earned for good behavior. In a typical year the students would get to spend them at the student store, but with all of the closures, this wasn’t possible. For the students in my daughter’s 8th grade this was a big deal as they’re going on to High School next year, so we can just roll them over to next year!

Instead of having the kids ‘spend’ their Hero Points the PTO offered up the solution of a raffle based on the number of Hero Points they had. But they weren’t sure how to do it.

I jumped at the chance to write something like this up (especially after all of my works on the PyBites CodeChalleng.es platform) and so my wife volunteered me 😁

In order to really get my head wrapped around the problem, I wanted to treat my solution like a real world analog. For example, in a real work raffle, when you get your tickets, there are two tickets with the same number. One that you get to hold onto, and one that goes into a bowl (or other vessel) that is randomly drawn from.

How many tickets?

Each student had some number of Hero Points. The PTO decided that 10 Hero Points would equal 1 Raffle ticket. Further, it was decided that we would ALWAYS round up. This means that 1 Hero Point would equal 1 Raffle Ticket, but that 9 Hero Points would also equal 1 Raffle Ticket.

Create tickets

I decided to use a namedtuple to store the Raffle Tickets. Specifically, I store the student name, ticket numbers they drew, and the number of tickets they have

Raffle_Tickets = namedtuple('Raffle_Tickets', ['name', 'ticket_numbers', 'tickets'])

The list of student names and total Hero Points was stored in an Excel File (.xlsx) so I decided to use the Pandas Package to import it and manipulate it into a dataframe. The structure of the excel file is: Student Name, Grade, Available Points.

df = pd.read_excel (r'/Users/ryan/Documents/python-files/8th  Hero Points.xlsx')

After a bit of review it turned out that there were a couple of students with NEGATIVE Hero Points. I’m not really sure how that happened, but I was not properly accounting for that originally, so I had to update my dataframe.

The code below filters the dataframe to only return students with positive ‘Available Points’ and then reindex. Finally, it calculates the number of Raffle tickets by dividing by 10 and rounding up using Python’s ceil function. It puts all of this into a list called tickets. We append our tickets list to the original dataframe.

df = df[df['Available Points'] >0]
df.reset_index(inplace=True, drop=True)
tickets = []
for i in df['Available Points'] / 10:
    tickets.append(ceil(i))
df['Tickets'] = tickets

Our dataframe now looks like this: Student Name, Grade, Available Points, Tickets.

Next, we need to figure out the Raffle ticket numbers. To do that I count the total number of Tickets available. I’m also using some extra features of the range function which allows me to set the start number of the Raffle.1

total_number_of_tickets = sum(df['Tickets'])
ticket_number_start = 1000000
ticket_number_list = []
for i in range(ticket_number_start, ticket_number_start+total_number_of_tickets):
    ticket_number_list.append(i)

Once we have the list of ticket numbers I want to make a copy of it … remember there are two tickets, one that goes in the bowl and one that the student ‘gets’. Extending the metaphor of having two different, but related, tickets, I decided to use the deepcopy function on the ticket_number_list to create a list called assigned_ticket_number_list.

For more on deepcopy versus (shallow) copy see the documentation

assigned_ticket_number_list = deepcopy(ticket_number_list)

Finally, I reindex the dataframe just to add a bit more randomness to the list

df = df.reindex(np.random.permutation(df.index))

Assign Tickets

Next we’ll assign the tickets randomly to the students.

raffle_list = []
for student in range(df.shape[0]):
    student_ticket_list = []
    for i in range(df.loc[student].Tickets):
        assigned_ticket_number = randint(0, len(assigned_ticket_number_list)-1)
        student_ticket_list.append(assigned_ticket_number_list[assigned_ticket_number])
        assigned_ticket_number_list.pop(assigned_ticket_number)
    raffle_list.append(Raffle_Tickets(df.loc[student].Name, student_ticket_list, len(student_ticket_list)))

OK … the code above looks pretty dense, but basically all we’re doing is looping through the students to determine the number of tickets they each have. Once we have that we loop through the available ticket numbers and randomly assign it to the student. At the end we add a namedtuple object called Raffle_Tickets that we defined above to the raffle_list to store the student’s name, their ticket numbers, and the number of tickets that they received.

Draw Tickets

Now we want to ‘draw’ the tickets from the ‘bowl’. We want to select 25 winners, but we also don’t want to have any student win more than once. Honestly, the ’25 winning tickets with 25 distinct winners’ was the hardest part to get through.

selected_tickets = []
for i in range(25):
    selected_ticket_number_index = randint(0, len(ticket_number_list) - 1)
    selected_ticket_number = ticket_number_list[selected_ticket_number_index]
    for r in raffle_list:
        if selected_ticket_number in r.ticket_numbers:
            ticket_number_list = [x for x in ticket_number_list if x not in r.ticket_numbers]
    selected_tickets.append(selected_ticket_number)

We see above that we’ll select 25 items from the ‘bowl’ of tickets. We select the tickets one at a time. For each ticket we determine what set of tickets that selected ticket is in. Once we know that, we then remove all tickets associated with that winning ticket so that we can guarantee 25 unique winners.

Find the Winners

We now have 25 tickets with 25 winners. Now we just need to get their names!

winners_list=[]
for r in raffle_list:
    for t in r.ticket_numbers:
        student_winning_list = []
        if t in selected_tickets:
            student_winning_list.append(t)
            winners_list.append((Raffle_Tickets(r.name, student_winning_list, len(student_winning_list))))

Again, we construct a list of namedtuple Raffle\_Tickets only this time it’s just the winners.

Output winners

Whew! Now that we have the results we want to write them to a file.

with open('/Users/ryan/PyBites/Raffle/winners_new.txt', 'w+') as f:
    for winner in winners_list:
        tickets = ticket_count(winner.name)
        percent_chance_of_winning = tickets / total_number_of_tickets * 100
        percent_chance_of_winning_string = "{:.2f}".format(percent_chance_of_winning)
        f.write(f'{winner.name} with winning ticket {winner.ticket_numbers[0]}. They had {tickets} tickets and a {percent_chance_of_winning_string}% chance of winning.\n')

One of the reasons that I stored the number of tickets above was so that we could see what the chance was of a student winning given the number of tickets they started with.

For each student we output to a line to a file with the student’s name, the winning tickets number, the number of tickets they started with and their chance of winning (the ratio of tickets the student had to the total number of starting tickets)

Conclusion

This was a fun project for me because it was needed for a real world application, allowed me to use MANY of the concepts I learned at PyBites CodeChalleng.es AND helped my daughter’s school.

  1. Why am I doing this, versus just stating a 0? Mostly because I wanted the Raffle Ticket numbers to look like real Raffle Ticket Numbers. How many times have you seen a raffle ticket with number 0 on it?
Categories
PyCharm Python

Issues with psycopg2 … again

In a previous post I had written about an issue I’d had with upgrading, installing, or just generally maintaining the python package psycopg2 (link).

I ran into that issue again today, and thought to myself, “Hey, I’ve had this problem before AND wrote something up about it. Let me go see what I did last time.”

I searched my site for psycopg2 and tried the solution, but I got the same forking error.

OK … let’s turn to the experts on the internet.

After a while I came across this article on StackOverflow but this specific answer helped get me up and running.

A side effect of all of this is that I upgraded from Python 3.7.5 to Python 3.8.1. I also updated all of my brew packages, and basically did a lot of cleaning up that I had neglected.

Not how I expected to spend my morning, but productive nonetheless.

Categories
Django Python

My First Django Project

I’ve been writing code for about 15 years (on and off) and Python for about 4 or 5 years. With Python it’s mostly small scripts and such. I’ve never considered myself a ‘real programmer’ (Python or otherwise).

About a year ago, I decided to change that (for Python at the very least) when I set out to do 100 Days Of Web in Python from Talk Python To Me. Part of that course were two sections taught by Bob regarding Django. I had tried learn Flask before and found it … overwhelming to say the least.

Sure, you could get a ‘hello world’ app in 5 lines of code, but then what? If you wanted to do just about anything it required ‘something’ else.

I had tried Django before, but wasn’t able to get over the ‘hump’ of deploying. Watching the Django section in the course made it just click for me. Finally, a tool to help me make AND deploy something! But what?

The Django App I wanted to create

A small project I had done previously was to write a short script for my Raspberry Pi to tell me when LA Dodger (Baseball) games were on (it also has beloved Dodger Announcer Vin Scully say his catch phrase, “It’s time for Dodger baseball!!!”).

I love the Dodgers. But I also love baseball. I love baseball so much I have on my bucket list a trip to visit all 30 MLB stadia. Given my love of baseball, and my new found fondness of Django, I thought I could write something to keep track of visited stadia. I mean, how hard could it really be?

What does it do?

My Django Site uses the MLB API to search for games and allows a user to indicate a game seen in person. This allows them to track which stadia you’ve been to. My site is composed of 4 apps:

  • Users
  • Content
  • API
  • Stadium Tracker

The API is written using Django Rest Framework (DRF) and is super simple to implement. It’s also really easy to changes to your models if you need to.

The Users app was inspired by Will S Vincent ( a member of the Django Software Foundation, author, and podcaster). He (and others) recommend creating a custom user model to more easily extend the User model later on. Almost all of what’s in my Users App is directly taken from his recommendations.

The Content App was created to allow me to update the home page, and about page (and any other content based page) using the database instead of updating html in a template.

The last App, and the reason for the site itself, is the Stadium Tracker! I created a search tool that allows a user to find a game on a specific day between two teams. Once found, the user can add that game to ‘Games Seen’. This will then update the list of games seen for that user AND mark the location of the game as a stadium visited. The best part is that because the game is from the MLB API I can do some interesting things:

  1. I can get the actual stadium from visited which allows the user to indicate historic (i.e. retired) stadia
  2. I can get details of the game (final score, hits, runs, errors, stories from MLB, etc) and display them on a details page.

That’s great and all, but what does it look like?

The Search Tool

Stadia Listing

National League West

American League West

What’s next?

I had created a roadmap at one point and was able to get through some (but not all) of those items. Items left to do:

  • Get Test coverage to at least 80% across the app (currently sits at 70%)
  • Allow users to be based on social networks (right now I’m looking at Twitter, and Instagram) probably with the Django Allauth Package
  • Add ability to for minor league team search and stadium tracking (this is already part of the MLB API, I just never implemented it)
  • Allow user to search for range of dates for teams
  • Update the theme … it’s the default MUI CSS which is nice, but I’d rather it was something a little bit different
  • Convert Swagger implementation from django-rest-swagger to drf-yasg

Final Thoughts

Writing this app did several things for me.

First, it removed some of the tutorial paralysis that I felt. Until I wrote this I didn’t think I was a web programmer (and I still don’t really), and therefore had no business writing a web app.

Second, it taught me how to use git more effectively. This directly lead to me contributing to Django itself (in a very small way via updates to documentation). It also allowed me to feel comfortable enough to write my first post on this very blog.

Finally, it introduced me to the wonderful ecosystem around Django. There is so much to learn, but the great thing is that EVERYONE is learning something. There isn’t anyone that knows it all which makes it easier to ask questions! And helps me in feeling more confident to answer questions when asked.

The site is deployed on Heroku and can be seen here. The code for the site can be seen here.

This article was also posted on the PyBit.es Blog

Categories
Tools

Using Python to Check for File Changes in Excel

The Problem

Data exchange in healthcare is … harder than it needs to be. Not all partners in the healthcare arena understand and use technology to its fullest benefit.

Take for example several health plans which want data reported to them for CMS (Centers for Medicare and Medicaid Services) regulations. They will ask their ‘delegated’ groups to fill out an excel file. As in, they expect you will actually fill out an excel file, either by manually entering the data OR by potentially copying and pasting your data into their excel file.

They will also, quite frequently, change their mind on what they want AND the order in which they want the data to appear in their excel file. But there’s no change log to tell you what (if anything has changed). All that you will get is an email which states, “Here’s the new template to be used for report XYZ” … even if this ‘new’ report is the same as the last one that was sent.

Some solutions might be to use versioning software (like Git) but all they will do is tell you that there is a difference, not what the difference is. For example, when looking at a simple excel file added to git and using git diff you see:


diff --git a/Book3.xlsx b/Book3.xlsx
index 05a8b41..e96cdb5 100644
Binary files a/Book3.xlsx and b/Book3.xlsx differ

This has been a giant pain in the butt for a while, but with the recent shelter-in-place directives, I have a bit more time on the weekends to solve these kinds of problems.

The Solution

Why Python of Course!

Only two libraries are needed to make the comparison: (1) os, (2) pandas

The basic idea is to:

  1. Load the files
  2. use pandas to compare the files
  3. write out the differences, if they exist

Load the Files

The code below loads the necessary libraries, and then loads the excel files into 2 pandas dataframes. One thing that my team has to watch out for are tab names that have leading spaces that aren’t easy to see inside of excel. This can cause all sorts of nightmares from a troubleshooting perspective.

import os
import pandas as pd

file_original = os.path.join(\\path\\to\\original\\file, original_file.xlsx)
file_new = os.path.join(\\path\\to\\new\\file, new_file.xlsx)

sheet_name_original = name_of_sheet_in_original_file
sheet_name_new = name_of_sheet_in_new_file

df1 = pd.read_excel(file_original, sheet_name_original)
df2 = pd.read_excel(file_new, sheet_name_new)

Use Pandas to compare

This is just a one liner, but is super powerful. Pandas DataFrames have a method to see if two frames are the same. So easy!

data_frame_same = df1.equals(df2)

Write out the differences if they exist:

First we specify where we’re going to write out the differences to. We use w+ because we’ll be writing out to a file AND potentially appending, depending on differences that are found. The f.truncate(0) will clear out the file so that we get just the differences on this run. If we don’t do this then we’ll just append to the file over and over again … and that can get confusing.

f.open(\\path\\to\\file\\to\\write\\differences.txt, 'w+')
f.truncate(0)

Next, we check to see if there are any differences and if they are, we write a simple message to our text file from above:

if data_frame_same:
	f.write('No differences detected')

If differences are found, then we loop through the lines of the file, finding the differences and and writing them to our file:

else:
	f.write('*** WARNING *** Differences Found\n\n')
	for c in range(max(len(df1.columns), len(df2.columns))):
		try:
			header1 = df1.columns[c].strip().lower().replace('\n', '')
			header2 = df2.columns[c].strip().lower().replace('\n', '')
			if header1 == header2:
				f.write(f'Headers are the same: {header1}\n')
			else:
				f.write(f'Difference Found: {header1} -> {header2}\n')
		except:
			pass

f.close()

The code above finds the largest column header list (the file may have had a new column added) and uses a try/except to let us get the max of that to loop over.

Next, we check for differences between header1 and header2. If they are the same, we just write that out, if they aren’t, we indicate that header1 was transformed to header2

A sample of the output when the column headers have changed is below:

*** WARNING *** Differences Found

Headers are the same: beneficiary first name
...
Difference Found: person who made the request -> who made the request?
...

Future Enhancements

In just using it a couple of times I’ve already spotted a couple of spots for enhancements:

  1. Use input to allow the user to enter the names/locations of the files
  2. Read the tab names and allow user to select from command line

Conclusion

I’m looking forward to implementing the enhancements mentioned above to make this even more user friendly. In the mean time, it’ll get the job done and allow someone on my team to work on something more interesting then comparing excel files to try (and hopefully find) differences.

Categories
Django PyCharm Python

Mischief Managed

A few weeks back I decided to try and update my Python version with Homebrew. I had already been through an issue where the an update like this was going to cause an issue, but I also knew what the fix was.

With this knowledge in hand I happily performed the update. To my surprise, 2 things happened:

  1. The update seemed to have me go from Python 3.7.6 to 3.7.3
  2. When trying to reestablish my Virtual Environment two packages wouldn’t installed: psycopg2 and django-heroku

Now, the update/backdate isn’t the end of the world. Quite honestly, next weekend I’m going to just ditch homebrew and go with the standard download from Python.org because I’m hoping that this non-sense won’t be an issue anymore

The second issue was a bit more irritating though. I spent several hours trying to figure out what the problem was, only to find out, there wasn’t one really.

The ‘fix’ to the issue was to

  1. Open PyCharm
  2. Go to Setting
  3. Go to ‘Project Interpreter’
  4. Click the ‘+’ to add a package
  5. Look for the package that wouldn’t install
  6. Click ‘Install Package’
  7. Viola … mischief managed

The next time this happens I’m just buying a new computer

Categories
Django

Updating the models for my Django Rest Framework API

I’ve been working on a Django project which would allow users to track games that they’ve seen and, therefore, see what stadia they have visited.

This is all being done at a site i set up called StadiaTracker.com. Initially when constructing my model I kept it relatively simple. I had one model that had two fields. The two fields tied the User from my CustomUser Model to a Game ID that I retrieve from an API that MLB provides.

I thought this simple approach would be the best approach. In addition to having a Django App I set up a Django Rest Framework (DRF) API. My initial plan was to have a DRF backend with a Vue (or React) front end. (I still want to do that, but I really wanted to try and finish a project before proceeding down that path).

After some development and testing I quickly realized that the page loads for the app were suffering because of the number of API calls to MLB that were being made.

I created a new model to tie the user id (still from the CustomUser model I’d created) to the game id, but in addition I’d get and store the following information:

  • Home Team Name
  • Home Team Score
  • Home Team Hits
  • Home Team Errors
  • Away Team Name
  • Away Team Score
  • Away Team Hits
  • Away Team Errors
  • Game Recap Headline
  • Game Recap Summary
  • Game Date / Time

By storing all of this my views could render more quickly because they didn’t have to go to the MLB API to get the information.

Of course, once I did this I realized that the work I had done on the DRF API would also need to be updated.

Initially I kept putting off the refactoring that was going to have to be done. Finally, I just sat down and did it. And you know what, within 10 minutes I was done.

I only had to change 3 files:

  • serializers.py
  • urls.py
  • views.py

For the searializers.py and views.py all I had to do was add the new model and then copy and paste what I had done for the previous model.

For the urls.py it was just a simple matter of updating the the DRF path and detail path to use the new views I had just created.

It was so amazingly simple I could barely believe it. This thing I had put off for a couple of weeks because I was afraid it was going to be really hard, just wasn’t.

Categories
100DaysOfCode Python

My Experience with the 100 Days of Web in Python

As soon as I discovered the Talk Python to me Podcast, I discovered the Talk Python to me courses. Through my job I have a basically free subscription to PluralSight so I wasn’t sure that I needed to pay for the courses when I was effectively getting courses in Python for free.

After taking a couple ( well, truth be told, all ) of the Python courses at PluralSight, I decided, what the heck, the courses at Talk Python looked interesting, Michael Kennedy has a good instructor’s voice and is genuinely excited about Python, and if it didn’t work out, it didn’t work out.

I’m so glad I did, and I’m so glad I went through the 100 Days of Web in Python course.

On May 2, 2019 I saw that the course had been released and I tweeted

This x 1000000! Thank you so much @TalkPython. I can’t wait to get started!

I started on the course on May 4, 2019 and completed it August 11, 2019. Full details on the course are here.

Of the 28 concepts that were reviewed over the course, my favorites things were learning Django and Django Rest Framework and Pelican. Holy crap, those parts were just so much fun for me. Part of my interest in Django and DRF comes from William S Vincent’s books and Podcast Django Chat, but having actual videos to watch to get me through some of the things that have been conceptually tougher for me was a godsend.

The other part that I really liked was actual deployment to a server. I had tried (about 16 months ago) to deploy a Django app to Digital Ocean and it was an unmitigated disaster. No static files no matter what I did. I eventually gave up.

In this course I really learned how to deploy to both Heroku and a Linux box on Digital Ocean, and so now I feel much more confident that the app I’m working on (more on that below) will actually see the light of day on something other than a dev machine!

The one thing that I started to build (and am continuing to work on) is an app with a DRF backend and a Vue.js front end that allows a user to track which Baseball stadia they’ve been to. So far I have an API set up via DRF (hosted at Heroku) and sketches of what to do in Vue.js. There’s also a Django front end (but it’s not the solution I really want to use).

Writing code for 100 days is hard. Like really hard. For nearly 20 of those days I was on a family vacation in the Mid Western part of the US, but I made time for both the coding, and my family. My family was super supportive of my goal which was helpful, but the content in the course was really interesting and challenging and made me want to do it every day, which was also super helpful.

On day 85 I got a video from Bob that helped get me through the last 2 weeks. It was encouraging, and helpful which is just what I needed. So thank you Bob.

At the end I also got a nice congratulatory video from Julian, which was surprising to say the least, especially because he called out some of the things that I tweeted that I enjoyed about the class, addressed me by name, and just genuinely made me feel good about my accomplishment!

OK. I just wrapped up the 100 Days of Code with Python and the web. Now what?

I took a week off to recuperate and am now ready to ‘get back to it’.

After all, I’ve got baseball stadia to track in my app!

Talk Python to me Podcast

Why I like the Talk Python Podcast

When I started listening to it

Listening to the back catalog (nearly all of it)

Categories
Python

Keeping Python up to date on macOS

Sometimes the internet is a horrible, awful, ugly thing. And then other times, it’s exactly what you need.

I have 2 Raspberry Pi each with different versions of Python. One running python 3.4.2 and the other running Python 3.5.3. I have previously tried to upgrade the version of the Pi running 3.5.3 to a more recent version (in this case 3.6.1) and read 10s of articles on how to do it. It did not go well. Parts seemed to have worked, while others didn’t. I have 3.6.1 installed, but in order to run it I have to issue the command python3.6 which is fine but not really what I was looking for.

For whatever reason, although I do nearly all of my Python development on my Mac, it hadn’t occurred to me to upgrade Python there until last night.

With a simple Google search the first result came to Stackoverflow (what else?) and this answer.

brew update
brew upgrade python3

Sometimes things on a Mac do ‘just work’. This was one of those times.

I’m now running Python 3.7.1 and I’ll I needed to do was a simple command in the terminal.

God bless the internet.

Categories
Programming

Fizz Buzz

I was listening to the most recent episode of ATP and John Siracusa mentioned a programmer test called fizz buzz that I hadn’t heard of before.

I decided that I’d give it a shot when I got home using Python and Bash, just to see if I could (I was sure I could, but you know, wanted to make sure).

Sure enough, with a bit of googling to remember some syntax fo Python, and learn some syntax for bash, I had two stupid little programs for fizz buzz.

Python

def main():

	my_number = input("Enter a number: ")
	
	if not my_number.isdigit():
		return
	else:
		my_number = int(my_number)
		if my_number%3 == 0 and my_number%15!=0:
			print("fizz")
		elif my_number%5 == 0 and my_number%15!=0:
			print("buzz")
		elif my_number%15 == 0:
			print("fizz buzz")		
		else:
			print(my_number)


if __name__ == '__main__':
    main()

Bash

#! /bin/bash

echo "Enter a Number: " 

read my_number

re='^[+-]?[0-9]+$'
if ! [[ $my_number =~ $re ]] ; then
   echo "error: Not a number" >&2; exit 1
fi

if ! ((my_number % 3)) && ((my_number % 15)); then
	echo "fizz"
elif ! ((my_number % 5)) && ((my_number % 15)); then
	echo "buzz"
elif ! ((my_number % 15)) ; then
	echo "fizz buzz"
else
	echo my_number
fi

And because if it isn’t in GitHub it didn’t happen, I committed it to my fizz-buzz repo.

I figure it might be kind of neat to write it in as many languages as I can, you know … for when I’m bored.

Categories
Hockey Raspberry Pi

ITFKH!!!

It’s time for Kings Hockey! A couple of years ago Emily and I I decided to be Hockey fans. This hasn’t really meant anything except that we picked a team (the Kings) and ‘rooted’ for them (i.e. talked sh*t* to our hockey friends), looked up their position in the standings, and basically said, “Umm … yeah, we’re hockey fans.”

When the 2018 baseball season ended, and with the lack of interest in the NFL (or the NBA) Emily and I decided to actually focus on the NHL. Step 1 in becoming a Kings fan is watching the games. To that end we got a subscription to NHL Center Ice and have committed to watching the games.

Step 2 is getting notified of when the games are on. To accomplish this I added the games to our family calendar, and decided to use what I learned writing my ITFDB program and write one for the Kings.

For the Dodgers I had to create a CSV file and read it’s contents. Fortunately, the NHL as a sweet API that I could use. This also gave me an opportunity to use an API for the first time!

The API is relatively straight forward and has some really good documentation so using it wasn’t too challenging.

import requests
from sense_hat import SenseHat
from datetime import datetime
import pytz
from dateutil.relativedelta import relativedelta



def main(team_id):
    sense = SenseHat()

    local_tz = pytz.timezone('America/Los_Angeles')
    utc_now = pytz.utc.localize(datetime.utcnow())
    now = utc_now.astimezone(local_tz)

    url = 'https://statsapi.web.nhl.com/api/v1/schedule?teamId={}'.format(team_id)
    r = requests.get(url)

    total_games = r.json().get('totalGames')

    for i in range(total_games):
        game_time = (r.json().get('dates')[i].get('games')[0].get('gameDate'))
        away_team = (r.json().get('dates')[i].get('games')[0].get('teams').get('away').get('team').get('name'))
        home_team = (r.json().get('dates')[i].get('games')[0].get('teams').get('home').get('team').get('name'))
        away_team_id = (r.json().get('dates')[i].get('games')[0].get('teams').get('away').get('team').get('id'))
        home_team_id = (r.json().get('dates')[i].get('games')[0].get('teams').get('home').get('team').get('id'))
        game_time = datetime.strptime(game_time, '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.utc).astimezone(local_tz)
        minute_diff = relativedelta(now, game_time).minutes
        hour_diff = relativedelta(now, game_time).hours
        day_diff = relativedelta(now, game_time).days
        month_diff = relativedelta(now, game_time).months
        game_time_hour = str(game_time.hour)
        game_time_minute = '0'+str(game_time.minute)
        game_time = game_time_hour+":"+game_time_minute[-2:]
        away_record = return_record(away_team_id)
        home_record = return_record(home_team_id)        
        if month_diff == 0 and day_diff == 0 and hour_diff == 0 and 0 >= minute_diff >= -10:
            if home_team_id == team_id:
                msg = 'The {} ({}) will be playing the {} ({}) at {}'.format(home_team, home_record, away_team, away_record ,game_time)
            else:
                msg = 'The {} ({}) will be playing at the {} ({}) at {}'.format(home_team, home_record, away_team, away_record ,game_time)
            sense.show_message(msg, scroll_speed=0.05)


def return_record(team_id):
    standings_url = 'https://statsapi.web.nhl.com/api/v1/teams/{}/stats'.format(team_id)
    r = requests.get(standings_url)
    wins = (r.json().get('stats')[0].get('splits')[0].get('stat').get('wins'))
    losses = (r.json().get('stats')[0].get('splits')[0].get('stat').get('losses'))
    otl = (r.json().get('stats')[0].get('splits')[0].get('stat').get('ot'))
    record = str(wins)+'-'+str(losses)+'-'+str(otl)
    return record


if __name__ == '__main__':
    main(26) # This is the code for the LA Kings; the ID can be found here: https://statsapi.web.nhl.com/api/v1/teams/

The part that was the most interesting for me was getting the opponent name and then the record for both the opponent and the Kings. Since this is live data it allows the records to be updated which I couldn’t do (easily) with the Dodgers programs (hey MLB … anytime you want to have a free API I’m ready!).

Anyway, it was super fun and on November 6 I had the opportunity to actually see it work:

I really like doing fun little projects like this.