From: Patrick Brennan <pbrennan@gmail.com>
To: Patrick Brennan <pbrennan@gmail.com>, emacs-orgmode@gnu.org
Subject: Re: Google Tasks Integration
Date: Tue, 31 Jan 2012 10:31:03 -0800 [thread overview]
Message-ID: <CAB5xGOGw1Z6TGycpVJUACpq6eY-Mkx3Agt9J3QSSwQxPrF8oow@mail.gmail.com> (raw)
In-Reply-To: <8762fs9fsk.fsf@pinto.chemeng.ucl.ac.uk>
[-- Attachment #1.1: Type: text/plain, Size: 2358 bytes --]
Hi Eric,
Here is the script. It's pretty simple. There is no facility for
synchronizing an org-mode file as yet. I think the best way to do the sync
would be to do it when pushing an org-mode file to Google Tasks.
Tell me how you like this and if it works well for you.
Patrick
PS:
** Requirements:
- Python 2.6. This is the version I am using, and I am not sure if an
earlier
version will work.
- setuptools. This is necessary in order to install the Google API for
Python.
Get setuptools from here:
http://pypi.python.org/pypi/setuptools#downloads
I downloaded the egg package
(
http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086
),
set it to +x and run it
sudo ./setuptools-0.6c11-py2.6.egg
- Google Tasks API for Python.
Use easy_install per this page:
http://code.google.com/p/google-api-python-client/wiki/Installation
sudo easy_install --upgrade google-api-python-client
Thanks,
Patrick
On Tue, Jan 31, 2012 at 5:12 AM, Eric S Fraga <e.fraga@ucl.ac.uk> wrote:
> Patrick Brennan <pbrennan@gmail.com> writes:
>
> > This weekend, while trying to avoid doing any real work, I started
> noodling
> > around with the Google Tasks API and I got a respectable distance toward
> a
> > script which will read your Google Tasks and export them to Org-mode.
> > Currently it will capture the task title, the notes, the todo status
> (TODO
> > or DONE) and the hierarchy, i.e. child tasks will be correctly placed
> under
> > their parents. There's still a lot of polish to apply, and of course,
> there
> > is no bidirectional capability as yet. Still, I wanted to send out this
> > notice in case anyone wanted to compare notes or thought it might be an
> > interesting application to share. The mobile apps for Google Tasks are
> > quite good, and if I can get a really good export going, I think this
> will
> > actually provide a plausible alternative workflow to the existing
> MobileOrg
> > flow.
> >
> > Patrick
>
> I would be very interested in this. I have links to and from google
> calendar for appointments but could not figure out how to do either
> direction for TODO items.
>
> Thanks,
> eric
>
> --
> : Eric S Fraga (GnuPG: 0xC89193D8FFFCF67D) in Emacs 24.0.92.1
> : using Org-mode version 7.8.03 (release_7.8.03.283.g171ea)
>
>
[-- Attachment #1.2: Type: text/html, Size: 3661 bytes --]
[-- Attachment #2: TasksToOrgMode.py --]
[-- Type: text/x-python, Size: 9410 bytes --]
#! /usr/bin/python
import gflags
import httplib2
import math
import re
from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.tools import run
########################################################################
# Algorithm from pg 7 of Peter Duffet-Smith,
# Practical Astronomy With Your Calculator, 3d ed.
# Check against http://www.onlineconversion.com/julian_date.htm
# and <Orbiter distribution>/Utils/Date.exe
# This version does not check for Gregorian calendar or BC!!!
# This version does not handle fractional days!!!
def JulianDate (Year,Month,Day,modified=False):
if (Month == 1)or(Month == 2):
Yprime = Year - 1
Mprime = Month + 12
else:
Yprime = Year
Mprime = Month
A = math.floor(Yprime / 100.0)
B = 2.0 - A + math.floor(A / 4.0)
C = math.floor(365.25 * Yprime)
D = math.floor(30.6001 * (Mprime + 1.0))
# Julian Day
JD = B + C + D + Day + 1720994.5
# Modified Julian Day
MJD = B + C + D + Day - 679006.0
if modified:
return MJD
else:
return JD
########################################################################
# Given a year, month, and day, compute the day of the week
def DayOfWeek(Year,Month,Day):
dayNames = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
mjd = JulianDate(Year,Month,Day,True)
diff = mjd - 15019.0
# dayIndex = math.floor(diff) % 7
dayIndex = int(diff) % 7
return dayNames[dayIndex]
########################################################################
# Given a string which represents a date and time, e.g.
# '2012-01-30T23:59:00Z' RFC 3339 timestamp
# Format it into a Org-Mode date.
def formatDateTimeStringToOrgMode(DateTimeString,angleBrackets=True):
prog = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(.{1})')
match = prog.match(DateTimeString)
Year = int(match.group(1))
Month = int(match.group(2))
Day = int(match.group(3))
ShortDayName = DayOfWeek(Year,Month,Day)
Hour = int(match.group(4))
Minute = int(match.group(5))
Second = int(match.group(6))
TimeZone = match.group(7) # TODO: Use the timezone somehow
if angleBrackets:
openBracket = '<'
closeBracket = '>'
else:
openBracket = '['
closeBracket = ']'
retString = '%s%04d-%02d-%02d %s %02d:%02d:%02d%s' % \
(openBracket, \
Year, Month, Day, ShortDayName, Hour, Minute, Second, \
closeBracket)
return retString
########################################################################
# A simple utility function to print out a given attribute of a dict.
def printDictAttributeIfExists(dict,label,attr):
try:
print label + dict[attr]
except KeyError:
pass
########################################################################
# Print out all the fields of a task
def printTask(task):
printDictAttributeIfExists(task," Task : ", 'title')
printDictAttributeIfExists(task," Kind : ", 'kind')
printDictAttributeIfExists(task," id : ", 'id')
printDictAttributeIfExists(task," etag : ", 'etag')
printDictAttributeIfExists(task," updated : ", 'updated')
printDictAttributeIfExists(task," selfLink : ", 'selfLink')
printDictAttributeIfExists(task," position : ", 'position')
printDictAttributeIfExists(task," parent : ", 'parent')
printDictAttributeIfExists(task," notes : ", 'notes')
printDictAttributeIfExists(task," status : ", 'status')
printDictAttributeIfExists(task," due : ", 'due')
printDictAttributeIfExists(task," completed: ", 'completed')
printDictAttributeIfExists(task," deleted : ", 'deleted')
printDictAttributeIfExists(task," hidden : ", 'hidden')
print
########################################################################
# Given a list of nodes, which may themselves contain a list of nodes
# in their children attribute, find a node which has the given id and
# return it.
def findNode(nodeList, nodeId):
for i in range(len(nodeList)):
if (nodeList[i]['id'] == nodeId):
return nodeList[i]
if (len(nodeList[i]['children']) > 0):
rv = (findNode(nodeList[i]['children'], nodeId))
if (rv != False):
return rv
return False
########################################################################
# Given a list of nodes representing tasks, print out the tree in
# Org-mode format.
def printNodeList(nodeList, depth=0):
stars = '*'
for i in range(depth):
stars = stars + '*'
spaces = ' '
for i in range(depth):
spaces = spaces + ' '
for i in range(len(nodeList)):
node = nodeList[i]
if (node['status'] == 'needsAction'):
status = ' TODO '
elif (node['status'] == 'completed'):
status = ' DONE '
else:
status = ' '
try:
deadline = node['due']
except KeyError:
deadline = False
try:
notes = node['notes']
except KeyError:
notes = False
# TODO: Add completed date
try:
completed = node['completed']
except KeyError:
completed = False
# For recording when the item was closed, or the deadline
dates_line = ''
print stars + status + node['title']
if (deadline or completed):
dates_line = dates_line + spaces
if (completed):
dates_line = dates_line + \
'CLOSED: ' + formatDateTimeStringToOrgMode(completed,False)
if (completed and deadline):
dates_line = dates_line + ' '
if (deadline):
dates_line = dates_line + 'DEADLINE: ' + \
formatDateTimeStringToOrgMode(deadline)
if (dates_line != ''):
print dates_line
# Properties. TODO: Any others?
print spaces + ':PROPERTIES:'
print spaces + ':ID:' + ' ' + node['id']
print spaces + ':UPDATED:' + ' ' + node['updated']
print spaces + ':END:'
if (notes):
noteslines = notes.split('\n')
for line in noteslines:
print spaces + line
printNodeList(node['children'], depth+1)
FLAGS = gflags.FLAGS
# Set up a Flow object to be used if we need to authenticate. This
# sample uses OAuth 2.0, and we set up the OAuth2WebServerFlow with
# the information it needs to authenticate. Note that it is called
# the Web Server Flow, but it can also handle the flow for native
# applications
# The client_id and client_secret are copied from the API Access tab on
# the Google APIs Console
FLOW = OAuth2WebServerFlow(
client_id='218130629659.apps.googleusercontent.com',
client_secret='4/kMrW-AHZalDLvMnJlWTopIR2E1Cx',
scope='https://www.googleapis.com/auth/tasks',
user_agent='TasksToOrgMode/1.0')
# To disable the local server feature, uncomment the following line:
FLAGS.auth_local_webserver = False
# If the Credentials don't exist or are invalid, run through the native client
# flow. The Storage object will ensure that if successful the good
# Credentials will get written back to a file.
storage = Storage('tasks.dat')
credentials = storage.get()
if credentials is None or credentials.invalid == True:
credentials = run(FLOW, storage)
# Create an httplib2.Http object to handle our HTTP requests and authorize it
# with our good Credentials.
http = httplib2.Http()
http = credentials.authorize(http)
# Build a service object for interacting with the API. Visit
# the Google APIs Console
# to get a developerKey for your own application.
service = build(serviceName='tasks', version='v1', http=http)
# Get all of the task lists
tasklists = service.tasklists().list().execute()
# Loop through the returned task lists
for tasklist in tasklists['items']:
# Create a flat list of all the tasks.
orgTaskList = {}
orgTaskList['title'] = tasklist['title']
orgTaskList['children'] = []
# print 'Task List Title = ' + tasklist['title']
# print 'Task List id = ' + tasklist['id']
tasklist_id = tasklist['id']
tasks = service.tasks().list(tasklist=tasklist_id).execute()
# Loop through the tasks, add them to a flat list of tasks
for task_item in tasks['items']:
task_item_id = task_item['id']
task = service.tasks().get(tasklist=tasklist_id,task=task_item_id).execute()
task['children'] = []
# print "Adding task " + task['title']
orgTaskList['children'].append(task)
# Now take tasks and add them to their correct parents
i = 0
while (i<len(orgTaskList['children'])):
try:
parentId = orgTaskList['children'][i]['parent']
except KeyError:
# no parent
parentId = ''
i = i + 1
if (parentId != ''):
parentNode = findNode(orgTaskList['children'], parentId)
if (parentNode):
parentNode['children'].append(orgTaskList['children'].pop(i))
print orgTaskList['title']
print
printNodeList(orgTaskList['children'])
next prev parent reply other threads:[~2012-01-31 18:31 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-01-30 19:33 Google Tasks Integration Patrick Brennan
2012-01-30 21:47 ` Philipp Haselwarter
2012-01-31 13:12 ` Eric S Fraga
2012-01-31 18:31 ` Patrick Brennan [this message]
2012-01-31 19:11 ` Simon Thum
2012-01-31 19:10 ` Eric S Fraga
2012-02-01 20:35 ` Simon Thum
2012-02-02 21:34 ` Matthew Jones
2012-02-03 21:45 ` Eric S Fraga
2012-01-31 15:18 ` Mehul Sanghvi
2012-01-31 19:07 ` Simon Thum
-- strict thread matches above, loose matches on Subject: below --
2011-08-31 13:33 Google Tasks integration Brad Collette
2011-09-03 17:44 ` Sven Bretfeld
2011-09-06 16:09 ` Robert Goldman
2011-10-22 10:23 ` Bastien
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.orgmode.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CAB5xGOGw1Z6TGycpVJUACpq6eY-Mkx3Agt9J3QSSwQxPrF8oow@mail.gmail.com \
--to=pbrennan@gmail.com \
--cc=emacs-orgmode@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).