The Omni Group Forums

The Omni Group Forums (http://forums.omnigroup.com/index.php)
-   OmniFocus Extras (http://forums.omnigroup.com/forumdisplay.php?f=44)
-   -   Applescript to Show Active Tasks (http://forums.omnigroup.com/showthread.php?t=24979)

DrJJWMac 2012-07-24 03:55 AM

Applescript to Show Active Tasks
 
1 Attachment(s)
This script reports "active" tasks, defined as those that are flagged and not completed. The output format is as

HEADER (COUNT)
BAR
PRE-STRING context SEPARATER task
...

Action groups can be displayed expanded or not, whereby in the latter case they show as

Project Group SEPARATER group name <COUNT>

Tasks with empty contexts show as

PRE-STRING <empty> SEPARATER task

The report can be sorted forward or reverse, and counts can be hidden. I use this with GeekTool -- caveat OF must be running for it to work. The script is attached in an archive.

[code]
(*
This script creates a list of flagged actions and reports them in (reverse) order by context

Report Format
NOHEADER
or
HEADER (COUNT)
BAR
PRESTRING context SEPARATOR task
...
*)

property reverseSort : "true" -- set to false for forward sort
property showCount : "true" -- set to false to avoid showing count
property expandGroup : "true" -- set to true to expand action groups
property headerStr : "Active" -- HEADER text when active count > 0
property nheaderStr : "No Active Tasks" -- NOHEADER when active count = 0
property mtcntxtStr : "<empty>" -- string when context is missing
property barStr : "--" -- BAR string after header
property preStr : "●" & tab -- PRESTRING prior to each list item
property actGroupStr : "Project Group" -- pre-string when task is action group (shows when expandGroup = false)
property sepStr : " : " -- SEPARATOR string between context and task

tell application "OmniFocus"
tell front document
-- get list of active tasks
set AList to flattened tasks where ((flagged is true) and (completed is false))
if ((count of AList) > 0) then
-- make the header
set theCount to (count of AList)
set SortedList to ""
set theReport to ""
-- start parsing each task
repeat with ListItem in AList
if (the number of tasks of ListItem = 0) then
-- a single task
try
set theContext to preStr & (name of context of ListItem) & sepStr
on error
set theContext to preStr & mtcntxStr & sepStr
end try
set theAction to (name of ListItem)
set SortedList to SortedList & (theContext & theAction) as list
else
-- an action group (no expand or expand)
if (expandGroup is "false") then
-- do not expand action groups
if (showCount is "true") then
set countStr to " <" & (number of tasks of ListItem) & ">"
else
set countStr to ""
end if
set theContext to preStr & actGroupStr & sepStr
set theAction to (name of containing project of ListItem) & sepStr & (name of ListItem) & countStr
set SortedList to SortedList & (theContext & theAction) as list
else
-- expand action groups completely
set XList to ((flattened tasks of ListItem) where (completed is false))
set theCount to theCount - 1
repeat with XListItem in XList
try
set theContext to preStr & (name of context of XListItem) & sepStr
on error
set theContext to preStr & mtcntxtStr & sepStr
end try
set theAction to (name of XListItem)
set theCount to theCount + 1
set SortedList to SortedList & (theContext & theAction) as list
end repeat
end if
end if
end repeat
if (reverseSort is "true") then
set SortedList to the reverse of my simple_sort(the SortedList)
else
set SortedList to my simple_sort(the SortedList)
end if
repeat with ListItem in SortedList
set theReport to theReport & (ListItem) & return
end repeat
else
set theReport to nheaderStr
end if
end tell
if (showCount is "true") then
set countStr to " (" & theCount & ")"
else
set countStr to ""
end if
set theReport to headerStr & countStr & return & barStr & return & theReport
return theReport
end tell

on simple_sort(my_list)
set the index_list to {}
set the sorted_list to {}
repeat (the number of items in my_list) times
set the low_item to ""
repeat with i from 1 to (number of items in my_list)
if i is not in the index_list then
set this_item to item i of my_list as text
if the low_item is "" then
set the low_item to this_item
set the low_item_index to i
else if this_item comes before the low_item then
set the low_item to this_item
set the low_item_index to i
end if
end if
end repeat
set the end of sorted_list to the low_item
set the end of the index_list to the low_item_index
end repeat
return the sorted_list
end simple_sort
[/code]

RobTrew 2012-07-24 06:25 AM

If you'd like to get at the raw data without needing OmniFocus to be running, you could write something along these lines (to get sorted rows of three tab-delimited fields).

( [I]man sort[/I] in Terminal.app will list the other switches for the bash [I]sort[/I] command )

[CODE]property pstrSQL : quoted form of "
SELECT p.name, c.name, tt.name
FROM ((task t left join projectinfo pi on t.containingprojectinfo=pi.pk) tt
left join task p on tt.task=p.persistentIdentifier)
left join context c on tt.context = c.persistentIdentifier
WHERE tt.flagged=1 and tt.dateCompleted is null"

tell application id "MACS" to set strID to (get id of application file id "OFOC")
set strDBPath to "~/Library/Caches/" & strID & "/OmniFocusDatabase2"
set strLines to do shell script "sqlite3 " & strDBPath & space & pstrSQL & " | tr '|' '\\t' | sort -k 1,2"[/CODE]

[COLOR="White"]--[/COLOR]

DrJJWMac 2012-07-24 10:08 AM

I can just about follow what is happening ... the SQLite commands are stored, the datapath is stored, the commands are assembled, and the result is sent through a shell pipe.

Nice to watch a master at work here!

Thanks.

--
JJW

RobTrew 2012-07-24 10:51 AM

No mastery at all, but you are right – the sqlite approach is not very good for legibility or maintenance – especially as code like this is likely to break with OF2

(but echoing lines to the [I]sort[/I] command may prove to be a useful thing to experiment with. Here it's just sorting by columns 1 and 2.)

[COLOR="White"]--[/COLOR]

DrJJWMac 2012-07-25 06:29 AM

2 Attachment(s)
Version 1.1 is attached with following notes:

[code]
(*
version 1.1 (JJW)
-- added ability to hide tasks starting later
-- added ability to show only Next Actions
-- changed to booleans for flags
-- increment count of tasks in all loops
*)
[/code]

This can give an equivalent output to the perspective settings shown below.

--
JJW

RobTrew 2012-07-25 11:02 PM

FWIW a lazy implementation of sorting in Applescript might look like this:

[CODE]on sort(my_list)
set {dlm, my text item delimiters} to {my text item delimiters, linefeed}
set lstSorted to paragraphs of (do shell script "echo " & quoted form of (my_list as text) & " | sort")
set my text item delimiters to dlm
lstSorted
end sort[/CODE]

(you could append the [B]-r[/B] switch for a reversed sort)

[COLOR="White"]--[/COLOR]

RobTrew 2012-07-25 11:12 PM

i.e.

[CODE]set lstReversed to paragraphs of (do shell script "echo " & quoted form of (my_list as text) & " | sort -r")[/CODE]

DrJJWMac 2012-07-26 05:27 AM

[QUOTE=RobTrew;112979]FWIW a lazy implementation of sorting in Applescript might look like this: ... [/QUOTE]

Thank you. That's not really lazy IMO, actually it is rather clever to use a shell sort. I figured it was possible somehow, I just cobbled in what I found as the first reasonable result after a Web search for Applescript sorting routines. This is also useful to see how to capture returns from shell commands.

--
JJW

RobTrew 2012-07-29 07:45 AM

You can do simple formatting (Markdown bulleted lists with headers and sub-headers, for example) by further piping through [I]awk[/I] from Applescript

[CODE]property pstrSQL : quoted form of "
SELECT c.name, p.name, tt.name
FROM ((task t left join projectinfo pi on t.containingprojectinfo=pi.pk) tt
left join task p on tt.task=p.persistentIdentifier)
left join context c on tt.context = c.persistentIdentifier
WHERE tt.flagged=1 and tt.dateCompleted is null
ORDER BY c.name, p.name"

tell application "Finder" to set strDBPath to "~/Library/Caches/" & (id of application file id "OFOC") & "/OmniFocusDatabase2"
set strReport to (do shell script "sqlite3 " & strDBPath & space & pstrSQL & " | tr '|' '\\t' | awk '
BEGIN {FS=\"\\t\"; ctx=0; prj=0} # Initialise File Separator and header variables
{
# Output an MD header whenever 1st col value [CONTEXT] changes,
if (ctx!=$1) {ctx=$1; if (length(ctx) < 1) {print \"\\n##(no context)\"}
else {print \"\\n##\" ctx}};

# and output an MD bulleted project name whenever 2nd col value [PROJECT] changes,
if (prj!=$2) {prj=$2; if (length(prj) < 1) {print \"\\n* Inbox\"}
else {print \"\\n* \" prj}};

# and output column 3 [TASK] (with an MD sub-bullet) if it differs from col 2
if ($3!=$2) {print \"\\t* \" $3}
}'")[/CODE]

[COLOR="White"]--[/COLOR]

RobTrew 2012-07-29 10:46 AM

Or simple text formatting from a pure shell script, for NerdTool, GeekTool, Terminal.app etc, without needing OmniFocus to be running.

[CODE]#!/bin/sh
#OFOC=$(osascript -e 'tell application "Finder" to get id of application file id "OFOC"')
OFOC="com.omnigroup.OmniFocus" # For Appstore-purchased variant use (slower) line above instead
echo -e "FLAGGED AND NOT COMPLETED:\n"
sqlite3 $HOME/Library/Caches/$OFOC/OmniFocusDatabase2 '
SELECT c.name, p.name, tt.name
FROM ((task t left join projectinfo pi on t.containingprojectinfo=pi.pk) tt
left join task p on tt.task=p.persistentIdentifier)
left join context c on tt.context = c.persistentIdentifier
WHERE tt.flagged=1 and tt.dateCompleted is null
ORDER BY c.name, p.name' | awk '
BEGIN {FS="\|"; prj=0; ctx=0;}
{
# Output a [CONTEXT] header whenever 1st col value changes,
if (ctx!=$1) {ctx=$1; if (length(ctx) < 1) {print "(no context)"}
else {print "[" ctx "]"}};
# and output a project name whenever 2nd col value [PROJECT] changes,
if (prj!=$2) {prj=$2; if (length(prj) < 1) {print "\t(Inbox)"}
else {print "\t" prj}};
# and output column 3 [TASK] if it differs from col 2
if ($3!=$2) {print "\t• " $3}
}'[/CODE]


All times are GMT -8. The time now is 04:52 PM.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2024, vBulletin Solutions, Inc.