Google App Engine
Feedback on this document

Getting Started with Google Tasks API on Google App Engine

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

  1. Install Google App Engine SDK for Python for local testing and deployment of your application.
  2. Install Mercurial to check out a repository.
  3. 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:

  1. Create a new directory named mytasks. This will contain your new application.

  2. In the mytasks directory, create a file named app.yaml with the following contents:

    application: mytasks
    version: 1
    runtime: python27
    api_version: 1
    threadsafe: true
    
    handlers:
    - url: .*
      script: main.application
    

    For more information, see Python Application Configuration.

  3. In the mytasks directory, create a file named main.py with the following contents:

    import webapp2
    
    class MainHandler(webapp2.RequestHandler):
      def get(self):
        self.response.write('Hello App Engine!')
    
    application = webapp2.WSGIApplication([('/', MainHandler)], debug=True)
    
  4. Run dev_appserver.py mytasks.

  5. Point your browser to http://localhost:8080.

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

  1. Go to the Google APIs Console.
  2. Create a new project called mytasks.
  3. Turn on the Google Tasks API.
  4. Click API Access.
  5. Click Create an OAuth 2.0 client ID.
  6. In the Product Name field enter mytasks.
  7. Select scheme http:// (do not use https://).
  8. Fill hostname with localhost.
  9. Click Create client ID.
  10. In the mytasks directory, create a new file named settings.py with 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.

  1. Follow the Google APIs Client Library for Python install instructions.

  2. Download the file named google-api-python-client-gae-N.M.zip from the list of downloads.

  3. Unzip that file into the mytasks directory.

  4. Replace the current contents of main.py with 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)
    
  5. Go to http://localhost:8080.

  6. Login.

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

  1. Go to the Google App Engine Administration Console.

  2. Create a new application or choose an existing one to re-use.

  3. Choose an Application Identifier.

  4. Update application in app.yaml with your new Application Identifier.

    application: your_app_id
    version: 1
    runtime: python27
    api_version: 1
    threadsafe: true
    
    handlers:
    - url: .*
      script: main.application
    
  5. Go to the Google APIs Console.

  6. Click API Access.

  7. Click Edit settings.

  8. Add the following Authorized Redirect URIs, replacing your_app_id with your Application Identifier.

    http://your_app_id.appspot.com/oauth2callback
    https://your_app_id.appspot.com/oauth2callback

  9. Run google_appengine/appcfg.py update mytasks/.

  10. Go to https://your_app_id.appspot.com.

  11. Login.

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

  1. Update settings.py by changing the scope to readonly:

    CLIENT_ID='PASTE_YOUR_GENERATED_CLIENT_ID_HERE'
    CLIENT_SECRET='PASTE_YOUR GENERATED_CLIENT_SECRET_HERE'
    SCOPE='https://www.googleapis.com/auth/tasks.readonly'

  2. Update main.py with 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)
    
  3. In the mytasks directory, create a file named templates/index.html with 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" %}
                &#x2713;
                {% else %}
                &nbsp;
                {% endif %}
              </span>
            </span>
            <a class="title" title="{{ task.title }}">{{ task.title_short }}</a>
          </li>
          {% endfor %}
        </ul>
      </body>
    </html>
    
  4. In the mytasks directory, create a file named css/style.css with 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;
    }
    
  5. Update app.yaml with 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
    
  6. Deploy your application by following the instructions in Step 4.

  7. Go to https://your_app_id.appspot.com You should see a well-designed lists of tasks.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.