Editor’s Note:This is a guest post from John Watkinson. John is a developer and the co-founder of Docracy, a start-up company that hosts crowd-sourced legal documents and provides free e-signing services. He recently attended, and won first place, at the Google Apps Hackathon in NYC. Here's John's story about the event.
Our company is focused on documents in the cloud, so we attended the Google Apps Developer Hackathon on December 1st in New York City to learn more about the various Google Apps APIs, and how we can best integrate with them. During the initial presentations from Ryan Boyd and Saurabh Gupta of the Google Apps team, we were delighted to learn about Apps Script. It provides a powerful JavaScript interface into Google Apps, including Docs, Calendar, Gmail, Maps and many others.
After the tech presentations, we formed teams and started hacking with the technology. I joined a team with Matt Hall (also from Docracy), Nick Siderakis, Jeff Hsin and Scott Thompson. We struck upon the idea of creating a function that would make calls to Wolfram Alpha via their developer API. It took all five of us hacking away for a few hours to parse the Alpha results properly, but eventually we had an Apps Script custom function for Spreadsheet called “wolf”. It had a kind of magic feeling about it, as you could pass simple English-language queries into the function and get back exact numeric results in your Google Spreadsheet. Some example working queries:
=wolf("distance from the earth to the moon in km")
=wolf("population of canada in 1960")
=wolf("average january temperature in buenos aires")
=wolf("9th digit of pi")
=wolf("count of olympic medals won by japan in 2008")
Any function in Apps Script can be used as a spreadsheet function directly, so our main wolf function is the entry point to our script. We use Apps Script’s UrlFetch service to make our API call to Wolfram Alpha (note, the full URL is truncated for brevity):
wolf
UrlFetch
var response = UrlFetchApp.fetch("http://api.wolframalpha.com/..." + encodeURIComponent(input));
The response from Alpha is an XML file that includes a lot of metadata we don’t need. So, we used a recursive function and the XML manipulation tools in Apps Script to track down the element of interest to us (below is a simplified version):
function findText(e) { if (e.getName().getLocalName() == 'plaintext') { return e.getText(); } else { var children = e.getElements(); for (var i = 0; i < children.length; i++) { var result = findText(children[i]); if (result) return result; } return null; } }
A little bit more work is required to parse the result, as sometimes Alpha will report numbers in words (i.e. “40.7 million”), but otherwise that’s it!
For a small script, it definitely feels pretty powerful! Our little invention earned us a first place finish at the hackathon, and the prizes were little indoor RC-controlled helicopters. I managed to irreparably damage mine in a gruesome crash into my refrigerator already, but I hear that many of the others are still serviceable and flying missions daily.
Once we were comfortable with the Apps Script Services (and aware of its capabilities with spreadsheets in particular), a side project emerged that used some of the other integration features available. Apps Script functions can listen for events that are triggered by the editing of a spreadsheet cell (if the user grants the appropriate permissions). Using these onEdit Event Handlers, we wrote an implementation of the classic game Mine Sweeper that is playable right in the spreadsheet!
You can play the game by making a copy of this spreadsheet and then clicking the menu item SheetSweeper > New Game. When you play the game, the script responds to onEdit events that are dispatched from the spreadsheet.
function onOpen() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var menuEntries = [{name: "New Game", functionName: "startGame"}]; ss.addMenu("SheetSweeper", menuEntries); }
Then when the user edits a cell, the script responds and implements the mine sweeper logic:
function onEdit(event) { var ss = event.source.getActiveSheet(); var cell = ss.getActiveCell(); if (cell.getValue() == 'x') { // Flagging a mine flagCell(cell); } else { // Clearing a cell clearCell(cell); } }
We had a great time at the hackathon, and are happy to have been introduced to Apps Script. We believe it has an exciting future ahead of it.
Are you ever at an airport with no internet access, and realize that you forgot to set your "Out of Office" (OOO) message?" I forget regularly, but always remember to block off my calendar. Why not have the calendar automatically update my OOO message? We can! With the Calendar API, we can retrieve calendar events and set a vacation responder for the event duration for any user in the domain using the Email Settings API.
The first step is to authorize the Calendar and Email Settings client to make any call to the APIs respectively. The new Calendar API requires google-api-client library while the Email settings API still works with the gdata-client library in Python. These libraries use different underlying protocols and handle authorization differently.
Fortunately we can use the same code to get an OAuth 2.0 token for both the Calendar API and the Email Settings API.
from oauth2client.file import Storage from oauth2client.client import AccessTokenRefreshError from oauth2client.client import OAuth2WebServerFlow from oauth2client.tools import run SCOPES = ('https://www.googleapis.com/auth/calendar ' 'https://apps-apis.google.com/a/feeds/emailsettings/2.0/') FLOW = flow_from_clientsecrets('client_secrets.json', scope=SCOPES) storage = Storage('vacation.dat') credentials = storage.get() if credentials is None or credentials.invalid: credentials = run(FLOW, storage)
Now that we have obtained the OAuth 2.0 token from OAuth2WebServerFlow, we can use it in either library. To authorize the Calendar service:
# Create an httplib2.Http object to handle the HTTP # requests and authorize it with our good Credentials. http = httplib2.Http() http = credentials.authorize(http) # Build authorized service for Calendar API service = build('calendar', 'v3', http=http)
Authorizing the Email Settings client requires adapting the credentials:
auth2token = gdata.gauth.OAuth2Token(client_id=client_id, client_secret=client_secret, scope=SCOPE, access_token=credentials.access_token, refresh_token=credentials.refresh_token, user_agent='vacation-responder-sample/1.0') email_client = auth2token.authorize( gdata.apps.emailsettings.client.EmailSettingsClient( domain=domain))
Now its time to update the vacation responder based on calendar event using the authorized client. Lets query for events containing the string ‘vacation’ from the Calendar API. The following code snippet shows the retrieval of events.
# Convert the date to the RFC 3339 timestamp format # for Calendar API. cur_datetime = datetime.datetime.now() cur_date = cur_datetime.strftime('%Y-%m-%dT%H:%M:%SZ') events = service.events().list(calendarId=username, q='vacation', timeMin=cur_date, singleEvents=True, orderBy='startTime').execute()
By specifying a user’s email address as the calendarID, we can query against any primary calendars in our domain that are shared and readable by the administrator. If the user opts to hide their calendar, we won’t find any of their events. For additional details about the query on calendar events, refer to the calendar query parameters from the reference guide.
Next we can obtain the start and end time of the event and update the vacation responder using the Email Settings client if the event’s duration is day long or more.
if 'items' in events: for event in events['items']: startDate = event['start'] endDate = event['end'] # If event is set for the day not just for a time slot. if 'date' in startDate: email_client.UpdateVacation(username=username, enable=True, subject='Out of office', message='If urgent call me.', domain_only=True, start_date=startDate['date'], end_date=endDate['date']) print '\nVacation responder set for the days between %s to %s' % (startDate['date'], endDate['date']) break
You can download the sample and build your own application on top of it. We hope that this sample makes it easier to get started, particularly for apps that need to combine APIs from the two API clients. Please feel free to reach us in the Google Domain Info and Management Forum with any questions you have or write us feedback on this post.
We recently updated the Google Apps Marketplace with several new features to help developers better engage with their customers and improve discoverability of apps in the marketplace.
It’s no secret that engaging your customers and responding to their feedback is critical to success. It’s now possible to engage in conversations with customers based on comments & reviews for your app in the marketplace.
Google Search recently introduced rich snippets for applications several months ago with enhanced search results for applications from marketplaces like Android Market and others. Marketplace apps will soon be appearing as rich snippets with ratings and pricing details.
Lastly, we introduced new home pages for each category in the Marketplace to feature the top installed and newest apps for that category.
We hope that you find value in these changes and wish everyone a happy new year!
2011 was the year of momentum for Google Apps Script. As 2012 dawns upon us, let us take a moment to reflect on the past year.
We started 2011 with a bang! In January we released a cloud-based Debugger into Apps Script’s IDE that proved to be very useful for developers. The Script Editor was upgraded bringing about many features and bug fixes. In March we implemented a very powerful feature of embedding Apps Script in Google Sites pages as Gadgets, making it easy to enhance Sites in amazing ways. We also improved Contacts Services, making it more stable with an improved API.
At Google I/O in May we launched Document Services, Gmail Services and the drag ‘n’ drop GUI Builder. These were major steps forward in making sure that Apps Script provides a full set of APIs to allow developers to build rich workflow and automation solutions.
We were very busy during the summer months preparing for a series of launch for later part of 2011. In September, we launched Charts Services. It allows users to dynamically create Charts and embed them in emails, UiApp or export as images. We also released three Google API services for Prediction, UrlShortener and Tasks APIs.
Lock and Cache Services launched in October. These services are important for building performant and scalable applications. We also improved the Script Editor by adding Project Support and made other UI improvements. November brought about the launch of Client Handlers and Validators. This is only the beginning of our commitment to allow developers to build more advanced UI using Apps Script.
In December we continued to improve the reliability and stability of Apps Script runtime. We capped the year by releasing Groups and Domain Services. And who can forget the very useful AdSense Services for AdSense advertisers!
Throughout the year we expanded our outreach channels. There were Apps Script sessions at Google I/O and Bootcamp, and several attendees got their last minute tickets through the Apps Script I/O challenge. We were at Google Developer Day and DevFest events, met with GTUGs, and hosted hackathons throughout the world. Our blog also featured scripts like Revevol’s Trainer Finder, Corey’s Gmail Snooze, Dave’s Flubaroo, Top Contributor’s Mail Merge, Saqib’s Idea Bank, and Drew’s Calorie Counting that showed the power of Apps Script.
Recently we started Office Hours in G+ hangout. These hangouts proved to be very popular, personal and effective means to share ideas with Apps Script community. Join us some day!
In our efforts to help educators, we worked with a New York city school to help them make most out of Google Apps. We also hosted many EDU focused webinars and workshops. In great Google tradition, Apps Script team participated in CAPE and Google Serve.
2012 is going to be an even more exciting and promising year. Tighten your seat belts because we intend to keep firing on all cylinders!
The recently launched Groups Settings API allows Google Apps domain administrators to write client applications for managing groups in their domain. Once you have created the groups and added members using the Provisioning API, you can use the Groups Settings API to perform actions like:
Let’s have a look at how you can make authorized calls to the Groups Settings API from your client application.
You must enable the Provisioning API to make requests to the Groups Settings API. You can do so by enabling the Provisioning API checkbox in the Domain settings tab of your Google Apps control panel.
Next, ensure that the Google Groups for Business and Email services are added to your domain by going to the Dashboard. If these services are not listed, add them by going to Add more services link next to the Service settings heading.
Now you are set to write client applications. Let's discuss the steps to write an application using the Python client library. You need to install the google-api-python-client library first.
You can register a new project or use an existing one from the APIs console to obtain credentials to use in your application. The credentials (client_id, client_secret) are used to obtain OAuth tokens for authorization of the API requests.
client_id
client_secret
The Groups Settings API supports various authorization mechanisms, including OAuth 2.0. Please see the wiki for more information on using the library’s support for OAuth 2.0 to create a httplib2.Http object. This object is used by the Groups Settings service to make authorized requests.
httplib2.Http
The Python client library uses the Discovery API to build the Groups Settings service from discovery. The method build is defined in the library and can be imported to build the service. The service can then access resources (‘groups’ in this case) and perform actions on them using methods defined in the discovery metadata.
build
The following example shows how to retrieve the properties for the group staff@example.com.
staff@example.com
service = build(“groups”, “v1”, http=http) group = service.groups() g = group.get(groupUniqueId=”staff@example.com”).execute()
This method returns a dictionary of property pairs (name/value):
group_name = g['name'] group_isArchived = g['isArchived'] group_whoCanViewGroup = g['whoCanViewGroup']
The update method can be used to set the properties of a group. Let’s have a look at how you can set the access permissions for a group:
update
body = {'whoCanInvite': ALL_MANAGERS_CAN_INVITE, 'whoCanJoin': ‘INVITED_CAN_JOIN’, 'whoCanPostMessage': ‘ALL_MEMBERS_CAN_POST’, 'whoCanViewGroup': ‘ALL_IN_DOMAIN_CAN_VIEW’ } # Update the properties of group g1 = group.update(groupUniqueId=groupId, body=body).execute()
Additional valid values for these properties, as well as the complete list of properties, are documented in the reference guide. We have recently added a sample in the Python client library that you can refer to when developing your own application. We would be glad to hear your feedback or any queries you have on the forum.
Though developers are quite comfortable thinking in abstractions, we still find a lot of value in code examples and fully developed sample applications. Judging by the volume of comments, tweets, and git checkouts of the Au-to-do sample code we released a few weeks ago, our readers agree.
For Google Apps API developers who want to get started writing mobile apps, here’s a great new resource: a sample application that integrates Google Calendar API v3 with Android and illustrates best practices for OAuth 2.0. This meeting scheduler app built by Alain Vongsouvanh gets the user’s contact list, lets users select attendees, and matches free/busy times to suggest available meeting times. It provides a simple Android UI for user actions such as attendee selection:
The sample code for Meeting Scheduler demonstrates many key aspects of developing with Google Apps APIs. After authorizing all required access using OAuth 2.0, the Meeting Scheduler makes requests to the Calendar API. For example, the class FreeBusyTimesRetriever queries the free/busy endpoint to find available meeting times:
FreeBusyTimesRetriever
FreeBusyRequest request = new FreeBusyRequest(); request.setTimeMin(getDateTime(startDate, 0)); request.setTimeMax(getDateTime(startDate, timeSpan)); for (String attendee : attendees) { requestItems.add(new FreeBusyRequestItem().setId(attendee)); } request.setItems(requestItems); FreeBusyResponse busyTimes; try { Freebusy.Query query = service.freebusy().query(request); // Use partial GET to retrieve only needed fields. query.setFields("calendars"); busyTimes = query.execute(); // ... } catch (IOException e) { // ... }
In this snippet above, note the use of a partial GET request. Calendar API v3, along with many other new Google APIs, provides partial response with GET and PATCH to retrieve or update only the data fields you need for a given request. Using these methods can help you streamline your code and conserve system resources.
For the full context of all calls that Meeting Scheduler makes, including OAuth 2.0 flow and the handling of expired access tokens, see Getting Started with the Calendar API v3 and OAuth 2.0 on Android. The project site provides all the source code and other resources, and there’s a related wiki page with important configuration details.
We hope you’ll have a look at the sample and let us know what you think in the Google Apps Calendar API forum. You’re welcome to create a clone of the source code and do some Meeting Scheduler development of your own. If you find a bug or have an idea for a feature, don’t hesitate to file it for evaluation.
All developers agree that saving bandwidth is a critical factor for the success of a mobile application. Less data usage means faster response times and also lower costs for the users as the vast majority of mobile data plans are limited or billed on a per-usage basis.
When using any of the Google Data APIs in your application, you can reduce the amount of data to be transferred by requesting gzip-encoded responses. On average, the size of a page of users returned by the Provisioning API (100 accounts) is about 100 Kb, while the same data, gzip-encoded, is about 5 Kb -- 20 times smaller! When you can reduce data sizes at this dramatic scale, the benefits of compression will often outweigh any costs in client-side processing for decompression.
Enabling gzip-encoding in your application requires two steps. You have to edit the User-Agent string to contain the value gzip and you must also include an Accept-Encoding header with the same value. For example:
User-Agent
gzip
Accept-Encoding
User-Agent: my application - gzip Accept-Encoding: gzip
Client libraries for the various supported programming languages make enabling gzip-encoded responses even easier. For instance, in the .NET client library it is as easy as setting the boolean UseGZip flag of the RequestFactory object:
service.RequestFactory.UseGZip = true;
For any questions, please get in touch with us in the respective forum for the API you’re using.