Editorial Workflows - Send TaskPaper Tasks to Reminders

Apr 02, 2014

With the advent of Fantastical 2 for iPad, I thought it would be as good a time as any to write about these two little Editorial Workflows I have. As I've mentioned before, I'm using Reminders to keep track of my time sensitive tasks. If something needs to be done on a certain day at a certain time, then it'll find it's way to Reminders.

When coming across such a task, I add the tag @remind with an optional date and time. When I trigger the workflow, it runs through the current TaskPaper document, identifies any task with that tag and recursively adds it to Reminders with the same method I covered in a previous post.

They're still very raw and need much refinement, but given I've found some use for them in my daily workflow, you may too? In time, I'll no doubt improve on them to add more features, error testing and possibly some form @done status sync.

Currently, there are two workflows. One uses Fantastical 2 and the other GoodTask. Each workflow allows you to define a few settings:

Notes — If you activate this, then any notes and tags that the TaskPaper task has will be added to the Reminder's body;

Time — If no time is specified in the task's tag, then the script will use this time as the default;

List — The Reminders list to which the task will be added;

Tag — The tag used. You can choose which best suits your needs.

Fantastical

Get the Fantastical workflow here. Given that the script that does all the heavy lifting can't be seen on the Editorial Workflow site, I'm adding it here for clarities sake. You'll no doubt notice that the script for Fantastical is much simpler and versatile. That's thanks to Fantastical's excellent Natural Language Parsing engine

#coding: utf-8importworkflowimportreimporturllib2importdatetimePARAMS=workflow.get_parameters()DEFAULT_TIME=PARAMS['Time']DEFAULT_LIST=PARAMS['List']ADD_NOTES=PARAMS['Notes']TAG=PARAMS['Tag']defstripTags(task):"""Strip tags from a task item and return the resulting list. Will ommit the remind tag."""#Strip remind tagtp_task=re.sub('@remind(\(\d{4}-\d{2}-\d{2}\s*((|\d{2}:\d{2}))\)|)','',task,re.M)# Get list of remaining tagstp_task=re.finditer("\@\w+(\(\d{4}-\d{2}-\d{2}\s*((|\d{2}:\d{2}))\)|)",tp_task,re.M)return[tags.group()fortagsintp_task]deftaskTime(task):"""Will processo the date/time value in the remind tag and create a proper date/time string to use when creating the task"""tp_task=re.search('(?<[email protected]\().*(?=\))',task,re.M)dt=tp_task.group().split(" ")# Get the datetry:date_str=datetime.datetime.strptime(dt[0],'%Y-%m-%d').strftime('%d-%m-%Y')exceptIndexError,errmsg:return"There is no valid date defined."# Get the timetry:time_str=dt[1]exceptIndexError,errmsg:time_str=DEFAULT_TIMEreturn(date_str,time_str)defmain():arg=workflow.get_input().encode('utf-8')tag_pattern=re.compile("((?!.*@done).*"+TAG+".*(\n*\t*(?!\w.*:$)\w.*)*)",re.M)task_pattern=re.compile('\s{1}@\w+(\(\d{4}-\d{2}-\d{2}\s*((|\d{2}:\d{2}))\)|)',re.M)notes_pattern=re.compile("^-.*\n*|\t")# Isolate tasks with @remind tagtp_remind=re.findall(tag_pattern,arg)# Iterate over every task tagged and process it.url_str=''formatchintp_remind:# Process Tasksrc_task=re.sub("- ","",re.search("^-.*",match[0].strip()).group())task=re.sub(task_pattern,'',src_task)# Process task timedt=taskTime(src_task)# Prepate notes if Notes == True, else ommit notes from Reminder.ifADD_NOTES:# Get list of tags from task without remind tagtag_str=' '.join([tagfortaginstripTags(src_task)ifstripTags(src_task)])# Prepare Notesnotes=(re.sub(notes_pattern,"",match[0].strip())).strip()# Create body of taskiftag_strandnotes:notes_str=notes.strip()+'\n'+tag_str.strip()eliftag_strandnotnotes:notes_str=tag_str.strip()elifnotesandnottag_str:notes_str=notes.strip()else:notes_str=''else:notes_str=''# Add the task to Reminders.task_str='%s on %s at %s /%s'%(task,str(dt[0]),str(dt[1]),DEFAULT_LIST)ifurl_str=='':url_str+='fantastical2://x-callback-url/parse?sentence='+urllib2.quote(task_str,'')+'&notes='+urllib2.quote(notes_str)+'&reminder=1'else:url_str='fantastical2://x-callback-url/parse?sentence='+urllib2.quote(task_str,'')+'&notes='+urllib2.quote(notes_str,'')+'&reminder=1&x-success='+urllib2.quote(url_str,'')workflow.set_output(url_str)if__name__=='__main__':main()

GoodTasks

Once again, this workflow uses a custom action and therefore the script isn't visible on the workflow site, but you can get a glimpse here before downloading if you prefer.

#coding: utf-8importworkflowimportreimporturllib2importdatetimePARAMS=workflow.get_parameters()DEFAULT_TIME=PARAMS['Time']DEFAULT_LIST=PARAMS['List']ADD_NOTES=PARAMS['Notes']TAG=PARAMS['Tag']defstripTags(task):"""Strip tags from a task item and return the resulting list. Will ommit the remind tag."""#Strip remind tagtp_task=re.sub('@remind(\(\d{4}-\d{2}-\d{2}\s*((|\d{2}:\d{2}))\)|)','',task,re.M)# Get list of remaining tagstp_task=re.finditer("\@\w+(\(\d{4}-\d{2}-\d{2}\s*((|\d{2}:\d{2}))\)|)",tp_task,re.M)return[tags.group()fortagsintp_task]deftaskTime(task):"""Will processo the date/time value in the remind tag and create a proper date/time string to use when creating the task"""tp_task=re.search('(?<[email protected]\().*(?=\))',task,re.M)dt=tp_task.group().split(" ")# Get the datetry:date_str=dt[0]exceptIndexError,errmsg:return"There is no valid date defined."# Get the timetry:time_str=dt[1]exceptIndexError,errmsg:time_str=DEFAULT_TIMEreturnstr(date_str)+' '+str(time_str)defmain():arg=workflow.get_input().encode('utf-8')tag_pattern=re.compile("((?!.*@done).*"+TAG+".*(\n*\t*(?!\w.*:$)\w.*)*)",re.M)task_pattern=re.compile('\s{1}@\w+(\(\d{4}-\d{2}-\d{2}\s*((|\d{2}:\d{2}))\)|)',re.M)notes_pattern=re.compile("^-.*\n*|\t")# Isolate tasks with @remind tagtp_remind=re.findall(tag_pattern,arg)# Iterate over every task tagged and process it.url_str=''formatchintp_remind:# Process Tasksrc_task=re.sub("- ","",re.search("^-.*",match[0].strip()).group())task=re.sub(task_pattern,'',src_task)# Process task timedt=taskTime(src_task)# Prepate notes if Notes == True, else ommit notes from Reminder.ifADD_NOTES:# Get list of tags from task without remind tagtag_str=' '.join([tagfortaginstripTags(src_task)ifstripTags(src_task)])# Prepare Notesnotes=(re.sub(notes_pattern,"",match[0].strip())).strip()# Create body of taskiftag_strandnotes:notes_str=notes.strip()+'\n'+tag_str.strip()eliftag_strandnotnotes:notes_str=tag_str.strip()elifnotesandnottag_str:notes_str=notes.strip()else:notes_str=''else:notes_str=''# Add the task to Reminders.task_str='goodtask://x-callback-url/add?text=%s&list=%s&due=%s&alarm=1&notes=%s' \
%(urllib2.quote(task),DEFAULT_LIST,urllib2.quote(dt),urllib2.quote(notes_str))ifurl_str=='':url_str=task_strelse:url_str=task_str+'&x-success='+urllib2.quote(url_str,'')workflow.set_output(url_str)if__name__=='__main__':main()

I sincerely hope you too find the workflows useful and let me know if you come across any odd behavior.