I am trying to write an AppleScript that completes all reminders that match a name and list, but if they're either without a due date OR overdue (due date < today).
I know that I can loop over reminders to solve this, BUT looping over reminders is intractably slow when there are lots of reminders, so I would like to know how to do this in 1 line, which is multiple orders of magnitude faster in my use case.
When I run this script:
set curDate to current date
tell application "Reminders"
set myList to "Reminders"
set myTitle to "Test 1"
set completed of every reminder in list myList whose completed is false and name is myTitle and (due date is missing value or due date is less than curDate) to true
end tell
I get this error:
error "Reminders got an error: Can’t make missing value into type date." number -1700 from missing value to date
that highlights this line:
set completed of every reminder in list myList whose completed is false and name is myTitle and (due date is missing value or due date is less than curDate) to true
I cannot seem to get the type of due date and the type of missing value to match without creating a dummy reminder... observe: the following code works:
set curDate to current date
tell application "Reminders"
set myList to "Reminders"
set myTitle to "Test 1"
-- Create a dummy reminder whose due date is "missing value"
set newremin to make new reminder
set name of newremin to "Debugging delete me"
set completed of every reminder in list myList whose completed is false and name is myTitle and (due date is (due date of newremin) or due date is less than curDate) to true
end tell
I created 2 reminders to test this, both named "Test 1". One is due today and the other has no due date. The working example with the dummy reminder succeeds in completing both reminders, and runs relatively fast.
Is there a way to modify the 1 set completed... line to work without having to create a dummy reminder?
Updated Answer
There was a problem with my first answer which I worked out. Neither my script nor the script in the other answer would complete a due reminder shown in the Reminders app as not completed. I think that this is due to either a reminder synch issue (between multiple devices) or due to the fact that I completed then "un-completed" the reminder, either of which causes duplicate reminders.
Regardless of the cause, in this situation, the
every reminder...statement only compares the search criteria to one (and it's not the one the Reminders app was displaying). Here's a screen recording showing that the AppleScript never loops on the currently due reminder shown in the reminders app:https://drive.google.com/open?id=1S0ToJcMK7o5wOspwOG_eIVIJ1xowA6fg&authuser=hepcat72%40gmail.com&usp=drive_fs
Oddly, the "future-due" reminders in that recurrence do get evaluated & returned. I discovered that if I return every reminder and search through the results, I find a recurring reminder whose due date matches the incomplete reminder shown in the Reminders app, but the properties of the returned reminder shows it to be complete.
Also, the script, given
whose due date is greater than curDateonly returns future-due recurring reminders if a reminder in the recurrence has been completed then "un-completed".Another interesting/odd behavior is that if I do not include
whose due date is..., one of the incomplete reminders returned shows as due nearly a year from now and I can find no such reminder in the actual sqlite3 database.Regardless, the only solution I have found that is both fast and robust to these reminder database inconsistencies is to obtain the reminder ID's from the sqlite3 database and loop in the AppleScript on those reminder IDs.
I have a perl script I wrote to find reminders in the sqlite3 database, though I have not published it publicly yet. However, if you write your own script, here is a screen recording proof of concept of what to have it return and how you can use that reminder ID to complete the reminder:
https://drive.google.com/open?id=1S2WEyhUiL9CsyWcfFCvJ0CV7OqLqtAO2&authuser=hepcat72%40gmail.com&usp=drive_fs
I will eventually publish my perl script and I will try to update this answer when I do, but I will say that my resulting AppleScript/PerlScript combo always (so far) seems to complete every intended reminder thrown at it, though I will add a caveat that if another device's sqlite3 database has a different set of IDs in it's sqlite3 database, this script cannot complete those unless it is run on that device.
In the meantime, here is the content of my AppleScript (edited for brevity), which shows the calls to my perl script:
Old Answer
While I have not figured out a way to do this in 1 line, I did note that speed was the reason I was looking to do this in 1 line, and combined with the other answer, I have found my way to a solution that runs in 2 times the fastest speed possible that you get from using a 1-line
every reminderstatement without the need to create a dummy reminder.Even with my dummy reminder 1-line solution, I tested to discover that it was the dummy reminder creation that was taking over a minute to complete. The 1-line statement was going very fast once it had the dummy reminder to compare a "missing value due date" to any due dates of the reminders.
I also tried searching for an existing dummy reminder, but still, it ran in over a minute's time. I suspected it was the requirement of a return from the
every reminderstatement that is the time-limiting bottleneck. However, with more testing, I found that I can get a quick (roughly 7-second) execution of afirst reminderstatement that does a return.I also realized that all I'm trying to do is avoid completing reminders due in the future, so I was able to combine that with a
first reminderstatement to determine if there actually existed matching reminders that I do not want to complete based on the due date. The result is the following script that completes the 2 reminders (no due date and a past-due due date) in roughly 13 seconds (when there are no reminders that explicitly should not be completed). Worst case, if there are reminders to avoid completing, it runs in the time of @Robert Kniazidis's solution (between 1m30s and 2m30s) plus 7 seconds:And here's a screen recording showing that it runs in about 13s (compared to the other solution, which takes between 1m30s and 2m30s).
https://drive.google.com/open?id=187J4PV0qM9fB8ycdVoZBqjtBqAmYDBh2&authuser=hepcat72%40gmail.com&usp=drive_fs
Note, if there's a timeout error on the slow portion of the code, the error string is returned.