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 sync OF perspective with todo.txt app in Android, windows, etc. (http://forums.omnigroup.com/showthread.php?t=28277)

kingsinger 2013-02-14 11:09 AM

Applescript to sync OF perspective with todo.txt app in Android, windows, etc.
 
Moving this over here. Posted it by accident in the applying forum.

I'm an Android phone and tablet user. So I've been on a quest for a while to find the best way to get my OF tasks into my phone.

For the last few weeks, I've been using this great applescript by OF user William McKeehan to sync a perspective with the todo.txt app for android: [URL="http://mckeehan.homeip.net/computers/omnifocus/index.htm"]http://mckeehan.homeip.net/computers/omnifocus/index.htm[/URL]

The user experience isn't on par with the OF iOS apps, but this is best approach I've found so far (at least for me).

Todo.txt doesn't present the task info in the nicest fashion visually, but you do get most of the important stuff in each line item and you can filter by project/context, flagged, etc.

It also has dropbox sync built-in, so when you do a sync, it will update the todo.txt file and push the newest info to your phone.

If you are running an android device that has 4.0 or greater on it, there's another app called simpletask, which is a fork of todo.txt with holo theming and a few other goodies. It's currently free too, whereas todo.txt is $1.99.

So in either case, it's not a huge investment of cash to see if this system could work for you.

When you mark an action item completed in todo.txt, the script will mark it as complete in omni when you run a sync with it. If you create new items in todo.txt, the script will also add these times to omni (including info about project/context, etc., if you have added them). Items without a project, are added to your inbox. Items with a project, should get added at the bottom the project's task list.

The comments of the script provide additional detail on this.

Todo.txt has a simple syntax for entering items, because it's a plain text task management app (the syntax is similar to taskpaper). Projects get a "+" in front of them. contexts get an "@".

If you have flagged items in OF, they come into todo.txt as a context called @flagged, so you can filter on that.

The script also automatically replaces spaces in any OF context or project with underscores, so that todo.txt can properly parse each line item for contexts and projects (e.g. "@Tom's_House" or +Bob_Smith_Sales_Presentation).

Without the underscores, todo.txt just thinks the first word is the project or context.

Going back in the other direction, the script removes the underscores for you.

All in all, it's not a bad way to go, as it also has the added benefit of making your todo items available on a windows machine via dropbox and a program like todotxt.net (actually, because the file is plain text, you can look at your todo items using any text editor or maybe even a web browser in a pinch).

If there's a downside other than the look of the items themselves in the todo.txt app, it's that the script can be a little slow.

But at least for me, that hasn't been a huge issue. You might even be able to automate the syncing with a Hazel rule if you're into that. If so, it might make sense to add a script step at the end of AS that closes OF, unless you want it to just stay open all the time on one of your machines.

Anyway, William has done some great work here imho. If anyone uses the script and finds it helpful, I'm sure he'd appreciate hearing that feedback. The same goes for any bugs you encounter.

Best,

KS

luminosity 2013-02-15 09:00 AM

Excellent. Appreciated.
 
Thanks for posting this information. In the absence of an Android version of OF, this might be the best workaround I have found thus far. Cheers.

luminosity 2013-02-15 11:43 AM

Hi KS,

I have been unable to get the script from William to work. The text versions when pasted into AppleScript Editor gives me an error, and the .scrpt version is compiled in some way, so I can't modify the path to the todo.txt file.

I couldn't find a way on William's site to contact him. I've sent him a direct tweet, but no idea if he reads them.

Any suggestions? I am very keen to take this workaround for a spin.

Thanks...

whpalmer4 2013-02-15 02:02 PM

He's got some gibberish characters in the text version, probably the wrong encoding or something. They are for things like the <= character. I was able to download the scpt file without difficulty and open it in the Applescript Editor.

See if this works any better for you...

[code]
(*
This script will read the and parse lines in the todo.txt file.
OmniFocus tasks whose ID matches a completed task in todo.txt will be marked as complete in OmniFocus.
A task will be created in OmniFocus for each task in todo.txt which do not have an OmniFocus ID.
The contents of the todo.txt file will be replaced with an export from the "Active Export" perspective (which should not be grouped).
If growl is available, a message will be displayed informing the user when the process finishes.

This expects to have a perspective setup in OmniFocus called "Active Export" which is an uncategorized list of all tasks that you would like to "sync" to todo.txt

The only items synced from todo.txt to OmniFocus are new tasks and tasks that are marked as done. For everything else, OmniFocus as the system of record and the data stored there will overwrite the data in todo.txt.

Known Issues:
If you create a project in todo.txt, it will not be created in OF.
If a project name in OF has a new-line in its name, tasks added to that project in todo.txt will not be added to that project in OF.
The only things that are synced from todo.txt to OF are new taks, and tasks marked as done.
New-lines and spaces are replaced with an underscore character in projects and contexts. This can cause issues if your projects or contexts have underscores in them.
New-lines are replaced with a space in task names (from OF to todo.txt)
The OF ID must be the last item on the line in todo.txt to be parsed properly.
'@' and '+' are used to identify contexts and projects in todo.txt - having these characters in either project names, contexts or task names will cause them to be interpreted as contexts and projects in todo.txt and not part of the name
*)

-- ---------- --------- ----------
-- settings
-- ---------- --------- ----------
set ofPerspectiveName to "ActiveExport"
set todoTxtPath to "/Users/whp4/Dropbox/todo/todo.txt"

-- ---------- --------- ----------
-- begin the main routine
-- ---------- --------- ----------

--get current items in todo.txt
open for access todoTxtPath
set todoData to paragraphs of (read todoTxtPath)
close access todoTxtPath

--look at each item from todo.txt and update the corresponding task
--in OmniFocus or add the item if it does not exist in OmniFocus
repeat with taskLine in todoData
if length of taskLine > 0 then
set t to makeTask(taskLine)
set taskName to taskName of t
set taskId to taskId of t
--already exists in omnifocus if it has an ID
if taskId is not equal to "" then
if done of t then --if todo.txt has it marked as done, mark it as done in OmniFocus
markDoneInOmniFocus(taskName, taskId)
end if
else
--does not exist in omnifocus, so create it
createOmniFocusTask(t)
end if
end if
end repeat

--pull items from OmniFocus and genereate a new list of tasks for todo.txt
set todoTaskList to {}
tell application "OmniFocus"
tell the default document to tell the front document window
set perspective name to ofPerspectiveName
set oTrees to trees of content
set n to count of oTrees
repeat with i from 1 to count of oTrees
set t to value of (item i of oTrees)
set my todoTaskList's end to my createTaskFromOFTask(i, t)
end repeat
end tell
end tell

--write the todo's out to a new file
set nf to open for access todoTxtPath & "-new" with write permission
set eof nf to 0 --erase the file in case it's there already

repeat with t in todoTaskList
if not waiting of t then
write (getLine() of t) to nf
write "
" to nf
end if
end repeat
close access nf

--move the new file into place -- backup the original first
do shell script "mv " & quote & todoTxtPath & quote & " " & quote & todoTxtPath & "-old" & quote
do shell script "mv " & quote & todoTxtPath & "-new" & quote & " " & quote & todoTxtPath & quote

--
growl("Sync with todo.txt script complete")


-- ---------- --------- ----------
-- end of the main routine
-- ---------- --------- ----------





(*
This is essentially a class of "task" that can be used to parse a todo.txt line or build a todo.txt line
*)
on makeTask(taskLine)
script task
property tmpLine : ""
property taskId : ""
property taskName : ""
property done : false
property creationDate : ""
property doneDate : ""
property dueDate : "" --not currently used
property startDate : "" --not currently used
property priority : ""
property tprojects : {} --a list of projects to which the todo.txt task is assigned
property tcontexts : {} --a list of contexts to which the todo.txt task is assigned
property waiting : false --not currently implemented

--parse a line from a todo.txt file
on parseLine(inputLine)
set my tmpLine to inputLine
parseDone()
parsePriority()
parsecreationDate()
parseProject()
parseContext()
parseId()
set my taskName to tmpLine
end parseLine

--generate a line suitable for inclusion in a todo.txt file
on getLine()
set newLine to ""
if my done then
set newLine to "x " & doneDate
else if my priority is not equal to "" then
set newLine to "(" & priority & ") "
end if

if my creationDate is not equal to "" then
set newLine to newLine & creationDate & " "
end if


set newLine to newLine & replaceSubString(my taskName, "
", " ")

--technically, tasks from OF will not have multiple projects, but since todo.txt CAN have multiple projects assigned to one task, I assume that in all cases.
repeat with p in replaceStringInList(replaceStringInList(my tprojects, " ", "_"), "
", "_")
set newLine to newLine & " +" & p
end repeat

--see above
repeat with c in replaceStringInList(replaceStringInList(my tcontexts, " ", "_"), "
", "_")
set newLine to newLine & " @" & c
end repeat

if my startDate is not equal to "" then
set newLine to newLine & " start:" & my startDate
end if


if my dueDate is not equal to "" then
set newLine to newLine & " due:" & my dueDate
end if
--add the OF task ID to the end of the line
set newLine to newLine & " $ID:" & my taskId

return newLine
end getLine

on parseDone()
set d to character 1 of my tmpLine
if d is equal to "x" then
set my done to true
set my tmpLine to (characters 3 thru -1 of my tmpLine) as string
parseDoneDate()
else
set my done to false
end if
end parseDone

on parseDoneDate()
if my done then
--done date will be the next caracters after x space of the following format yyyy-mm-dd
set my doneDate to (characters 1 thru 10 of my tmpLine) as string
set my tmpLine to (characters 12 thru -1 of my tmpLine) as string
end if
end parseDoneDate

on parsecreationDate()
--I really should do some validation around these dates that I'm pulling out
set my creationDate to (characters 1 thru 10 of my tmpLine) as string
set my tmpLine to (characters 12 thru -1 of my tmpLine) as string
end parsecreationDate

on parsePriority()
if character 1 of my tmpLine is equal to "(" and character 3 of my tmpLine is equal to ")" and character 4 of my tmpLine is equal to " " then
set my priority to character 2 of my tmpLine
set my tmpLine to (characters 5 thru -1 of my tmpLine) as string
else
set my priority to ""
end if
end parsePriority

--split on the delimiter of " $ID:", the part before that is the task line, the part after is the ID, so ID must be the last thing on the line.
on parseId()
-- save delimiters to restore old settings
set oldDelimiters to AppleScript's text item delimiters
-- set delimiters to delimiter to be used
set AppleScript's text item delimiters to " $ID:"
-- create the array
set theArray to every text item of my tmpLine
-- restore the old setting
set AppleScript's text item delimiters to oldDelimiters
if length of theArray = 2 then
set my taskId to item 2 of theArray
set my tmpLine to item 1 of theArray
end if
end parseId

--used to get all projects and contexts from a todo.txt line.
on getListWithSep(sep)
set myList to {}
set s to offset of sep in my tmpLine
repeat while s > 1
--grab the first part of the line - up to the project start mark
set fst to (characters 1 thru (s - 1) of my tmpLine) as string
--set the remainder of the line as the part after the start mark
set rem to (characters (s + 2) thru -1 of my tmpLine) as string
--project name ends with a space (technically any whitespace)
set e to offset of " " in rem
if e > 0 then
--the end was found
--set the last part of the line as the part after the project title
set lst to (characters e thru -1 of rem) as string
set e to e - 1
set myValue to (characters 1 thru e of rem) as string
set myList's end to myValue
else
--else the end is the end of the string
set e to -1
set lst to ""
set myValue to (characters 1 thru e of rem) as string
set myList's end to myValue
end if

--remove the project string from the line
set my tmpLine to fst & lst
set s to 0 offset of sep in my tmpLine
end repeat
return myList
end getListWithSep

on parseProject()
set projects to getListWithSep(" +")
set my tprojects to replaceStringInList(projects, "_", " ")
end parseProject

on parseContext()
set contexts to getListWithSep(" @")
set my tcontexts to replaceStringInList(contexts, "_", " ")
end parseContext

on replaceStringInList(inList, fromChar, toChar)
set outList to {}
-- save delimiters to restore old settings
set oldDelimiters to AppleScript's text item delimiters

repeat with i in inList
-- set delimiters to delimiter to be used
set AppleScript's text item delimiters to fromChar
set tmpList to every text item of i
set AppleScript's text item delimiters to toChar
set iName to tmpList as string
set outList's end to iName
end repeat
-- restore the old setting
set AppleScript's text item delimiters to oldDelimiters

return outList
end replaceStringInList

on replaceSubString(i, fromChar, toChar)
set output to ""
-- save delimiters to restore old settings
set oldDelimiters to AppleScript's text item delimiters

-- set delimiters to delimiter to be used
set AppleScript's text item delimiters to fromChar
set tmpList to every text item of i
set AppleScript's text item delimiters to toChar
set output to tmpList as string

-- restore the old setting
set AppleScript's text item delimiters to oldDelimiters

return output
end replaceSubString

end script

if length of taskLine > 0 then
tell task to parseLine(taskLine)
end if
return task
end makeTask

--given a "task" object, create a corresponding task in OF.
on createOmniFocusTask(task)
set taskName to taskName of task

set con to missing value
set taskFlagged to false
if length of (tcontexts of task) > 0 then
repeat with c in (tcontexts of task)
if ("" & c) is equal to "flagged" then
set taskFlagged to true
else
if con is equal to missing value then
set con to c
end if
end if
end repeat
end if

if length of (tprojects of task) > 0 then
set prj to first item of (tprojects of task)
else
set prj to missing value
end if

set userChoice to display dialog "Add " & taskName & " to OmniFocus?" buttons {"YES", "NO"}
if button returned of userChoice is equal to "YES" then
tell application "OmniFocus"
tell default document
set inboxTask to true
if prj is not equal to missing value then
set theProject to first flattened project where name is prj
if theProject is not equal to missing value then
set inboxTask to false
end if
end if
if inboxTask is equal to false then
tell theProject
set t to make new task with properties {name:taskName}
end tell
else
set t to make new inbox task with properties {name:taskName}
end if

if con is not equal to missing value then
set mycontext to (first flattened context where name is con)
if mycontext is not equal to missing value then
set context of t to mycontext
end if
end if

if taskFlagged is equal to true then
set flagged of t to true
end if

end tell
end tell
end if
end createOmniFocusTask

on markDoneInOmniFocus(taskName, taskId)
tell application "OmniFocus"
tell default document
set foundTasks to (flattened tasks whose id is taskId)
if number of items in foundTasks is equal to 1 then
set completed of (first item in foundTasks) to true
else
display dialog "Could not identify which task titled \"" & taskName & "\" to mark as complete!"
end if
end tell
end tell
end markDoneInOmniFocus


--create a todo.txt task from an OmniFocus task
on createTaskFromOFTask(ofPriority, ofTask)
using terms from application "OmniFocus"
--I use priority D-Z allowing for A-C (the ones that get color and are at the top) to be selected manually
set priorityString to "DEFGHIJKLMNOPQRSTUVWXYZ"
set t to makeTask("")
if ofPriority ≤ length of priorityString then
set priority of t to (character ofPriority of priorityString)
end if
set taskId of t to id of ofTask
if (name of ofTask) is not equal to missing value then
set taskName of t to name of ofTask
end if
if (creation date of ofTask) is not equal to missing value then
set creationDate of t to convertDateToString(creation date of ofTask)
end if
if (start date of ofTask) is not equal to missing value then
set startDate of t to convertDateToString(start date of ofTask)
end if
if (due date of ofTask) is not equal to missing value then
set dueDate of t to convertDateToString(due date of ofTask)
end if
if (context of ofTask) is not equal to missing value then
set (tcontexts of t)'s end to name of context of ofTask
end if
if flagged of ofTask then
set (tcontexts of t)'s end to "flagged"
end if
if (containing project of ofTask) is not equal to missing value then
set conDoc to (containing project of ofTask)
set n to name of conDoc
set (tprojects of t)'s end to n
end if

return t
end using terms from
end createTaskFromOFTask

--convert an AppleScript date object to a string in the format of yyyy-mm-dd
on convertDateToString(inDate)
if inDate is not missing value then
set y to year of inDate
set mm to (characters -2 thru -1 of ("00" & (month of inDate as number))) as string
set dd to (characters -2 thru -1 of ("00" & (day of inDate))) as string
return y & "-" & mm & "-" & dd
else
return ""
end if
end convertDateToString

on growl(message)
tell application "System Events"
set isRunning to (count of (every process whose bundle identifier is "com.Growl.GrowlHelperApp")) > 0
end tell

if isRunning then
tell application id "com.Growl.GrowlHelperApp"
-- Make a list of all the notification types
-- that this script will ever send:
set the allNotificationsList to ¬
{"Sync Complete Notification", "Sync Failed Notification"}

-- Make a list of the notifications
-- that will be enabled by default.
-- Those not enabled by default can be enabled later
-- in the 'Applications' tab of the growl prefpane.
set the enabledNotificationsList to ¬
{"Sync Complete Notification"}

-- Register our script with growl.
-- You can optionally (as here) set a default icon
-- for this script's notifications.
register as application ¬
"Sync with todo.txt" all notifications allNotificationsList ¬
default notifications enabledNotificationsList ¬
icon of application "Script Editor"

notify with name ¬
"Sync Complete Notification" title ¬
"Sync Complete" description ¬
message application name "Sync with todo.txt"
end tell
end if
end growl

[/code]

luminosity 2013-02-15 02:59 PM

Yes, it is strange about the .scrpt file. AppleScript says this when I try to open it: The document “Sync_with_todo-txt.scpt” could not be opened.

If I open it in a text editor, it's filled with odd characters like:

(Actually, I can't even post the code here, as the characters and throwing the forum into a spin). Needless to say, the are very odd characters and not AppleScript. Very strange it does this. No idea why.

Anyway, I will try the version you have posted. Thank you.

luminosity 2013-02-15 03:05 PM

[QUOTE=whpalmer4;120494]

See if this works any better for you...


if ofPriority ≤ length of priorityString then
set priority of t to (character ofPriority of priorityString)
end if

[/code][/QUOTE]

Very strange. There must be something wrong on my computer. Perhaps a system font that is missing?

That line:
[CODE] if ofPriority ≤ length of priorityString then[/CODE]

Displays as
[CODE] if ofPriority ˛ length of priorityString then
[/CODE]
In my AppleScript editor. The ≤ is replaced with a superscript 2 (˛). What could cause that?

luminosity 2013-02-15 03:09 PM

Okay. Somehow I sorted out the odd characters issue.

Now what I get is this error:
[CODE]Can’t get application id "com.Growl.GrowlHelperApp".[/CODE]

I don't have Growl installed. Although I did on Lion, but not since I upgraded to ML.

Shouldn't it just detect there is no Growl and move on?

luminosity 2013-02-15 03:26 PM

Okay. Resolved the Growl issue by installing an old version of Growl.

Now the issue is that when I run the script it pulls in any data I've put into the text file, but it doesn't add to the text file the tasks in the Active Export perspective I set up. I'll read over the developer's page again and see if I have missed something.

barbelith 2013-03-13 11:13 PM

Error -39
 
Hi, I've tried to run the script but it always give me this error:

error "End of file error." number -39 from file "Macintosh HD:Users:pedro:Dropbox:todo:todo.txt"

The file is created empty, and I have tried giving the file rw permissions also.

I've got mountain lion 10.8.2 installed

sjconnolly 2013-03-25 07:25 AM

trouble with Script
 
I am very interested in this script because I am an android user and have been searching for a good way to work with OF on Android. I have never worked with a script before, but I have been following the instructions. Right now, I am getting this error:

Can’t make characters 1 thru 10 of tmpLine of «script task» into type string.

Can anyone help?


All times are GMT -8. The time now is 03:32 AM.

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