Categories
Personal

Monthly Review: January 2022

At the beginning of the year, my only resolution was to review the past month and set some focuses, themes, and goals according to where my life was at that point. Hopefully as a way to try to avoid the pitfalls of all or nothing boolean resolutions. Consider this a trial into having a semi-public semi-regular monthly review.

The goal I set for January was to recover my fitness and get back to my pre-Christmas weight. Fortunately, as of this morning, not only am I sub-pre-Christmas, but my weight hit the goal I set back at the beginning of August when I started this journey. I have a couple of potential blog posts around the past 6 months and how I got to here – joining a running club, slow-carb-dieting – but they deserve some time and thought to themselves.

It’s not a direct goal or aim, but I need to figure out where to go next. I’ve never got to my goal weight before – when I’ve lost significant weight previously, it’s been until “that’ll do” and then it’s gradually (or sometimes quickly) piled back on again. So my focus needs to be, what about this eating program has really worked for me, and how do I adapt that to focusing on other things like fitness, strength, and not necessarily a calorie deficit? This is something I will think about over the next week, and in the meantime, I’m going to keep “slow-carbing” – if only because I have a leg injury right now and am running minimum mileage.

With that goal in mind, throughout the last month, fitness has been my main non-work focus. I’ve run in a couple of cross country events, and started to see some gains in my speed.

Outside of fitness, I made some decent progress on my playground project, without doing much more than fiddling with code while I watched TV. I did a bit of CSS tidying up – although that’s something I’m hoping to revisit after I have a calendar-like visualisation implemented (which might be a month or two away). It looks horrible, almost as if I’ve just mashed a bunch of styling together without much thought. Mad that 👀.

I got generic WordPress pages loading in, which allowed me to initially bring my /now page across – so I can not keep that up to date in two places, rather than one.

I think that most significantly, I got podcast integration working. I’m really pleased with this, it’s a set of data that I can’t look at at a glance anywhere else. It also might serve as a jumping-off point if I ever want to write anything about the stuff that I’m consuming day-to-day. I have a bunch of Permanent Notes in my Zettelkasten that start off as Fleeting Notes from podcasts, maybe there’s something I can do here to make some of that process public?

And I started in earnest doing something with my Weight data. There’s a bit of a spike over at https://playground.breakfastdinnertea.co.uk/weight/, but that might change significantly in the next week or two. I might even pivot libraries, given my experience with Nivo so far.

February

Next month, there are a few things I wish to focus on:

  • I want to write more. Not necessarily on here, but rather in Roam (or maybe DayOne, depending on what’s more natural) – whether that’s free journaling, morning pages, or something else. I know that if I want to write better, I need to write more. I also want to do more reflecting on what I’m doing to take stock a little and figure out whether I’m just doing stuff for the sake of doing it.
  • I want to lock down an approach to pivoting my diet and fitness regime to something more long-term sustainable and runner-friendly. I have something in my head around this, but I need to better formalise it – it’s a combination of things that work for me across Slow Carb, Intuitive Eating, and Racing Weight. But I guess I have to play with it and see if it meets my longer-term goals (improve running performance, don’t put weight back on, maybe lean up along the way), and whether it is sustainable as a set of life-long habits.
  • Finally, don’t let my ego get the best of me, and fully recover from this injury, even if it means my weekly mileage suffers temporarily.

Consumption highlights

Films

Not a massive movie month, but saw some corkers.

TV

I caught quite a bit of good TV this month. Yellowjackets was by far my favourite. Our lunchtime light binge has been Mortimer and Whitehouse: Gone Fishing, which I’m now sad to have finished.

Podcasts

  • This week, one of my favourite podcasts, The Tip Off which covers stories of fascinating and important investigative journalism cases, returned. This one covers slave labour in the British building industry, it is a great listen.
  • One from back in July, but I listened to a fascinating interview with UK Ultrarunner, Damian Hall on the Strength Running podcast – great listen, all about the dangers of drinking too much water and his experiences running stupidly long distances.
  • If you’ve not caught one yet, you should give Blindboy’s podcast a listen. His “hot takes” are amazing, I would love to see his process for putting this stuff together, because they go all over the place, make a billion great points, and then always seem to resolve to a nice take-home message full of nuance and humanity. I listened to one about pineapples in Ireland this month, and it didn’t disappoint.

Books

  • This month saw me finally complete Staff Engineer: Leadership Beyond the Management Track – a book that I finished the meat of earlier in the year, but I trickled the interview section as and when I had the breadth. Well worth a read for anyone wanting to know what a software engineer might do beyond senior. (Without spoiling much, it is one of a billion things).
  • I’ve also progressed and nearly finished Team Topologies, which is full of pragmatic wisdom about how to create teams that foster both autonomy and alignment. And in fictionland, I’ve been really enjoying A Wizard of Earthsea, one of those many “classics” that I’m only now getting around to.
Categories
Personal Projects Technical

Exposing a Personal API using Withings, WordPress, and GraphQL

My playground project started from a Google Sheet I found on Reddit to track my weight. I initially only wanted to play about and create some visualisations for it. But,eventually I stopped using that spreadsheet and, like most of my weight-loss escapades, got fat again. In the meantime, the playground developed into a hodgepodge of data sources. There was little rhyme or reason to a lot of it but kept me busy and off the streets.

Since then, I bought myself a fancy Withings body fat scale and have continued to track my weight with that. It does a job, but I don’t like that it locks me into Withings’ platform forever. One day it might shut down – leaving me at best with the hope that they’ll make my data available as a half-arsed CSV file.
I also missed some of the features in the original weight tracker. For instance, I implemented rolling average graphs. These give an idea of how my weight is trending over time, rather than day-to-day fluctuations. As much as I care about how big the poo I did was that day, the average is much more useful.
So I set to extract that weight to my own data store of choice, WordPress. It is not the first place I’d visit for storing time-series data that I could later display as graphs. Or to create an Event store to create my own personal audit trail. It is also not what I would consider a “sexy” technology choice. The primary APIs are all in PHP, a language I’ve not written in any significant manner since 2015. But it’s free, open-source, has a huge vault of useful plugins, and is malleable.
The querying/visualisation is still a work in progress. You can see how it’s going at this top-secret, hard to guess URL: https://playground.breakfastdinnertea.co.uk/weight/. With that said, the main method of data ingress has been up and running for well over 6 months now. It feels stable enough to talk about.
I was recently sharing this with a new Slack that I’ve joined (Si Jobling‘s On The Side slack). Si asked me about this particular integration, which I’ve taken as a prompt to make me write some of this down. If only so that when it next breaks, I have a little dummies guide to help me debug it all.

Overview

Here’s a bit of a dodgy and incomplete diagram showing what the information flow looks like. The “A” logo at the right is for the fantastic Astro framework which I use to render the playground. It is very early in its development, but a lot of fun to use. I’d encourage you to give it a look over if that’s your bag.

I won’t go into too much detail on the rendering part, if only because it’s still early days. But the rest of it, I’ll dig into a little below.

Custom Post Types

WordPress allows you to create your own data types using Custom Post Types. There are plugins to define these, but I like having the control you get from setting them yourself. Also, the custom post types API is dead simple. Custom types are exposable using WPGraphQL. Which gives the added bonus of not worrying about a domino chain of dependencies being up to date.

You can find the code to register these fields in this method – https://github.com/SimonS/tdee-plaything/blob/06bf8d55849db72792c24dcb78ae20b3baa1ee31/packages/bdt-customisations/lib/register-weighins.php#L11

It registers the 3 main fields in that custom post type. The “sanitize_callback” property allows you to translate from Withings format to something UTC-like.

WPGraphQL

GraphQL allows me to export those post types for consumption off-site. From there, I can aggregate and filter data without having to write custom business logic. I didn’t choose GraphQL from the outset. I started using it because I was integrating against Gatsby, where the primary API is GraphQL. Fortunately, it’s a common interchange method, so I have stuck with it.
WPGraphQL is a plugin that allows you to expose data in your blog as a GraphQL schema. Once installed, it provides a single endpoint from which I can query for lots of different data at once. For simple integrations like a single feed of weights, this is likely overkill. But here it presents new interesting opportunities. I could one day, for example, create a no-extra-code FriendFeed-like aggregation. Because no, I still haven’t realised it’s not 2008 anymore.
WPGraphQL supports many other WordPress plugins. I could use these to do a lot of this with little to no code. But as we’ve already established, I’m a boilerplate masochist. Further down the file in which I register those post types, I also set-up the GraphQL types for it: https://github.com/SimonS/tdee-plaything/blob/06bf8d55849db72792c24dcb78ae20b3baa1ee31/packages/bdt-customisations/lib/register-weighins.php#L74
It is a bit repetitive, but it allows me to tune the schema and set up some of the fields I want to filter and sort by. It also gives me extra resilience – I can keep the APIs inline myself, instead of being at the mercy of plugin authors staying up to date with each other. This generally isn’t a problem, but I have seen instances where one plugin in a dependency chain updates before the others follow. This has created some dead hard to debug problems, sometimes only solvable by pinning to specific plugin versions.

WordPress JSON Rest API

I don’t know how well known it is, but WordPress has an incredible REST API. Seriously, a really good API. Without doing anything special, once you’ve turned it on, you can post into it (and read from it, if you’re a traditionalist) and it will act on those posts as if you were entering data into its admin interface. The only problem you need to solve is authentication. There are a billion plugins for this – from OAuth2 through to basic HTTP. You should only use basic HTTP if you’re comfortable giving a third party service your username and password. (You should not be comfortable doing that, btw).
I use the fantastic IndieAuth plugin – it’s a bit of a hangover from when I integrated everything with the IndieWeb suite. I ultimately stepped away from that suite because all my customisations broke anytime the plugins updated. I should probably switch out to the OAuth2 plugin at some point, but this works for me as it stands. You generate a token with a scope of what it can do in your WP instance, and then use that token anytime you need to use a privilege.

IFTTT

IFTTT is a nice little service that pipes data between lots of different APIs. It has connectors for both Withings and WordPress, although the WordPress one only covers basic post types. So I make heavy use of its “Make A Web Request” connector.
A previous iteration of this web connector sent a request to a relay lambda I created to pass through the access token. But at the end of last year, IFTTT added the ability to create custom headers to your web requests. This means I can now retire that relay. I enjoy deleting code.
So now, my web request looks a bit like this:
  • URL: “https://breakfastdinnertea.co.uk/wp-json/wp/v2/bdt_weighin”
  • Method: POST
  • Content-Type: application/json
  • Additional Headers: Authorization: Bearer {YOUR OAUTH TOKEN GOES HERE}
  • Body:
    • {
        "meta": {
            "weight": {{Weight}},
            "weighin_time": "{{DateAndTime}}",
            "body_fat_percentage": {{FatMassPercent}}
        },
        "status": "publish"
      }
      

I have that hooked up to the Withings connector. And now, any time I weigh in, data starts trickling into WordPress.

And that’s pretty much it

At this point, I have a database slowly filling up with my weigh-ins from Withings:

A screenshot of the wordpress admin interface with a list of weights and dates. Each contains a link simply saying "Untitled".

It’s far from pretty, but it works. There is a TODO somewhere to fix that “Untitled” link. And it’s also queryable from GraphQL, in a much more digestible format:

A screenshot of the WPGraphQL interface showing a simple query for weighins

 

Admittedly, this a fair bit of messing on, and it is absolutely too much work to justify a single content type. But I now have films, podcasts, and weigh-ins wired up and I intend to pull in more. I’m hoping that the benefits multiply as I add more stuff. But even if they don’t, the worst-case scenario is that I have a nice PESOS style backup for when services either shut down or arbitrarily kick me off.
And with that cheery thought, thanks very much for reading this far. I hope it is of some use to someone out there. If you want to dig into the implementation, it’s poorly documented but available in my mini-repo. Some pointers of where to look: