The Omni Group
These forums are now read-only. Please visit our new forums to participate in discussion. A new account will be required to post in the new forums. For more info on the switch, see this post. Thank you!

Go Back   The Omni Group Forums > OmniFocus > OmniFocus Extras
FAQ Members List Calendar Search Today's Posts Mark Forums Read

 
Applescript to sync OF perspective with todo.txt app in Android, windows, etc. Thread Tools Search this Thread Display Modes
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: http://mckeehan.homeip.net/computers...ocus/index.htm

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
 
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.

Last edited by luminosity; 2013-02-15 at 10:26 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...
 
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
 
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.
 
Quote:
Originally Posted by whpalmer4 View Post

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]
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
Displays as
Code:
		if ofPriority ˛ length of priorityString then
In my AppleScript editor. The ≤ is replaced with a superscript 2 (˛). What could cause that?
 
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".
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?
 
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.
 
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
 
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?
 
 


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes


Similar Threads
Thread Thread Starter Forum Replies Last Post
Is there a way to sync Omnifocus to my Android Phone? ssavanna OmniFocus Syncing 2 2013-05-22 04:24 AM
Applescript to sync OF perspective with todo.txt app in Android, windows, etc. kingsinger Applying OmniFocus 0 2013-02-13 09:50 PM
Android/Lotus Notes Sync? Sartori OmniFocus Syncing 3 2010-02-25 06:50 AM
OF 1.7.3 perspective independancy of windows Jupiter OmniFocus 1 for Mac 2 2009-09-14 11:10 AM
Perspective windows jumping about BevvyB OmniFocus 1 for Mac 5 2009-08-28 10:19 AM


All times are GMT -8. The time now is 09:07 PM.


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