Automating My Weekly Review in Things.app

If you want a crossplatform version of this I've built a version using Shortcuts.

For the past couple of months, I've adopted a weekly review ritual to help me stay in tune with my work and not be overwhelmed. I structured the original format from Peter Akkies's comprehensive review template for Things, but I have since modified it for my purposes.

The crux of Peter's review template is a helpful series of questions that you ask yourself for each Project in your Things.app account.

This is what my Weekly Review repeating to-do used to look like. Pretty overwhelming, right?

The first iteration of this involved a repeating task with a giant text note with all the steps. For each Project in Things.app, I had to jump over to the Project, answer each question, then go to the next Project and repeat. To shorten the loop, I would open the repeated task in one window, and have another window to do the actual review.

Things does not really want you to use more than one window, and it's a little difficult to set it up, so I wrote some Hammerspoon lua code that sets up my favorite window setup. (Pictured at the top of the article.)

hs.application.launchOrFocusByBundleID('com.culturedcode.ThingsMac')
local things = hs.application.find('com.culturedcode.ThingsMac')
things:selectMenuItem("Hide Sidebar")

hs.fnutils.imap(things:allWindows(), function(v) v:close() end)
things:selectMenuItem("New Things Window")
things:selectMenuItem("Today")

things:selectMenuItem("New Things Window")
if things:findMenuItem("Show Sidebar") then
  things:selectMenuItem("Show Sidebar")
end
things:selectMenuItem("Anytime")

hs.layout.apply(
  {
    {"Things", "Anytime", hs.screen.primaryScreen(), hs.layout.left70, 0, 0},
    {"Things", "Today", hs.screen.primaryScreen(), hs.layout.right30, 0, 0}
  }
)

(from review.lua)

This was a huge time-saver, but there still were problems. I had trouble "resetting" my brain to address each Project with the same questions. Without a checklist I found I was easily distractable and the whole process easily took an hour or two.

Finally, enough was enough. I rolled up my sleeves, pulled up @culturedcode's documentation, and spent some quality hours in Apple's Script Editor to write the following. (It's long, so I'll break it up into sections.)

(function() {
  let datestamp = new Date().toLocaleDateString("en-US")

  let review_proj = {
    "type": "project",
    "operation": "create",
    "attributes": {
      "title": `Weekly Review: ${datestamp}` ,
      "notes": "",
      "tags": ["Rituals"],
      "when": "today",

I want to build a Things project for the Weekly Review ritual. All my "ritual" type projects/to-dos have the "Rituals" tag, so I add that tag and assign it to "Today". I start to build the Weekly Review Project as a JSON object… and soon I'll add some items to it for the to-do actions.


    "items": [
    { "type": "heading", "attributes": { "title": "Prep" } },

I love Things.app's headings feature, and I make quite a bit of use of it in this automation. Here's one for the "Prep" part, then the following to-dos.

      {
        "type": "to-do",
        "attributes": {
          "title": "📓: Review journal."
        }
      },
      {
        "type": "to-do",
        "attributes": {
          "title": "✅: What did you accomplish?",
          "notes": "things:///show?id=logbook"
        }
      },
      [...]
      {
        "type": "to-do",
        "attributes": {
          "title": "Process your Things inbox. Assign each task to an area or to a project. Tag appropriately. (:estimate, $focus, !categorize, priority)"
        }
      },
      {
        "type": "to-do",
        "attributes": {
          "title": "Go through each of your projects. Use the checklists below."
        }
      },
      ]
    },
  };

The first things added to the Weekly Review project are the initial setup actions… reminders to gather all the sources of to-dos, review and prepare.

Now it's time to gather all the projects to review! I wanted to build Peter's checklist as a to-do for all the Projects currently active in my Things.app, something like the following:

With this system, I at least have a checklist to go through for each project!

Let's build it!


  let Things = Application("Things");
  Things.launch();
  for (area of Things.areas()) {
    review_proj["attributes"]["items"].push(
    { "type": "heading", "attributes": { "title": "Projects: " + area.name() } },
    )
    for (proj of Things.projects().filter(p => p.area() != null && p.area().id() === area.id())) {
      if (proj.status() == "open" ) {
        review_proj["attributes"]["items"].push(
        {
          "type": "to-do",
          "attributes": {
            "title": "Review: " + proj.name(),
            "notes": "things:///show?id=" + proj.id(),

Now I'm using Things.app's JXA/Applescript dictionary to loop through Things.app internal database. First I loop through the Things.areas() so that I can split up the Projects by area, then for each Project in Things.projects() I add the checklist of questions from Peter Akkie's worksheet (with some modifications for my purposes, shortened for this post.):

            "checklist-items": [
            {
              "type": "checklist-item",
              "attributes": {
                "title": "Is this project still relevant?"
              }
            },
            {
              "type": "checklist-item",
              "attributes": {
                "title": "Can I delegate this project?"
              }
            },
            {
              "type": "checklist-item",
              "attributes": {
                "title": "Should I move this project to Someday?"
              }
            },
            [...]
            {
              "type": "checklist-item",
              "attributes": {
                "title": "Is there a clear 'next action' for this project? (If not, break down your projects or tasks into smaller tasks until there is a clear next action.)"
              }
            }
            ]
          }
        }
        )
      }
    }
  }

Now that I'm done looping through the Projects, it's time to add the last actions related to the "Planning" time of the Weekly Review.

  review_proj["attributes"]["items"].push(
  { "type": "heading", "attributes": { "title": "Plan" } },

I add another heading to the items, and then the remaining to-dos.

  {
    "type": "to-do",
    "attributes": {
      "title": "Identify what’s due soon. Use the Upcoming view."
    }
  },
  {
    "type": "to-do",
    "attributes": {
      "title": "Identify which tasks are available for you to work on. Use the Anytime view."
    }
  },
  {
    "type": "to-do",
    "attributes": {
      "title": "Plan what to work on next. Assign “when” dates, as you learned in the section on planning your days and weeks. Choose important tasks as well as urgent ones."
    }
  },
  {
    "type": "to-do",
    "attributes": {
      "title": "🖊: Build spread for next week."
    }
  },
  {
    "type": "to-do",
    "attributes": {
      "title": "⭐️: What is essential for next week?"
    }
  }
  )
  let url = "things:///json?data=" + encodeURIComponent(JSON.stringify([review_proj]));
  return url;
})();

(from weekly_review.lua)

That's it! You can take the whole JXA script) from me and deploy it however you want, but I use it as part of my Hammerspoon Headspace script:

local buildThingsProjectUrl = function()
    return hs.osascript.javascript([[
        (function() {
           … (the JXA code above) …
        })()
    ]])
end

local ok, url = buildThingsProjectUrl()
if ok then
    hs.urlevent.openURL(url)
else
    print(result)
    print("something wrong with the jxa to build a review project.")
end

When I enter the Headspace, the JXA is run and the result is set to url, which I then open using hs.urlevent.openURL(). While I was troubleshooting the script, I made heavy use of Apple's Script Editor to debug because Hammerspoon kind of eats the JXA errors. If you want to see the whole thing in context, you can look here.

The finished review: Prep, Projects, Planning, all separated by Headers and with appropriate checklists.

This fulfills a lot of my goals: the last few weekly reviews that I've timed have decreased from two hours to around 30 minutes, mostly because I don't lose my place. 😛 I also have started taking notes on the week in the notes of the individually created Projects, so that I can actually go look at last week's lessons in the Logbook by searching for "Weekly Review." It is dependent on being on my desktop, I don't believe there is anyway for the JXA to work on iOS.

Not having a structured review was one of the main reasons that I have been tempted by the siren-call of Omnifocus. While this is a hack, and certainly one that is a little programmer-specific, it's enough to make me stay with Things.app for a while longer.

You can use the script without any coding thanks to Automator!

If you would like to use this script, and you aren't much of a coder, I've wrapped it in an Automator Service. You just download the Service, run it and it'll install in the Services menu of your Things.app.1 👍

Let me know if you find this useful, or if you have something that you want changed for your particular use case. 🙏


  1. You may also have to set Enable Things URLs in the Settings. things pref pane 


Changelog
  • 2023-01-27 22:22:04 -0600
    Update: link to Shortcut version

  • 2022-06-08 11:31:29 -0500
    Rename articles

  • 2021-01-12 09:52:45 -0600
    Fix misspelled tag

  • 2020-09-28 09:20:43 -0500
    Change repo name

    I decided to change the name of my hammerspoon configuration repo to
    `hammerspoon-config` to avoid confusion... it's not the actual
    hammerspoon executable, it's a configuration for that executable.

  • 2020-07-10 15:07:28 -0500
    Fix bug in Weekly Review.

    Thanks to beta-tester @CollinsJacob, I learned that `encodeURI` doesn't
    escape ampersand "&" characters. ¯\(°_o)/¯

    Updated the downloadable Automator Service and code snippets.

  • 2020-07-06 09:55:21 -0500
    Add instructions for enabling Things URLs

  • 2020-07-05 14:01:49 -0500
    New post: Automated Weekly Review