Johan Euphrosine
June 2011
Updated January 2013 by Danny Hermes
This article shows how to build a simple Python App Engine application that retrieves a list of tasks with the Google Tasks API. Google Tasks stores tasks that users enter via GMail, their mobile device, or calendar. The Google Tasks API provides developers with a powerful set of API endpoints for searching, reading, and updating Google Tasks content and metadata.
During this tutorial you will learn how to:
- Create a simple App Engine app using the Google APIs Client Library for Python.
- Provide Google Tasks users a mechanism to authorize your app to access their tasks.
- Make authorized requests to the Google Tasks API.
The source code for the final step of this tutorial is available in Google APIs Client Library for Python samples directory.
Before You Start
- Install Google App Engine SDK for Python for local testing and deployment of your application.
- Install Mercurial to check out a repository.
- Get the Google APIs Client Library for Python. This library supports the Google Tasks API.
Step 1: Create Your App Engine Project
First we need to create a web application:
-
Create a new directory named
mytasks. This will contain your new application. -
In the
mytasksdirectory, create a file namedapp.yamlwith the following contents:application: mytasks version: 1 runtime: python27 api_version: 1 threadsafe: true handlers: - url: .* script: main.applicationFor more information, see Python Application Configuration.
-
In the
mytasksdirectory, create a file namedmain.pywith the following contents:import webapp2 class MainHandler(webapp2.RequestHandler): def get(self): self.response.write('Hello App Engine!') application = webapp2.WSGIApplication([('/', MainHandler)], debug=True) -
Run
dev_appserver.py mytasks. -
Point your browser to http://localhost:8080.
-
You will see
Hello App Engine!in the browser window.
Step 2: Register Your Application
Next, you need to register your application so it can make authorized requests on behalf of other users. For more information see Google APIs Console help.
- Go to the Google APIs Console.
- Create a new project called
mytasks. - Turn on the Google Tasks API.
- Click API Access.
- Click Create an OAuth 2.0 client ID.
- In the Product Name field enter
mytasks. - Select scheme
http://(do not usehttps://). - Fill hostname with
localhost. - Click Create client ID.
-
In the
mytasksdirectory, create a new file namedsettings.pywith the following contents:CLIENT_ID='PASTE_YOUR_GENERATED_CLIENT_ID_HERE' CLIENT_SECRET='PASTE_YOUR GENERATED_CLIENT_SECRET_HERE' SCOPE='https://www.googleapis.com/auth/tasks'
This file will be imported and used by your authorization code. Replace the above placeholder values using those you've just generated.
Step 3: Add the Google Tasks API
Next, we need to import and use Google API Python client modules in order to retrieve the list of tasks for the current user.
-
Follow the Google APIs Client Library for Python install instructions.
-
Download the file named
google-api-python-client-gae-N.M.zipfrom the list of downloads. -
Unzip that file into the
mytasksdirectory. -
Replace the current contents of
main.pywith the following:import webapp2 from apiclient.discovery import build from oauth2client.appengine import OAuth2Decorator import settings decorator = OAuth2Decorator(client_id=settings.CLIENT_ID, client_secret=settings.CLIENT_SECRET, scope=settings.SCOPE) service = build('tasks', 'v1') class MainHandler(webapp2.RequestHandler): @decorator.oauth_required def get(self): tasks = service.tasks().list(tasklist='@default').execute( http=decorator.http()) self.response.write('<html><body><ul>') for task in tasks['items']: self.response.write('<li>%s</li>' % task['title']) self.response.write('</ul></body><html>') application = webapp2.WSGIApplication([ ('/', MainHandler), (decorator.callback_path, decorator.callback_handler()), ], debug=True) -
Go to http://localhost:8080.
-
Login.
-
Authorize. After authorizing, you will see the list of tasks from the default task list of this user.
You've now completed your first Google Tasks API application and you are ready to deploy to Google App Engine.
Step 4: Deploy Your Application
Finally, we will create a new Application Identifier for your app, and deploy it to Google App Engine.
-
Go to the Google App Engine Administration Console.
-
Create a new application or choose an existing one to re-use.
-
Choose an Application Identifier.
-
Update
applicationinapp.yamlwith your new Application Identifier.application: your_app_id version: 1 runtime: python27 api_version: 1 threadsafe: true handlers: - url: .* script: main.application -
Go to the Google APIs Console.
-
Click API Access.
-
Click Edit settings.
-
Add the following Authorized Redirect URIs, replacing
your_app_idwith your Application Identifier.http://your_app_id.appspot.com/oauth2callback https://your_app_id.appspot.com/oauth2callback
-
Run
google_appengine/appcfg.py update mytasks/. -
Go to
https://your_app_id.appspot.com. -
Login.
-
Authorize. After authorizing, you should see the list of tasks from the default task list of this user.
Congratulations! You've just deployed your first Google Tasks API application to Google App Engine.
Bonus Task: Add Landing Page, Style and Template
Providing a landing page for your application is also important for making a less jarring OAuth experience, letting the user know up front why they are being directed into the OAuth flow before it begins as documented in the Google APIs Client Library for Python documentation.
You need to choose scopes wisely and not request for more permission than your application actually needs.
Lastly, if you'd like to add some polish, add some CSS styling to your HTML. You can use ours below, or create your own.
-
Update
settings.pyby changing the scope toreadonly:CLIENT_ID='PASTE_YOUR_GENERATED_CLIENT_ID_HERE' CLIENT_SECRET='PASTE_YOUR GENERATED_CLIENT_SECRET_HERE' SCOPE='https://www.googleapis.com/auth/tasks.readonly'
-
Update
main.pywith the following contents:import webapp2 from webapp2_extras import jinja2 from apiclient.discovery import build from oauth2client.appengine import OAuth2Decorator import settings decorator = OAuth2Decorator(client_id=settings.CLIENT_ID, client_secret=settings.CLIENT_SECRET, scope=settings.SCOPE) service = build('tasks', 'v1') class MainHandler(webapp2.RequestHandler): def render_response(self, template, **context): renderer = jinja2.get_jinja2(app=self.app) rendered_value = renderer.render_template(template, **context) self.response.write(rendered_value) @decorator.oauth_aware def get(self): if decorator.has_credentials(): result = service.tasks().list(tasklist='@default').execute( http=decorator.http()) tasks = result.get('items', []) for task in tasks: task['title_short'] = truncate(task['title'], 26) self.render_response('index.html', tasks=tasks) else: url = decorator.authorize_url() self.render_response('index.html', tasks=[], authorize_url=url) def truncate(s, l): return s[:l] + '...' if len(s) > l else s application = webapp2.WSGIApplication([ ('/', MainHandler), (decorator.callback_path, decorator.callback_handler()), ], debug=True) -
In the
mytasksdirectory, create a file namedtemplates/index.htmlwith the following contents:<!doctype html> <html> <head> <link href="/css/style.css" rel="stylesheet" type="text/cs"> <link href="http://fonts.googleapis.com/css?family=Coming+Soon:regular" rel="stylesheet" type="text/css"> </head> <body> <ul id="tasklist"> {% if tasks|length == 0 %} <li> <span class="check"> <span> </span> </span> {% if authorize_url %} <a class="title" title="Grant read access to your tasks" href="{{ authorize_url }}">Grant Access</a> {% else %} <a class="title" title="Add more tasks in Gmail" href="http://mail.google.com/">Add More Tasks</a> {% endif %} </li> {% endif %} {% for task in tasks %} <li class="{{ task.status }}"> <span class="check"> <span> {% if task.status == "completed" %} ✓ {% else %} {% endif %} </span> </span> <a class="title" title="{{ task.title }}">{{ task.title_short }}</a> </li> {% endfor %} </ul> </body> </html> -
In the
mytasksdirectory, create a file namedcss/style.csswith the following contents:body { width: 600px; margin: auto; margin-top: 48px; margin-bottom: 48px; font-family: 'Coming Soon', serif; font-size: 35px; } ul { -moz-border-radius: 20px; -webkit-border-radius: 20px; border-radius: 20px; background-color: #f8f6c6; background-image: -webkit-linear-gradient(#fdfce8, #f8f6c6); list-style-type: none; padding: 0; margin: 0; border: 5px solid #e3e2be; -moz-box-shadow: 8px 8px 8px #ccc; -webkit-box-shadow: 8px 8px 8px #ccc; box-shadow: 8px 8px 8px #ccc; } li { padding-left: 20px; border-bottom: 4px solid #e3e2be; } li .check { display: block; float: left; border-right: 8px solid #e3e2be; padding-right: 20px; width: 20px; } li .title { margin-left: 4px; padding-left: 20px; border-left: 4px solid #e3e2be; color: black; text-decoration: none; } li:last-child { border-bottom: none; } -
Update
app.yamlwith the following contents:application: mytasks version: 1 runtime: python27 api_version: 1 threadsafe: true handlers: - url: /css static_dir: css - url: .* script: main.application libraries: - name: jinja2 version: latest -
Deploy your application by following the instructions in Step 4.
-
Go to
https://your_app_id.appspot.comYou should see a well-designed lists of tasks.