Successive Tasks in Things 3

This may be more of an AppleScript question than a KM question, but what the hell: I'm currently giving Things 3 a shot through its free trial, and I wanted to take advantage of its AppleScript support (something my current task manager, 2Do, is sorely lacking) to have it automatically create a new, predefined task when I mark a given task complete. I've cobbled together something of a brute force solution with KM where it checks the status of the given task both every day at 7AM and every time I activate Things, and creates the new task if it the status returns "completed", but what I would really like to do is have Things automatically create the new task as soon as I mark the given task completed.

Here's the macro I've got right now. It works, but ideally I would like to change the time and app triggers to a trigger that runs the "create new task" AppleScript automatically, without the "If then Else" check, once the given task in Things is marked complete. Does anyone happen to have any ideas how I might go about doing that?

Auto-Create Things To-Do.kmmacros (3.9 KB)

1 Like

I think you are approaching it from the wrong side. What you are asking for is a KM trigger that actively observes all your interactions with Things. And I don't think this is possible.

Try it the other way round:

Write a macro that marks your "Debug Docs" task as completed and creates the new task in the Work area. Then launch the macro instead of manually checking the "Debug Docs" task.


BTW, which version of Things3 are you running? For me (Things 3.1.4) your AppleScript doesn't work.

This works for me:

tell application "Things3"
    tell area "Work"
        make new to do with properties {name:"New To-Do", due date:current date}
    end tell
end tell

So, here is a macro that approximately does what you want (tested with Things 3.1.4):

[demo] Things: Mark Task Completed and Create New One.kmmacros (2.1 KB)

I didn't think it was possible either, but I was hoping that I was wrong and that there was some way I didn't know of for KM to trigger a macro like this, or even that there was some entirely AppleScript based solution that checked the task's status on a loop. Ah well.

This is a good idea, and indeed one I had already considered (though I hadn't gotten around to writing a script for it, so thank you for doing that for me!) but I was still (perhaps foolishly) hoping there would be a way to have the "make new task" script run automatically even while working primarily within Things' interface, since I'm trying to familiarize myself with the app and its UI/UX during its trial period (plus, the way I had in mind, if I could get it to work, just suits the way my mind works better).

Same version, but I foolishly added the at beginning of list "Today" part to the script in KM without first testing it in Script Debugger and assumed it would still work :sweat_smile:
It works if you remove the tell area "Work" tell block though.

Anyway, unless someone else has any other ideas, it looks like I'll just have to resign myself to running a macro/script for marking tasks complete and creating new ones, rather than just the latter step. Not ideal, but still much better than having no AppleScript options available at all. Thanks for your help!

Would you really want to have an AppleScript on your computer running 24/7 and checking the status of a database entry of a specific app every 1s or so?! Personally I wouldn't :wink:

If you are thinking of Hazel, which in fact is doing (almost) realtime checks, this is a different story, because (AFAIK) Hazel relies on FSEvents, a system service that is already running, with or without Hazel.

But I have another idea for you:

Just paste the URL trigger of the macro into the Notes field of your "Debug Docs" task:

Now, instead of clicking the checkbox of the task you click that link and voilà!

Alternatively (without need of KM):

Save the AppleScript as app somewhere on your disk and drag it into the Notes field of the task. Then click that link and the script will be executed.

1 Like

Heh, fair point! This would certainly be overkill for this one thing, and isn't something I would generally want to rely on, but I'm still an AppleScript beginner, and this is the kind of thing I still need to learn just so I know about it in case I really need to make use of a solution like this.

Hmm. I do have Hazel, but use it for very little and am certainly squandering much of its potential. If it can monitor things like this in (almost) realtime, I'll have to put some time into learning how to really use it at some point. Thanks for the idea!

This is a really good workaround idea! I hadn't even considered using the Notes field of a task for something like this, so I would never have come up with this on my own. Thanks for the tip! This is probably as close to my ideal method as I'm going to get, so I think this qualifies as the solution :slightly_smiling_face: Thanks again!

Don't give up so fast :wink:

After mentioning Hazel I had another idea: Hazel is able to monitor file system changes, but it won't tell us what (inside a file) has changed. But with the proper script this may be enough!

When you perform any action in Things (e.g. marking something as completed) it will sync immediately and also write the changes to disk immediately. (Note the cloud icon that appears in the upper right corner of the Things window.) That means, after each action in Things there is something that will be registered by FSEvents.

Observing the FSEvents while changing something in Things I figured out that after each action in Things a temporary file "Things.sqlite3-journal" appears and disappears in the folder ~/Library/Containers/com.culturedcode.ThingsMac/Data/Library/Application Support/Cultured Code/Things. (The appearing/disappearing of the file may not be visible in the Finder.[1])

I tried to catch this with Hazel but it didn't really work. However – to my big surprise (thanks @peternlewis) – KM's Folder Trigger is able to catch it quite reliably!

So, here a new macro.

It does this:

  • The macro trigger is watching the aforementioned folder for any file additions ("Things.sqlite3-journal")
  • Each time the macro is triggered the AppleScript tests for a change of the status of the "Debug Docs" task. The status change must be "open" --> "completed" (and not the contrary)
  • In order to do this the script stores the previous status in NSUserDefaults (on disk: in a plist (preferences) file). [2]
  • If the required status change is met then it creates the new task in "Work"

[test] Things: Create New Task (Via Folder Monitoring).kmmacros (3.0 KB)

This is the content of the AppleScript:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set theDefaults to current application's NSUserDefaults's alloc()'s initWithSuiteName:"aaa.atest.ThingsDebugDocsTrigger"
theDefaults's registerDefaults:{ddStatusLast:"open"}

set newTask to "New To-Do"

set ddStatusPrevious to ddStatusLast of theDefaults as text

tell application "Things3"
    set ddStatusCurrent to status of to do "Debug Docs" as text
    set ddStatusHasChanged to (ddStatusCurrent ≠ ddStatusPrevious)
    if ddStatusHasChanged and ddStatusPrevious is not "completed" then tell area "Work" to make new to do with properties {name:newTask, due date:current date}
end tell

if ddStatusHasChanged then set ddStatusLast of theDefaults to ddStatusCurrent

# For debugging only
--say "changed " & ddStatusHasChanged

Thoughts:

Of course, the macro is – CPU-wise – more expensive than the manual method with the trigger in the task's Notes field. (The macro triggers on every change you make in Things.) But since it is using FSEvents it is way less expensive than having a every 1s and 24/7 looping macro.

That being said, personally, I would always prefer the manual solution (the link in the Notes field), because I don't like stuff running in the background. (There's already enough of it.) But this macro is way more interesting, and, after all, you have asked for full-auto :grinning:

If things get messed up:

You can always reset the stored status of the "Debug Docs" task by executing this in the Terminal:

defaults delete aaa.atest.ThingsDebugDocsTrigger

(I've named it "aaa…" so that you can easily locate it also in the prefs folder.)

This will reset the stored last status of the task to "open".

Note:

This is a test macro for your exemplary "Debug Docs" task. If it works for you and you decide to use it adjust the names of the variables and the name of the prefs file to something more adequate. If you want to extend the macro to other tasks just add new variables and the appropriate checks. (No need to create multiple macros, and you can store all the statuses in the same prefs file.)

Caveats:

  • If you perform your actions in Things too quickly, e.g. checking and immediately unchecking the task the macro may not be able to catch up.
  • When the script runs (and the conditions are met) you'll see a slight flickering in the Things window. Take it as a friendly confirmation that the script has run and the new task was created successfully :wink:

Edit (2017-09-08):

Optimized the AppleScript and added feedback for debugging. (Uncomment the last line of the script to get audible feedback on status changes of the task.)


[1]: You can conveniently see File System events with filemon (Terminal) or with FSMonitor (GUI)

[2]: You can as well store the status in a KM variable. (Originally the script was meant to be used with Hazel, so I used UserDefaults.)

4 Likes

Wow! I just tested this, and it looks like it works brilliantly! This is an incredibly in-depth solution that does indeed do exactly what I wanted, and is definitely not something I would ever have come up with on my own. Thank you so much for all the time and effort you’ve put into this! I knew I made the right choice in asking about this on this forum :wink: