Advertisement

Schedule a Weekly Reminder Email with AppleScript

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

We've recently gone over how to use two very important components of AppleScript: conditional statements and repeat loops. When combined, the power of these two tools is incredibly powerful. Now we're going to use what we've learned to build a simple and practical workflow that will automatically email out a list of your incomplete tasks from the Reminders app.


Step 1. Create an Algorithm

What we need is a simple, plain language list of steps to guide us.

Before we begin typing our code, it's critical that we figure out what it is that we're doing. We already have a good idea of the end result that we're looking to achieve, but blindly setting off to meet that goal is not the most effective way to achieve it.

Instead, what we want to do is work up a basic algorithm that will serve as a road map for how we lay out our code. What we need is a simple, plain language list of steps to guide us.

The tricky part here is that we need to build our algorithm while keeping in mind what we know about the inner workings of AppleScript. Our steps won't have code in them, but they need to be code conscious. Otherwise, we'd end up with something like this:

  • Get reminders
  • Put reminders in email
  • Send message

This is arguably somewhat useful, but in truth we could keep track of that process mentally without ever spelling out a specific algorithm. The reality of the situation is that this process is much more complex and requires some deeper thought into how we're going to accomplish each piece. Here's a more thorough and useful attempt at an algorithm:

  • Get all reminders in a specified list
  • Target only those reminders that are not completed
  • Figure out if the reminder has a due date
  • If the reminder has a due date, grab both the name and due date
  • If the reminder doesn't have a due date, just grab the name
  • Convert all of the relevant reminder information into Applescript list items
  • Create a new mail message and set its properties
  • Output our list items to the message body
  • Set the recipient of the message
  • Send the message
  • Schedule message to occur daily or weekly

Now we're talking. This is much more specific and effectively outlines how our code is going to be structured. Our first attempt was deceptively simple and seemed like it could be done in three or four lines of code. Our second attempt represents a much more realistic look at how much work is going to go into this script.


Step 2. Target Reminders App

Our first coding task is to get AppleScript to communicate with the new Reminders app inside of Mountain Lion. We of course do this with a tell block, just as if we were communicating with any other app.

[applescript]
tell application "Reminders"
end tell
[/applescript]

Our tell block will contain all of the commands that we want to toss at the Reminders app. Any code placed between these two lines will be assumed to be directed at this specific app.


Step 3. Start a Repeat Loop

The todo item at the top of our algorithm is to get all of the reminders in a specified list. We don't actually want all of them at once though. Instead, we want to grab one at a time, test to see if it matches our criteria and proceed accordingly.

To accomplish this cycling effect, we can use a repeat loop, which we learned all about in a previous article. This circumstance calls for a repeat with loop:

[applescript]
tell application "Reminders"
repeat with i from 1 to (count of every reminder of list "Reminders")
end repeat
end tell
[/applescript]

What this does is establish a block of code that will repeat once for every reminder in a list titled "Reminders". You should have one of these by default in your Reminders app.

reminders app
The Reminders list that we're targeting.

Every time the code block runs, the variable i will increment until it is equal to the number of items in our Reminders list.


Step 4. Set Your Variables

The next thing that we're going to do is create a whole mess of variables. Some of these will be used right away, others you won't see until later in our script, it's just a good idea to get them out of the way all at once.

[applescript]
--date
set theDate to "Reminders for " & date string of (current date)

--empty variables
set theEntry to ""
set theList to {}
set theOutput to ""

tell application "Reminders"
repeat with i from 1 to (count of every reminder of list "Reminders")

--reminder variables
set theReminder to reminder i of list "Reminders"
set reminderName to the name of theReminder

end repeat
end tell
[/applescript]

The first variable grabs the current date. We do this by referring to the "date string" of the current date. This takes the following format: Friday, February 1, 2012. By itself, "current date" is much larger and contains the time as well as the date.

After we have the date, we create three empty variables. This seems like a strange thing to do, but there is logic behind it. Basically, we're going to add to and manipulate these variables inside of our repeat and if blocks and we need them to have a basic level of existence before we can do that.

Finally, inside of our repeat loop, we create two more variables. theReminder is simply a shortcut so that we don't have to type out "reminder i of list Reminders" a bunch of times. reminderName grabs the name of each reminder as we cycle through the list.


Step 5. Toss Out Completed Items

The problem with our current structure is that is cycles through all of your reminders, even those that have been completed. We're not really interested in those so we need to filter them out with a simple conditional statement. This goes inside of the repeat loop right after the reminder variables from the previous step.

[applescript]
--reminder variables
set theReminder to reminder i of list "Reminders"
set reminderName to the name of theReminder

--is complete?
if theReminder is not completed then
end if
[/applescript]

As you can see, it's as simple as one if statement that targets not completed reminders. Now any code that we toss inside of this will execute only on the relevant reminders.


Step 6. Create a List of Pending Reminders

Now that we have a conditional statement to filter out non-completed items, we need find a way to fill theList with the title and due date of each pending reminder. The tricky part is, the Reminders app doesn't require a due date, which means that some reminders will have them and others won't.

To handle this we need to set up two different scenarios. First, we'll test to see if a due date exists. If it does, we'll set theEntry to the due date and the reminder name. If a due date doesn't exist, we'll skip the due date and just add the title to theEntry. Finally, we'll add theEntry to theList. Again, this process will be taken on all non-completed reminders, gradually building up theList so that it contains all of the information that we need.

[applescript]
--is complete?
if theReminder is not completed then
--has due date?
if due date of theReminder exists then
set dueDate to due date of theReminder
set theEntry to reminderName & linefeed & dueDate
else
set theEntry to reminderName
end if
set theList to theList & theEntry
end if
[/applescript]

As you can see, we've nested one conditional statement inside of another. Generally, I steer clear of this when possible to keep things simple and readable, but in this case, one layer of nesting is perfectly acceptable. Also note that I've placed a linefeed in between the reminder name and due date so they won't appear on the same line in our final output.


Step 7. Format the Output

We're pretty much all finished with the Reminders app tell block. Next, we need to take all of that text that was placed into theList and format it nicely for output into an email.

To accomplish this though, we need to get a feel for how our output looks in its current state. Any time you need to get a glimpse of some stored value, try using a return statement. Here's all of the code that we've written thus far followed by a statement that returns the contents of theList as a string of text.

[applescript]
set theDate to "Reminders for " & date string of (current date)
set theEntry to ""
set theList to {}
set theOutput to ""

tell application "Reminders"
repeat with i from 1 to (count of every reminder of list "Reminders")

--set variables
set theReminder to reminder i of list "Reminders"
set reminderName to the name of theReminder

--is complete?
if theReminder is not completed then
--has due date?
if due date of theReminder exists then
set dueDate to due date of theReminder
set theEntry to reminderName & linefeed & dueDate
else
set theEntry to reminderName
end if
set theList to theList & theEntry
end if
end repeat
end tell

return theList as string
[/applescript]

If we run this, we'll see a bunch of text pop up in the "Result" section of AppleScript Editor. This is the text that is stored in theList, but as you can see below, it's a little messy!

The output of our script thus far.
The output of our script thus far.

We've definitely got some line break issues. Let's clean that up shall we? The first thing you'll want to do is remove that return statement. It was just a temporary trick for testing purposes and has no place in our final script.

Next, insert the chunk of code below in place of the return statement. What we're doing here is cycling through the items in theList, adding extra linefeeds after each and inserting a few dashed lines to make everything look pretty.

[applescript]
repeat with i from 1 to (count of every item of theList)
set theOutput to (theOutput & item i of theList as string) & linefeed & "---" & linefeed
end repeat
[/applescript]

Now if we add in another temporary return statement to check theOutput, what we see is much prettier and easier to read. Notice that our script is handling both reminders with and without due dates just fine and separates each reminder with those dashes that I mentioned before.

The output of our script thus far.
The output of our script thus far.

Step 8. Send the Email

We're almost finished! Now that we have a nicely formatted list of the current reminders, we just need to get that information in an email and send it off.

This is where things get a little tricky. Scripting Mail is a pretty quirky task and you kind of just need to know how it works to make it all turn out right. The AppleScript Dictionary will help, but things aren't as straightforward as they are in other apps so don't feel discouraged if you get confused.

The basic process is as follows. First, you have to set a variable to create a new message for which you assign a set of properties (visible, sender, subject, and content). Then, you actually initiate a tell block on that variable (which is the new outgoing message) and make a new recipient. Then you of course send the message. Here's what this looks like in practice:

[applescript]
--Send Email
tell application "Mail"
activate
set theMessage to make new outgoing message with properties {visible:true, sender:"senderemail@gmail.com", subject:theDate, content:theOutput}
tell theMessage
make new to recipient with properties {address:"recipientemail@gmail.com"}
send
end tell
end tell
[/applescript]

Notice that we have a sender and a recipient here. You can make these the same email if you wish to send the message to yourself, or perhaps you want to include several team members so that you can use this daily email to keep everyone on task. It's up to you!

We also have a subject with the date in it that utilizes our theDate variable from before. The content of the message is set to theOutput so it will look just like what we saw in our tests.

Our script is now finished. Just to be sure you got it all, here's the full thing:

[applescript]
set theDate to "Reminders for " & date string of (current date)
set theEntry to ""
set theList to {}
set theOutput to ""

tell application "Reminders"
repeat with i from 1 to (count of every reminder of list "Reminders")

--set variables
set theReminder to reminder i of list "Reminders"
set reminderName to the name of theReminder

--is complete?
if theReminder is not completed then
--has due date?
if due date of theReminder exists then
set dueDate to due date of theReminder
set theEntry to reminderName & linefeed & dueDate
else
set theEntry to reminderName
end if
set theList to theList & theEntry
end if
end repeat
end tell

repeat with i from 1 to (count of every item of theList)
set theOutput to (theOutput & item i of theList as string) & linefeed & "---" & linefeed
end repeat

--Send Email
tell application "Mail"
activate
set theMessage to make new outgoing message with properties {visible:true, sender:"senderemail@gmail.com", subject:theDate, content:theOutput}
tell theMessage
make new to recipient with properties {address:"recipientemail@gmail.com"}
send
end tell
end tell
[/applescript]


Step 9. Scheduling the Script

Now that the script is perfectly functional, we need to figure out a way to schedule it to run regularly, say every Monday morning so you can get a look at your week. To do this, save the script as an application, place it anywhere you like on your hard drive, then open up the Calendar app.

From here, you'll probably want to create a new calendar specifically for automated tasks (so they won't clutter up your other calendars). Then create a new event that repeats every Monday. Finally, create an alert for the event that opens a file and choose your script application.

Scheduling the Script
Scheduling the Script in Calendar.

With this in place, every Monday at 8am, whether I'm at my computer or not, an email will be sent to me containing my Reminders for the week.

Reminder Email
Reminder Email

Make It Better!

If you followed the steps all the way to this point, congratulations, you just built an awesome little script that will help keep you productive this year. Now it's time to take what I've done and tweak it to suit your own needs. How can you make it better suited for your workflow?

Advertisement