top of page
Abstract Blue Light
Writer's pictureStan Camp

AI Caddie

In my last foray in AI I created a small model that will predict what club you should used based on a few data points.   The way that I demonstrated this was a bit contrived because I wanted to learn a small bit about building a bot.   It turned out well, but now I want to use the AI Caddie Model in Python API in a more natural way. In this blog I will be using this model in context of a golf app using some tech that interests me. This app(mobile web page) will allow the user to build a golf course map and track your score as you play. The app integrates with google maps and allows the user to select the tee and hole locations. It will make suggestions/predictions from the model to tell you what club to use and finally track the ending location of each shot. Once a few of rounds are played, this data can be used to retrain the model and improve the prediction.  I may cover the retraining in a future article.  Included in this post I will:

  • Create an Azure Postgres database to store some of the pertinent data collected in the UI

  • Create a Python API, which uses the database and model, containerize it, and run it in Azure as a container app

  • Create an Angular 17 app and deploy it to Azure as a static web app

 

Why Azure?  I've been in the AWS sphere for the last year or 3 and I wanted to get back into its workings and re-familiarize myself with its UI and navigation. When I continue with some new projects I may use the Azure CLI instead.


So let's dive straight into what I built to pull all these pieces together and to show you what I created.


Angular Caddie App:

I created a virtual caddie that could help you with many aspects of a round of golf. This is very much a work in progress, but when I arrived at a point in the development where I could illustrate how the model is used, it was time to show it. In this app you can setup your golf course as you play, and the next time you play the same course you should only have to select the tee you are using and start playing.


I won't go in depth into the user interface and code in this app, but  will give some key points and perhaps a screen shot or two.  I've written this as an Angular 17 app, which uses google maps to allow the user to setup the tee and hole locations on each hole as you play.  


Here is a screen shot from my phone where I was setting up a hole, the "T" pin is the tee, and "H" pin is the hole. Additionally you can see that the top bar contains how many strokes over or under par you are, plus the total strokes played in this round of golf. Directly below that is the distance from the current location to the hole, in this case it is the distance from the tee all the way to the hole.

Notice the Tee pin and the H Pin
Setup a hole

Once you select the par and then click tee off, the next screen shows the options for the first shot, and the api/shot/next route is used to get the suggested club using the single shot model. Predictably on this par 5 the suggested club is a driver.  You may notice on each screen that I am displaying the latitude and longitude. I was having some problem getting the positions to adjust properly once I started walking from the tee to where the ball ended, and I wanted to see what data was being received from the location service. This is still an issue, but really not a big deal in the scope of this blog.  I think I should open a ticket somehow against this and make it HIGH priority.

The model suggests a club to use
Select a club

After the hit button is selected, the flow of the interface will allow the user to tell the app they have reached the ball, and the model will be called again(and on every shot) to make a prediction on the club. When a hole is completed, the user will need to select the next tee and hole locations and continue playing.


There are so many data points that could be added to this app to make it really useful for a golfer. Some of these may include:

  • Shot type modifiers - for instance, hook, slice, fade, draw,

  • Shot result modifiers - rough, sand, out of bounds, penalty stroke, missed left, missed right

  • Shot style modifiers - Full swing, punch, chip, duff, thin, bladed, there are lots of ways to describe a bad shot in golf. Since a good shot is fairly rare for me, I would use these quite often.

  • Notes - allow notes to be kept on each hole, for instance, keep tee shot left, or you can miss to the right of the green and be safe, and so on.


With the data above you could track trends of your game. For instance that your miss with the Driver is generally right, or that your half swings are often not hit well enough to reach the green (that's me!)

Now that the app is semi-usable, I began deploying it to Azure. The general setup in Azure when creating the static web app was very easy, but I did encounter a problem with the build. Here are the details. Azure requires that you house the code in a compatible repository and build it to deploy it. I used Github, but in the build I ran into problems with the versions of Node that Azure expected versus what my Angular app wanted.  I dug around to find a solution that worked and was easy to use.  In the process of creating the static web app, Azure will create a yml file that describes the build.  I needed to modify that yml to specify the node version. The error and the solution are below. 

Here is the biggest problem encountered: 

Node version is not high enough
Azure Build problem

Azure currently supplies v18.12, but my code wanted version 20.9, so the solution was to add to the yml to tell it to use v20.9.  So in the build_and_deploy_job section I added this to force v20.9

Azure Build solution
Azure Build solution

After that the deploys were fairly smooth.

 

PostgreSQL Database:

There is not a lot to talk about in this section, but I will mention how I created the Azure DB and some information on what I decided to try to store regarding playing a round of golf. I used the Azure portal to create the database, which consisted of creating a resource group and then I created an Azure PostgresSQL database.  This does cost a little money, but its not much. Of Import regarding the cost is that I selected a Development workload type.  Here is a screen shot: 


Details of Azure db setup
Setup PostgreSQL Azure database

 


After you enter the server name and region you will also need to enter an admin name and password. Finally you will need to enter some rules on accessing the db, whether by domain, IP or whatever you need. And… there you have a new PostgreSQL instance. I connected to it from my local machine to get the db setup as I wanted.

 

I created a simple set of tables including:

  • Player - details on a player

  • Club - available clubs

  • Course - a few course details

  • Hole - contains details on a given hole of the course, of note is the latitude and longitude of the center of the green.

  • Tee - the tee being played in the current round of golf, contains latitude and longitude for a tee.

  • Round - collection of all the shots played in a round of golf, contains club selection, latitude and longitude of each shot start and end.

 

As I said this is rather simple, the most complex thing of note is that I include some referential integrity between tables to try to keep the data clean and consistent.

 

Python API:

Since I have been learning Python, I just decided to continue along this path for the API, and in all honesty I knew it would make integration with the Python joblib easier. I did some investigating of Python APIs and a Flask application seemed very easy to use and deploy so I went that direction. There are many other options, and if I decide to try to make something more production ready I will either choose a new way to house the code or at least wrap Gunicorn around this to handle advanced request processing.

 

In the API, I used SQLAlchemy as my ORM to read and update the data to/from my PostgreSQL db. I configured the Flask app to communicate with the deployed PostgreSQL instance via a connection string.  Here is an example of the player model:


As I have many models with data that needs to be read or updated, I needed to create routes for GET/PUT/POST requests. In a Flask application these routes are added to a blueprint.  In the code below for the player API I created a blueprint for my routes and then in the application startup these blueprints needed to be registered so the requests are routed properly. The PUT and GET below use the above SQLAlchemy model to get the data work done.

 

Player PUT:


Player GET:


 

This GET method will either get by a single id, or filter results to return a list of matching players.

 

Since this blog is a follow-on to the prior one about building a model, here is the API method that is used to get a prediction from the model. I've argued with myself over a good route and for now I've settled on this, but I can think of a few other good paths to use. This takes the playerId and the distance to the hole to make a suggestion on the next club to use.  The playerId is used to read the player record from the database and get the gender and handicap to pass to the model.


This API is deployed as an Azure Container app, which I did from the Azure plugin for VS Code.  I created a dockerfile for this and a docker-compose.yaml, but this is so small that a dockerfile alone would have been adequate.  I wanted to work with both files to make sure I understood how each was working.  After I had the files setup as I wanted, I ran the container locally and tested the API with Hoppscotch.  Hoppscotch is very similar to Postman but is open source and completely free.  I found it really easy to use, but I've read that it does not have some of the really advanced features of Postman, but for my purposes it worked great!  Here is the dockerfile used for the container.

 

Specifies Python and the requirements
Docker for container

 

After deploying the container to Azure I once again tested my APIs with Hoppscotch to prove everything was working.

  

Summary:

I really enjoyed building this and tying everything together, and I will likely continue to work on this as I can use this to improve my game.  After I finish a round, there is also a summary screen where all kinds of data could be displayed.  It currently has the score by hole, but I could do some sort of distance walked, average distance per club, score analysis on average per par 3, 4 or 5, and others. Now I just need it to stop snowing, so I can get out and actually play some golf, collect the data, and retrain the model.

 

I hope some of this has shown you something you did not know, or perhaps something I could have done differently. If there is something I should have done differently, shoot me an email or drop a comment.

 

In review, I have:

  • Shown how to deploy a Azure PostgreSQL database

  • Built Python Flask API, which will read and update the database

  • Deployed the Python app to Azure in a container app

  • Integrated the above API and data into a UI that uses the API and data in a useful way

 

If you have any questions on this or other topics, please feel free to contact me:  stan@myndcorepartners.com

 

About the author:

 

I am a Solution Architect with 20+ years of experience designing, coding, testing, and implementing large scale financial services applications. Over my career I've concentrated on Microsoft core development tools, but over the last few years have also enjoyed working with AWS, Go, and Postgres.

32 views0 comments

Recent Posts

See All

コメント


bottom of page