Show one task from today at a time (AppleScript solution?)

Hey all,

I’m not sure if there’s any way to do this with either a modification or perspectives, but it would be great if there were a mode to literally only show one task from my “today” perspective.

From there, perfection would be two options:

  1. Skip (that is, just move to the next with no change otherwise)
  2. Mark as completed (aka, normal space bar), wherein it would check the task and automatically show the next.

Think of it as an ultra-focused perspective that literally lets you only see a single task at a time. It helps me from getting overwhelmed by seeing a bunch of tasks all at once.

Thanks!
Alex

Edit: my final solution posted below at my last post.

2 Likes

I’ve used OmniFocus as a repository to hold my next actions. When I visit my today perspective, I’ll see all of my due soon/overdue tasks and my flagged tasks. I take out an index card and choose three tasks from the today perspective. Then I work off of that.

The less time I spend in OmniFocus means more time to complete the 3 tasks on my index card. I’ve found that if I have OmniFocus visible on my computer or iPhone, I might get distracted and start looking at other things to do besides the 3 tasks on my index card.

2 Likes

That’s certainly a way to go about it - just wish there were a more digital/smooth way to do it with re-writing my tasks elsewhere :).

LOL… writing down 3 tasks isn’t going to hurt my fingers.

There’s something magical about physically writing three things down. It signals actual intent that I will get them done.

I’ve found that the more I stay away from the digital stuff (peeking at OmniFocus), the more I actually get done.

One potential route I thought of but lack the expertise to do:

  1. Pull up the list of tasks from the current perspective.

This can be done utilizing the code from this function (though made to loop through all instead of randomize):

– Run this script to select a random task in your current view. It probably won’t work with nested tasks.
tell application id “com.omnigroup.OmniFocus2”
tell default document
tell the front document window
set theTasks to tree of content
set randIdx to random number from 1 to the length of theTasks
repeat with aTask in theTasks
set aTask’s selected to false
end repeat
set theTask to item randIdx of theTasks
set theTask’s selected to true
return theTask’s name
end tell
end tell
end tell

FOR EACH TASK (loop through each):

  1. Rename it to add “[SuperFocus]” or something to the end of the task name.
  2. Still in the loop, call this function to search for all tasks with “[SuperFocus]” in the name (aka, just the one above):

on run
set my_dialog to “[SuperFocus]”
set search_perspective to “Search Remaining”

tell application id “com.omnigroup.omnifocus2”
activate

  tell default document
  	tell first document window
  		set perspective name to search_perspective
  		set search term to my_dialog
  	end tell
  end tell

end tell
end run

  1. Still in the loop, display a dialogue with “completed” and “skip” options (and potentially “stop”):

  2. If “completed” marked, mark the task in OF as completed.
    If “skip” marked, do nothing for this step.
    If “stop” marked, the entire script finishes then and there.

  3. Remove “[SuperFocus]” from the task’s name.

So step one gets the list of tasks, and each task gets steps 2-6 applied to it.

The idea, then, is that the script will pause at step 4 (the dialogue with “completed” or “skip”) until the user selects an option - which may be in a second or 5 hours, shouldn’t matter.

Caveat: to keep it simpler, the assumption is that the user will “stop” the script if they change the tasks in the perspective partway through (particularly if they remove a task outside of the script’s functionality, which may have the program screw up?).

If anyone has the knowledge to do it, I don’t think it would be a super difficult script to write, but it would be awesome :).

Bonus points if they can put it in a button on the top of OF, much like the famous “Complete and Await Reply” and “Verify Next Actions Exist” scripts.

Anyhow, my two cents!

EDIT: I’m trying to work some on my own, but running into an issue. Here’s what I have so far:

tell application id “com.omnigroup.OmniFocus2”
activate
tell default document
tell the front document window

  	set theTasks to tree of content
  	-- Run Array
  	repeat with aTask in theTasks
  		set display_name to get name of aTask
  		display alert "Now looking at " & display_name
  		set name of aTask to "test"
  	end repeat
  end tell

end tell
end tell

However, I get the error:

OmniFocus got an error: Can’t set name of tree to “test”.

I can’t seem to figure out what a “tree” is, especially when I can get display_name from “name of aTask” but can’t edit “name of aTask”?

Any help here would be great :).

Try this as a start …

on run {}
	set PerspectiveName to "Active"
	tell application "OmniFocus"
		tell front document window of default document to set its perspective name to PerspectiveName
		tell content of front document window of default document to set TreeList to (value of every leaf)
		repeat with ListItem in TreeList
			set ContextName to name of context of ListItem as text
			set TaskName to " - " & name of ListItem
			display dialog "Task: " & ContextName & TaskName buttons {"Complete", "Skip", "Cancel"} default button "Cancel"
			if (result = {button returned:"Cancel"}) then return
			if (result = {button returned:"Complete"}) then set the completed of ListItem to true
		end repeat
	end tell
end run


JJW

Ah, it threw an error but I messed around and got it to work - thanks!

I actually removed the auto-set PerspectiveName so I could run it from any particular perspective/context I want (usually “Today,” but you never know.

I didn’t bother with changing-the-name-to-show-just-the-task in OF in the end. Partly because I just can’t get changing the name to work (there’s seemingly nothing on Google after hours of searching). Partly because I can just leave OF minimized and work straight from the applescript, no need to even see OF as I do it.

Anyhow, this works for my purposes. Thanks again!

Final code I have (saved it as an application):

on run {}
tell application “OmniFocus”
tell content of front document window of default document to set TreeList to (value of every leaf)
repeat with ListItem in TreeList
set ContextName to name of context of ListItem as text
set TaskName to " - " & name of ListItem
set my_dialog to (display dialog "Task: " & ContextName & TaskName buttons {“Complete”, “Skip”, “Cancel”} default button “Cancel”)
set button_result to button returned of my_dialog
if (button_result = “Cancel”) then return
if (button_result = “Complete”) then set the completed of ListItem to true
end repeat
end tell
end run

EDIT: When run in the toolbar (in Omnifocus, Help -> Open Scripts Folder; then View -> Customize Toolbar to make it show up), you can’t manually close the OF window. To clear this up, I have the script automatically minimize it.

Omnifocus won’t respond while this app is running (whether this app is launched from OF or as a standalone Applescript app), but quick entry still works. If you need to mess with anything else, you’ll have to cancel the script, do your thing, and start over (which shouldn’t be a big deal - if you’ve skipped any, you’ll just have to skip them again).

Final version (one line added for minimizing):

on run {}
tell application “OmniFocus”
set miniaturized of window 1 to true
tell content of front document window of default document to set TreeList to (value of every leaf)
repeat with ListItem in TreeList
set ContextName to name of context of ListItem as text
set TaskName to " - " & name of ListItem
set my_dialog to (display dialog "Task: " & ContextName & TaskName buttons {“Complete”, “Skip”, “Cancel”} default button “Cancel”)
set button_result to button returned of my_dialog
if (button_result = “Cancel”) then return
if (button_result = “Complete”) then set the completed of ListItem to true
end repeat
end tell
end run

Update script, for anyone else who stumbles along this.

Changes:

  1. The list of tasks is now randomized each time the script is ran (helps to keep things feeling fresh).
  2. The script won’t time out after 60 seconds of waiting for a response - now each task should be able to sit and patiently wait for an entire 24 hours before throwing an error. If more is somehow needed, adjust the 86,400 below.
  3. Fixed an error wherein, if OF was already minimized, the license window would open for some reason. Now it checks and only minimizes the window if it isn’t already minimized.
 -- Set function for shuffling
 on randomizeList(theList)
  set listCount to count of theList

set newList to {}
repeat listCount times
set subListCount to count of theList
set r to random number from 1 to subListCount
set end of newList to item r of theList

  -- remove the random item from theList
  if subListCount is 1 then
  	exit repeat
  else if r = 1 then --> first item
  	set theList to items 2 thru end of theList
  else if r = subListCount then --> last item
  	set theList to items 1 thru -2 of theList
  else
  	set theList to items 1 thru (r - 1) of theList & items (r + 1) thru -1 of theList
  end if

end repeat

return newList
end randomizeList

 -- Set function to get count of tasks and shuffle those counting numbers
  on run {}

– Get current OF data and minimize main window
tell application “OmniFocus”
if miniaturized of window 1 is false then set miniaturized of window 1 to true
tell content of front document window of default document to set TreeList to (value of every leaf)
end tell

set myList to {}
repeat with B from 1 to count of TreeList
set myList to myList & B
end repeat

set randomizedList to randomizeList(myList)
–return randomizedList

tell application “OmniFocus”
tell content of front document window of default document to set TreeList to (value of every leaf)

  repeat with randomNumber in randomizedList
  	with timeout of 86400 seconds
  		set ListItem to item randomNumber of TreeList
  		set ContextName to name of context of ListItem as text
  		set TaskName to " - " & name of ListItem
  		set my_dialog to (display dialog "Task: " & ContextName & TaskName buttons {"Complete", "Skip", "Cancel"} default button "Cancel")
  		set button_result to button returned of my_dialog
  		if (button_result = "Cancel") then return
  		if (button_result = "Complete") then set the completed of ListItem to true
  	end timeout
  end repeat

end tell
end run

I know it’s been while since this thread was updated but I just chanced upon it in search of the very thing you’re creating here. alexg25, I’ve used your script but for some reason it doesn’t work for the perspective that’s currently open; it shows the tasks of the perspective behind the current one. So if i have two perspectives open, it will minimize the one I want to minimize but show the tasks of the window still open.

Any thoughts on how to fix this?

Hey timmoore7,

You can check the following code - I’ve updated it slightly since the last update, I believe.

As far as two perspectives open . . . I’ve never done that workflow, so I’m not sure. I barely managed to scrape even this code together, so I’m hopeless to fix it for something like that. But let me know if you happen across something!

-- Set function for shuffling
on randomizeList(theList)
set listCount to count of theList

set newList to {}
repeat listCount times
	set subListCount to count of theList
	set r to random number from 1 to subListCount
	set end of newList to item r of theList
	
	-- remove the random item from theList
	if subListCount is 1 then
		exit repeat
	else if r = 1 then --> first item
		set theList to items 2 thru end of theList
	else if r = subListCount then --> last item
		set theList to items 1 thru -2 of theList
	else
		set theList to items 1 thru (r - 1) of theList & items (r + 1) thru -1 of theList
	end if
end repeat

return newList
end randomizeList

-- Set function to get count of tasks and shuffle those counting numbers
on run {}

-- Get current OF data and minimize main window
tell application "OmniFocus"
	if miniaturized of window 1 is false then set miniaturized of window 1 to true
	tell content of front document window of default document to set TreeList to (value of every leaf)
end tell

set myList to {}
repeat with B from 1 to count of TreeList
	set myList to myList & B
end repeat

set randomizedList to randomizeList(myList)
--return randomizedList


tell application "OmniFocus"
	tell content of front document window of default document to set TreeList to (value of every leaf)
	
	repeat with randomNumber in randomizedList
		with timeout of 86400 seconds
			set ListItem to item randomNumber of TreeList
			set ContextName to name of context of ListItem as text
			set TaskName to " - " & name of ListItem
			set my_dialog to (display dialog "Task: " & ContextName & TaskName buttons {"Complete", "Skip", "Cancel"} default button "Complete")
			set button_result to button returned of my_dialog
			if (button_result = "Cancel") then return
			if (button_result = "Complete") then set the completed of ListItem to true
		end timeout
	end repeat
	
	display dialog "All done! " buttons {"Finish"} default button "Finish"
	activate
	set index of window 2 to 2
	
end tell
end run
1 Like

Thanks, Alex!

This script is almost what I am looking for. I have been trying to modify it to replace the “Skip” button that will defer the task for 1 hour. I can’t figure out what I am doing wrong.

on run {}
	tell application "OmniFocus"
		tell content of front document window of default document to set TreeList to (value of every leaf)
		repeat with ListItem in TreeList
			set ProjectName to name of containing project of ListItem as text
			set TaskName to " - " & name of ListItem
			set NoteName to " - " & note of ListItem
			display dialog ProjectName & TaskName & NoteName buttons {"Complete", "Delay 1 Hour", "Cancel"} default button "Cancel"
			if (result = {button returned:"Cancel"}) then return
			if (result = {button returned:"Complete"}) then set completed of ListItem to true
			if (result = {button returned:"Delay 1 Hour"}) then set defer date to ((current date) + (1 * 0 * 0))
		end repeat
	end tell
end run

I keep getting the error “The variable result is not defined.”

Any insight would be very welcome. Thank you!

Hey Willtmc,

I’m not much of an Applescript guy, but I’d think it comes down to “(current date)” variable (as that appears to be the only variable you’ve added or modified).

The other option would be that the “(1 * 0 * 0)” part is wrong and thus giving “defer date” an invalid format.

I don’t know enough to say if either of those are right or wrong, lol, but I’d look there at least. Let me know if you find out, that’s a useful addition (perhaps including both “skip” and “delay” as two separate options?).

One other thing - maybe try just “Delay” (as the other options are also just one word) - maybe the spaces are messing with it somehow?

Alex

I suspect that you need to set the defer date in a more sophisticated manner. You may need to pull the current time and add 1 hour as 3600 seconds. Do a search on “set date and time via applescript” to find insights and code.


JJW

Looks like you’ve not defined result anywhere. Try changing your display dialog line to this:

set result to display dialog ProjectName & TaskName & NoteName buttons {"Complete", "Delay 1 Hour", "Cancel"} default button "Cancel"

Thanks to your help I have made some progress. Strangely, I have been able to produce a script that neither returns an error nor does what I want it to.

on run {}
	tell application "OmniFocus"
		tell content of front document window of default document to set TreeList to (value of every leaf)
		repeat with ListItem in TreeList
			set ProjectName to name of containing project of ListItem as text
			set TaskName to " - " & name of ListItem
			set NoteName to " - " & note of ListItem
			display dialog ProjectName & TaskName & NoteName buttons {"Complete", "Delay 1 Hour", "Cancel"} default button "Cancel"
			set Button_Returned to button returned of result
			
			if Button_Returned = {button returned:"Cancel"} then
				return
			else if Button_Returned = {button returned:"Complete"} then
				set completed of ListItem to true
			else if Button_Returned = {button returned:"Delay 1 Hour"} then
				set defer date to ((current date) + (1 * 0 * 0))
			end if
		end repeat
	end tell
end run

I’ve been testing the “Completed” button. It doesn’t seem to do anything. No error but the task is not marked completed. It advances to the next task.

I must be doing something obvious incorrectly. Ideas?

Without really testing it, I can only suggest and guess.

  • Put a display dialog statement within the else if portion of the code to see if it even gets to that step
  • Perhaps the syntax must be “set the completed of ListItem to true”


JJW

2020 updates:

Removals:
-The code to print the context no longer worked and I didn’t care so I removed it entirely.

Additions:

  • The pop-up now includes how many tasks remain (it just manually counts down by one each time you hit ‘complete’ on a tasks).
  • It also includes the project name on the pop-up.

Bug fixes:

  • The “mark complete” code has been updated to work with OF’s new script back-end changes.

if (button_result = “Complete”) then mark complete ListItem

– Set function for shuffling
on randomizeList(theList)
set listCount to count of theList

	set newList to {}
	repeat listCount times
		set subListCount to count of theList
		set r to random number from 1 to subListCount
		set end of newList to item r of theList
		
		-- remove the random item from theList
		if subListCount is 1 then
			exit repeat
		else if r = 1 then --> first item
			set theList to items 2 thru end of theList
		else if r = subListCount then --> last item
			set theList to items 1 thru -2 of theList
		else
			set theList to items 1 thru (r - 1) of theList & items (r + 1) thru -1 of theList
		end if
	end repeat
	
	return newList
end randomizeList

-- Set function to get count of tasks and shuffle those counting numbers
on run {}
	
	-- Get current OF data and minimize main window
	tell application "OmniFocus"
		if miniaturized of window 1 is false then set miniaturized of window 1 to true
		
		tell content of front document window of default document to set TreeList to (value of every leaf)
	end tell
	
	set myList to {}
	repeat with B from 1 to count of TreeList
		set myList to myList & B
	end repeat
	
	
	set randomizedList to randomizeList(myList)
	
	set TasksRemaining to (count of TreeList)
	
	tell application "OmniFocus"
		tell content of front document window of default document to set TreeList to (value of every leaf)
		
		
		repeat with randomNumber in randomizedList
			with timeout of 86400 seconds
				set ListItem to item randomNumber of TreeList
				set ProjectName to name of containing project of ListItem as text
				set TaskName to name of ListItem
				set TasksRemainingText to TasksRemaining as text
				set my_dialog to (display dialog TasksRemainingText & ") " & ProjectName & ": " & TaskName buttons {"Complete", "Skip", "Cancel"} default button "Complete")
				set button_result to button returned of my_dialog
				
				if (button_result = "Complete") then set TasksRemaining to TasksRemaining - 1
				
				if (button_result = "Cancel") then return
				if (button_result = "Complete") then mark complete ListItem
			end timeout
		end repeat
		
		
		display dialog "All done! " buttons {"Finish"} default button "Finish"
		activate
		set index of window 2 to 2
		
	end tell
	
	
end run
1 Like

Hi Alex,

May be useful… Minus (https://minus.io ) is a Mac app that can help you focus on one task at a time.

Minus.app has a feature called Task Bar which reads from OmniFocus Pro and displays your topmost task on the desktop using a small text strip. This is helpful for recalling what task you are working on as you switch around the Mac.
Task Bar has a built-in timer, and a pause button, celebrations, and other attention-grabbing features.

You can also drag tasks from OmniFocus to Minus.app whenever you need additional concentration. If your OmniFocus task has a time estimate, the Minus Task Bar will read that also and start its timer automatically.

I am one of the creators of Minus. Here to help!

Thank you. Daniel

Hey Daniel - I love the concept, but the website (https://minus.io) doesn’t seem to want to load on Chrome or Safari (too many redirects, it says). Anything odd happening over there?

Alex