I mentioned previously that personal software is going to become a thing. I lift weights 6 days a week, manually keeping track of my sets, first by typing them up on my phone at the gym and later transferring them to a notes file. Let's improve that process.
I've used Matt Pocock's /grill-me skill before, but decided to lean in with the fuller suite of skills this time, generating tickets out of the grilling session and following through on the implementation.
Tedious manual input of workouts
I've tracked my workouts consistently for a little over two years. Instead of a notes app I would text myself the workout and later manually transfer it to a workout file on my computer:
# Sample format
Leg Press - 220/235 - 15.15.13.13.13There are plenty of footguns here: typos, occasional format drift, and a multi-week backlog of workouts stacking up that becomes tedious to catch up on. Sure, there are notes apps that could sync from phone to computer, but I've built my own wiki-like note system in Neovim to manage nearly everything in my personal and professional life. This system has proven much more useful to me than juggling multiple apps and accounts.
The other problem is actually learning anything from the data. It's easy to grep for things in Vim:
:g/bench press/pwill print each line containing bench press. In a file that has grown as large as mine this stops being that useful. And if I want to analyze this data or graph it, I'd have to transform it anyway.
So let's build something.
Good ol' email
The main decision was how to link the phone to the data storage. I run everything off my computer so I wasn't interested in using the phone for storing everything. The phone just records the info. The computer will probably be asleep while I'm at the gym so I ruled out running a server. (Sure, I could have an app use some SQS or equivalent cloud-based queuing system but that seemed like overkill.)
Email was an easy choice. It'll be well-supported in whatever device/stack I choose and works async: I can send the email and process it at any later time. Multiple emails can queue up and be processed without it causing issues.
The hardest part of the project was waiting for Google. You can start using a new Gmail account right away, but creating an app password required waiting 7 days, excruciating when the actual "work" of the project only took an hour or two.
A consistent grammar for the workout sets
This sample workout isn't the most concise but it's easy to parse:
week: 3
routine: Arms A
BB Incline Press: 120x15, 130x12, 140x10
DB Hammer Curl: 30x15, 30x15, 30x10
Plank: 60, 50, 40
Jog: 3.1m in 30:00There are a few special cases: jogging tracks distance and time (as shown above), a single number per set like Plank: 60 means 60 seconds, and Pullup: 5, 4, 2, 2 implies bodyweight when no weight is specified. Specifying week 3 is optional: it just tracks where I am in my workout cycle, since I change my rep counts and rest times every two weeks to keep things fresh.
Processing the workouts and storing the data
Nothing too complicated here, just typescript and sqlite. Fetch the email, parse it, and store it in the database. Can't say I've really looked at the code besides determining the database schema ahead of time and spot-checking how tests were structured during implementation.
I landed on some simple CLI commands that support doing a dry-run by default and then committing after confirmation.
bun run src/cli.ts ingest # dry run: fetch, parse, print
bun run src/cli.ts ingest --commit # write to SQLite, archive the emailsCommitting archives the email so it won't be processed again. Note that the email still exists so technically, by choosing email, I have a log of every message sent through the system stored in the cloud forever.
Backfilling two years of workouts
I had 528 sessions to backfill into the system. This mostly involved making sure all the data was consistent, and then writing a script to convert entries from the file into the database in batches. Turns out there were a lot of inconsistencies over 2 years of recording my workouts, but 10 minutes of regexes and macros in Neovim took care of that.
I did this while waiting for the Gmail account verification period to pass. It was nice to see real data in the database (which prompted me to test out this vim-based SQL client).
What's next?
It felt great to start using this at the gym knowing that the workout was already queued up to be saved. Running the ingest command on my computer takes a few seconds once a week (and I can always automate it later), which beats copying texts by hand.
In the meantime, every day I used it, I noticed some small UX tweak I wanted to make: things that previously wouldn't have been worth the effort for software I alone use. Now it just takes typing (or speaking) a sentence or two. Let's tackle those next time!