A five-file pipeline instead of opening an app

A five-file pipeline instead of opening an app

The Garmin Golf app is fine. I just did not want to open it, tap through to history, and squint at a scorecard every time I wanted to see how a round went. So instead of using the app more, I wrote a script that pulls my scores out of it automatically and drops them into an Excel file I already know how to use. Five small Python files, one macOS Launch Agent, done. The point of this post is not the golf, it is that the same shape solves this for almost any hobby data you're tempted to check inside someone else's app.

The part that actually took thought was not the API call

Fetching data from Garmin Connect is the easy 20%. The other 80% was: what happens the second time it runs. A sync script you can only safely run once is not a script, it is a one-off you'll be scared to touch again. Every scorecard has a stable ID, so re-running the sync just checks which IDs are already in the file and skips them. Running it ten times in a row produces the exact same file as running it once. That property, not the API integration, is what actually makes daily automation trustworthy enough to leave alone.

Real data is messier than the demo case

The first version assumed every round was 18 holes played in order. Real rounds include practice nine-holers, rounds abandoned partway through, and the odd scorecard that starts on hole 10. Silently treating those the same as a full round would have quietly corrupted the averages the whole file exists to show. The fix was small: flag anything that is not a clean 18-hole round instead of guessing, and decide by hand what to do with it. Boring, and exactly the kind of boring that automation projects skip until the data actually breaks something.

Old data and new data have different trust levels

Rounds from before the automation existed had to be added by hand, and hand-entered data is not the same as API data even when the numbers look identical: it can have typos, misremembered dates, wrong course names. Rather than merge the two silently into one trusted pile, they live in separate sheets. If a number ever looks wrong, knowing whether it came from the API or from memory tells you where to start looking, instead of re-checking everything.

None of this needed to be sophisticated. It needed a stable identifier to dedupe against, an explicit decision about the shape of "unusual" input instead of an assumption, and a boundary between data sources with different trust levels. That short list solves this exact problem for a fitness tracker, a reading log, or anything else currently trapped inside an app you open too often for what it actually gives you.