February 17, 2022 at 8:37 PM by Dr. Drang
The release of Keyboard Maestro 10, with its addition of subroutines, and a couple of recent questions on the Keyboard Maestro forum regarding date calculations got me thinking that I should make a subroutine that calculates how many days it will be until the next Monday, Tuesday, Wednesday, or whatever weekday. And while I’m at it, I should do the same for the most recent Monday, Tuesday, etc. The calculations could be done in the same subroutine, with the difference being that upcoming days return a positive number and previous days return a negative number.
There are two reasons I decided to have the subroutine return a number instead of a date:
- Most import, Keyboard Maestro doesn’t have a Date type. While I could have the subroutine return a date string, like “2022-02-17,” that would make it difficult to use in macros that want a date string in a different format.
Keyboard Maestro has a token,
%ICUDateTimePlus%that allows you to specify a date in the past or future and format it however you like. So, for example, if it’s Thursday (which it is as I type this) and I want the date of last Sunday in the format “13 Feb 2022,” I could call
%ICUDateTimePlus%-4%Days%d MMM yyyy%
So by having my subroutine return the number of days until or since the weekday of interest, I can plug that return value into the
%ICUDateTimePlus%token in the macro that calls the subroutine.
Date calculations of this type (when is the next/previous weekday?) are typically done using modulo arithmetic, which is basically division where you return only the remainder. Ideally, the language you’re programming in assigns the numbers 0 through 6 to the days of the week (which weekday is Day 0 doesn’t matter as long as you know what it is) and you add or subtract days mod 7 to get the weekday of interest.
Keyboard Maestro has a
DOW() function which, if given no argument, returns an integer representation of today’s day of the week. The return value ranges from 1 (Sunday) to 7 (Saturday) instead of 0 to 6, but we can work around that.
Let’s say the number of the target weekday is stored in the variable
LocalTargetWeekday. Then if we’re looking for the next target weekday, we do this calculation,
(LocalTargetWeekday - DOW() + 7) MOD 7
and if we’re looking for the previous target weekday, we do this
(LocalTargetWeekday - DOW() - 7) MOD 7
The important thing to know here is that Keyboard Maestro’s
MOD operator returns a positive number if the number before
MOD is positive and a negative number if the number before
MOD is negative. This is why I add 7 in the “next weekday” formula and subtract 7 in the “previous weekday” formula. By doing so, I force the value before
MOD to be positive or negative as needed. And adding or subtracting multiples of 7 doesn’t change the remainder.
Let’s show how the calculation works. If it’s Thursday (5) and I want to know how many days until the next Monday (2), the calculation is\[(2 - 5 + 7) \bmod 7 = 4 \bmod 7 = 4\]
The number of days until next Saturday (7) is\[(7 - 5 + 7) \bmod 7 = 9 \bmod 7 = 2\]
Similarly, to get the number of days until the previous Monday, it’s\[(2 - 5 - 7) \bmod 7 = -10 \bmod 7 = -3\]
And the number of days until the previous Saturday is\[(7 - 5 - 7) \bmod 7 = -5 \bmod 7 = -5\]
All of which are the answers we expect, including the signs.
There is still one ambiguity left. Suppose we want the number of days until the next Thursday. The formula gives\[(5 - 5 + 7) \bmod 7 = 7 \bmod 7 = 0\]
Is this what we want? Maybe, maybe not. I can certainly anticipate some situations in which you want next to mean strictly next, in which case the correct answer is 7, and others in which you want it to mean today or next, in which case 0 is correct. So the subroutine should handle both of those situations.
Here it is.
Instead of building the subroutine yourself, you can just download it.
As you can see, the subroutine is called with three arguments:
- The target weekday as a number using Keyboard Maestro’s scheme.
- Whether you’re looking forward (the default) or backward.
- Whether your definition of next or previous is strict (the default) or not.
As you can see, any word that starts with “b” will make the subroutine look for the previous weekday, and any word that starts with “n” will make the search not strict. Also, Keyboard Maestro’s way of matching strings with the “starts with” option is not case-sensitive, so you can use “B” and “N,” too.
The subroutine would be used in a macro like this:
I used to end a lot of my posts with music videos in which the lyrics matched up with something I wrote about. I should go back to doing that.
February 14, 2022 at 11:25 PM by Dr. Drang
I’ve been measuring and recording my blood pressure more or less every day since early fall. I was in the “prehypertension” range at my last checkup, and my doctor wanted me to keep track of it. I’ve been adding each day’s reading to a table in Apple Notes, which has worked out well because Notes launches quickly on my phone, and it’s easy to add a new row with each reading.
To make entering the date faster, I made a Shortcut that puts today’s date on the clipboard. That shortcut sits in a widget on my iPhone’s home screen, and I run just before opening Notes. It’s just three steps:
|1||Get today’s date.|
|3||Put it on the clipboard.|
I used the
yyyy-MM-dd format because it’s easy to parse, and I thought someday I might want to pull the data out of Notes and do something with it.
That someday came yesterday when I decided to graph the data. For me, the easiest way to do that is to start with a CSV (comma-separated values) file and use a Python script to make the graph.
To make the CSV file, I opened the note on my Mac, copied the table, pasted it into BBEdit, and did some quick editing. The resulting file, named
bp.csv, looked like this:
Date,Systolic,Diastolic 2021-09-17,125,71 2021-09-18,137,72 2021-09-19,138,73 2021-09-20,124,71 etc.
From here, it was relatively simple to write a short Python script that used Pandas and Matplotlib to read in the data and plot it. I’ve written plenty about making plots in Python, so I’m not going to say any more about that—at least not in this post. I want to focus on how I streamlined my system.
The plot showed me when I had been doing well and when I had been doing poorly in controlling my blood pressure, so it seemed like a good idea to set up a system that would make a new plot every week or two. But to do so, I couldn’t rely on the manual process I had just used to take the data from Notes and turn it into a CSV file. I needed to either automate that process or create a system that would update the CSV file directly with each day’s blood pressure reading. I opted for the latter with this shortcut:
|1||Get today’s date.|
|2||Format it in a form that Pandas likes.|
|3||Prompt the user for blood pressure, which should be entered as either nnn/nn or nnn,nn.|
|4||Separate systolic from diastolic by a comma instead of a slash.|
|5||Assemble the CSV line for today.|
|6||Append the line to the CSV file. The file is in the
With this in a widget on my home screen, I can tap the button to launch the shortcut and enter the reading I just took. The comma-separated date, systolic, and diastolic are appended directly to the CSV file. This new system is not only better at organizing the data for plotting, it’s also faster at entering it in the first place. No need to open Notes, no need to run a shortcut to get the date, just enter the BP directly and everything is taken care of.
I don’t regret starting out using Notes. It was easy to set up, it synced across all my devices, and it kept the data in a format that I could export and transform. But it’s time to move on.
Update 02/15/2022 9:32 AM
A Mr. Richard Twitter of Ft. Lee, New Jersey asks1
Dear Dr. Drang,
Why don’t you put your blood pressure readings into the Health app? Couldn’t you just use the charts in Health? Or plot your blood pressure in Charty? And why don’t you buy a blood pressure cuff that will send its readings directly to Health?
Dear Mr. Twitter,
You ask a lot of questions with answers that should be obvious—even to someone from New Jersey.
The Health app is fine for tracking my walks and bike rides, but I don’t think much of it otherwise. I have basically no control over the charts it makes, and—most important—it lives exclusively on the iPhone. I want my data everywhere, and I don’t want to use workarounds to move it from one place to another. A CSV file on iCloud Drive allows me to do whatever I want.
As for Charty, it’s also more limited than I like. Plus, I use Python and Matplotlib so much, it takes almost no time for me to build a script that does just what I want.
Finally, I already had a cuff and didn’t see a need to buy a new one to get automatic connection to an app I wouldn’t use. And I’m cheap.
Gen Xers may think they’ve taken over the cultural nostalgia thing after the Super Bowl halftime, but Boomers will never give up. ↩
February 13, 2022 at 10:49 AM by Dr. Drang
February 10 was the last day Wordle was still available at its old site. A URL to one of the Wayback Machine snapshots on that day is
main.e65ce0a5.js file in the Extra Scripts folder in the lower left part of the window.
Now you have the two files needed to run Wordle either locally or on a server you control. What about your history? Unfortunately, that will be reset if you start playing from a new location. But if you take screenshots of your history and your last game, you can get the information needed to restore your history.
Wordle uses what’s called local storage to save your most recent game and your history. The most recent game information is stored in
gameState and the history in
Change Lines 18–31 to your history and Lines 38–42 to your most recent game. Lines 43–44 are a time stamp (in microseconds) for your most recent game. If it happens to be today, you can get that (more or less) from the
date returns seconds, and we need microseconds, just add three zeros to the end of the return value.
After you’ve made these changes, save the file as
sethistory.html in the same directory as the Wordle HTML file. Then navigate to that page using the browser and device you like to play Wordle on, click the button (which may be a tiny button in the upper left corner if you’re doing this on an iPhone), and your new Wordle should be set to carry on from your old one.
I’m using the terminology from Safari. There are similar commands in other browsers. ↩
February 12, 2022 at 5:26 PM by Dr. Drang
Not much to say beyond the title. I just realized I’d never followed up on the post I wrote last year when I started using NetNewsWire again. The past seven months of RSS feed reading have been great. NetNewsWire looks good and works perfectly on iPhone, iPad, and Mac. Even better, I haven’t had any iCloud syncing problems.
I know there are people with very complex systems for collecting and reading stuff on the internet. For them, NetNewsWire probably wouldn’t work. Their loss.