Automatically generate Samples wiki page from README files.
Reviewed in http://codereview.appspot.com/5900069/.
Index: samples-index.py
===================================================================
new file mode 100644
diff --git a/Makefile b/Makefile
index 8d6962b..3786b7f 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,11 @@
docs:
cd docs; ./build.sh
python describe.py
+ python samples-index.py ../google-api-python-client.wiki/SampleApps.wiki
+
+.PHONY: wiki
+wiki:
+ python samples-index.py > ../google-api-python-client.wiki/SampleApps.wiki
.PHONY: prerelease
prerelease:
diff --git a/samples-index.py b/samples-index.py
new file mode 100644
index 0000000..35e1c16
--- /dev/null
+++ b/samples-index.py
@@ -0,0 +1,229 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Build wiki page with a list of all samples.
+
+The information for the wiki page is built from data found in all the README
+files in the samples. The format of the README file is:
+
+
+ Description is everything up to the first blank line.
+
+ api: plus (Used to look up the long name in discovery).
+ keywords: appengine (such as appengine, oauth2, cmdline)
+
+ The rest of the file is ignored when it comes to building the index.
+"""
+
+import httplib2
+import itertools
+import json
+import os
+import re
+
+http = httplib2.Http('.cache')
+r, c = http.request('https://www.googleapis.com/discovery/v1/apis')
+if r.status != 200:
+ raise ValueError('Received non-200 response when retrieving Discovery document.')
+
+# Dictionary mapping api names to their discovery description.
+DIRECTORY = {}
+for item in json.loads(c)['items']:
+ if item['preferred']:
+ DIRECTORY[item['name']] = item
+
+# A list of valid keywords. Should not be taken as complete, add to
+# this list as needed.
+KEYWORDS = {
+ 'appengine': 'Google App Engine',
+ 'oauth2': 'OAuth 2.0',
+ 'cmdline': 'Command-line',
+ 'django': 'Django',
+ 'threading': 'Threading',
+ 'pagination': 'Pagination'
+ }
+
+
+def get_lines(name, lines):
+ """Return lines that begin with name.
+
+ Lines are expected to look like:
+
+ name: space separated values
+
+ Args:
+ name: string, parameter name.
+ lines: iterable of string, lines in the file.
+
+ Returns:
+ List of values in the lines that match.
+ """
+ retval = []
+ matches = itertools.ifilter(lambda x: x.startswith(name + ':'), lines)
+ for line in matches:
+ retval.extend(line[len(name)+1:].split())
+ return retval
+
+
+def wiki_escape(s):
+ """Detect WikiSyntax (i.e. InterCaps, a.k.a. CamelCase) and escape it."""
+ ret = []
+ for word in s.split():
+ if re.match(r'[A-Z]+[a-z]+[A-Z]', word):
+ word = '!%s' % word
+ ret.append(word)
+ return ' '.join(ret)
+
+
+def context_from_sample(api, keywords, dirname, desc):
+ """Return info for expanding a sample into a template.
+
+ Args:
+ api: string, name of api.
+ keywords: list of string, list of keywords for the given api.
+ dirname: string, directory name of the sample.
+ desc: string, long description of the sample.
+
+ Returns:
+ A dictionary of values useful for template expansion.
+ """
+ if api is None:
+ return None
+ else:
+ entry = DIRECTORY[api]
+ context = {
+ 'api': api,
+ 'version': entry['version'],
+ 'api_name': wiki_escape(entry.get('title', entry.get('description'))),
+ 'api_desc': wiki_escape(entry['description']),
+ 'api_icon': entry['icons']['x32'],
+ 'keywords': keywords,
+ 'dir': dirname,
+ 'dir_escaped': dirname.replace('/', '%2F'),
+ 'desc': wiki_escape(desc),
+ }
+ return context
+
+
+def keyword_context_from_sample(keywords, dirname, desc):
+ """Return info for expanding a sample into a template.
+
+ Sample may not be about a specific sample.
+
+ Args:
+ keywords: list of string, list of keywords for the given api.
+ dirname: string, directory name of the sample.
+ desc: string, long description of the sample.
+
+ Returns:
+ A dictionary of values useful for template expansion.
+ """
+ context = {
+ 'keywords': keywords,
+ 'dir': dirname,
+ 'dir_escaped': dirname.replace('/', '%2F'),
+ 'desc': wiki_escape(desc),
+ }
+ return context
+
+
+def scan_readme_files(dirname):
+ """Scans all subdirs of dirname for README files.
+
+ Args:
+ dirname: string, name of directory to walk.
+
+ Returns:
+ (samples, keyword_set): list of information about all samples, the union
+ of all keywords found.
+ """
+ samples = []
+ keyword_set = set()
+
+ for root, dirs, files in os.walk(dirname):
+ if 'README' in files:
+ filename = os.path.join(root, 'README')
+ with open(filename, 'r') as f:
+ content = f.read()
+ lines = content.splitlines()
+ desc = ' '.join(itertools.takewhile(lambda x: x, lines))
+ api = get_lines('api', lines)
+ keywords = get_lines('keywords', lines)
+
+ for k in keywords:
+ if k not in KEYWORDS:
+ raise ValueError(
+ '%s is not a valid keyword in file %s' % (k, filename))
+ keyword_set.update(keywords)
+ if not api:
+ api = [None]
+ samples.append((api[0], keywords, root[1:], desc))
+
+ samples.sort()
+
+ return samples, keyword_set
+
+
+def main():
+ # Get all the information we need out of the README files in the samples.
+ samples, keyword_set = scan_readme_files('./samples')
+
+ # Now build a wiki page with all that information. Accumulate all the
+ # information as string to be concatenated when were done.
+ page = ['<wiki:toc max_depth="3" />\n= Samples By API =\n']
+
+ # All the samples, grouped by API.
+ current_api = None
+ for api, keywords, dirname, desc in samples:
+ context = context_from_sample(api, keywords, dirname, desc)
+ if context is None:
+ continue
+ if current_api != api:
+ page.append("""
+=== %(api_icon)s %(api_name)s ===
+
+%(api_desc)s
+
+Documentation for the %(api_name)s in [http://api-python-client-doc.appspot.com/%(api)s/%(version)s PyDoc]
+
+""" % context)
+ current_api = api
+
+ page.append('|| [http://code.google.com/p/google-api-python-client/source/browse/#hg%(dir_escaped)s %(dir)s] || %(desc)s ||\n' % context)
+
+ # Now group the samples by keywords.
+ for keyword, keyword_name in KEYWORDS.iteritems():
+ if keyword not in keyword_set:
+ continue
+ page.append('\n= %s Samples =\n\n' % keyword_name)
+ page.append('<table border=1 cellspacing=0 cellpadding=8px>\n')
+ for _, keywords, dirname, desc in samples:
+ context = keyword_context_from_sample(keywords, dirname, desc)
+ if keyword not in keywords:
+ continue
+ page.append("""
+<tr>
+ <td>[http://code.google.com/p/google-api-python-client/source/browse/#hg%(dir_escaped)s %(dir)s] </td>
+ <td> %(desc)s </td>
+</tr>""" % context)
+ page.append('</table>\n')
+
+ print ''.join(page)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/samples/adexchangebuyer/README b/samples/adexchangebuyer/README
new file mode 100644
index 0000000..87ad8c4
--- /dev/null
+++ b/samples/adexchangebuyer/README
@@ -0,0 +1,4 @@
+Samples for working with the Ad Exchange Buyer API.
+
+api: adexchangebuyer
+keywords: cmdline
diff --git a/samples/adsense/README b/samples/adsense/README
new file mode 100644
index 0000000..b2f88ac
--- /dev/null
+++ b/samples/adsense/README
@@ -0,0 +1,4 @@
+A collection of command-line samples for the AdSense Management API.
+
+api: adsense
+keywords: cmdline
diff --git a/samples/analytics/README b/samples/analytics/README
new file mode 100644
index 0000000..8b3860a
--- /dev/null
+++ b/samples/analytics/README
@@ -0,0 +1,4 @@
+Command-line samples for producting reports with the Analytics API.
+
+api: analytics
+keywords: cmdline
diff --git a/samples/api-python-client-doc/README b/samples/api-python-client-doc/README
index 835a60c..bcbf475 100644
--- a/samples/api-python-client-doc/README
+++ b/samples/api-python-client-doc/README
@@ -1,6 +1,5 @@
-This sample is the code that drives
+This sample is the code that drives http://api-python-client-doc.appspot.com/
+It is an application that serves up the Python help documentation for each API.
- http://api-python-client-doc.appspot.com/
-
-It is an application that serves up the Python help documentation
-for each API.
+api: discovery
+keywords: appengine
diff --git a/samples/appengine/README b/samples/appengine/README
new file mode 100644
index 0000000..68befe7
--- /dev/null
+++ b/samples/appengine/README
@@ -0,0 +1,5 @@
+Simple Google+ sample that demonstrates the people API. Demontrates
+using the OAuth 2.0 Decorator for Google App Engine applications.
+
+api: plus
+keywords: appengine oauth2
diff --git a/samples/appengine_with_robots/README b/samples/appengine_with_robots/README
new file mode 100644
index 0000000..277df01
--- /dev/null
+++ b/samples/appengine_with_robots/README
@@ -0,0 +1,5 @@
+Sample application that demonstrates how to use AppAssertionCredentials
+to access an API.
+
+api: urlshortener
+keywords: appengine oauth2
diff --git a/samples/audit/README b/samples/audit/README
new file mode 100644
index 0000000..5fcb803
--- /dev/null
+++ b/samples/audit/README
@@ -0,0 +1,4 @@
+Prints the activities for a domain using the Audit API.
+
+api: audit
+keywords: cmdline
diff --git a/samples/blogger/README b/samples/blogger/README
new file mode 100644
index 0000000..3c6dda5
--- /dev/null
+++ b/samples/blogger/README
@@ -0,0 +1,4 @@
+Retrieve the list of blogs and their posts for a user.
+
+api: blogger
+keywords: cmdline
diff --git a/samples/customsearch/README b/samples/customsearch/README
new file mode 100644
index 0000000..144f598
--- /dev/null
+++ b/samples/customsearch/README
@@ -0,0 +1,4 @@
+Search from the command-line.
+
+api: customsearch
+keywords: cmdline
diff --git a/samples/dailymotion/README b/samples/dailymotion/README
new file mode 100644
index 0000000..7139bde
--- /dev/null
+++ b/samples/dailymotion/README
@@ -0,0 +1,3 @@
+Demonstrates using oauth2client against the DailyMotion API.
+
+keywords: oauth2 appengine
diff --git a/samples/django_sample/README b/samples/django_sample/README
new file mode 100644
index 0000000..3aaeda8
--- /dev/null
+++ b/samples/django_sample/README
@@ -0,0 +1,4 @@
+Sample app demonstrating using oauth2client and the Google+ API from Django.
+
+api: plus
+keywords: oauth2 django
diff --git a/samples/groupssettings/README b/samples/groupssettings/README
new file mode 100644
index 0000000..432997a
--- /dev/null
+++ b/samples/groupssettings/README
@@ -0,0 +1,4 @@
+Sample for the Groups Settings API.
+
+api: groupssettings
+keywords: cmdline
diff --git a/samples/gtaskqueue_sample/README b/samples/gtaskqueue_sample/README
index 6535127..f823db0 100644
--- a/samples/gtaskqueue_sample/README
+++ b/samples/gtaskqueue_sample/README
@@ -1,6 +1,9 @@
This acts as a sample as well as commandline tool for accessing Google TaskQueue
APIs.
+api: taskqueue
+keywords: cmdline
+
Installation
============
diff --git a/samples/latitude/README b/samples/latitude/README
new file mode 100644
index 0000000..2737a03
--- /dev/null
+++ b/samples/latitude/README
@@ -0,0 +1,4 @@
+Add a new location via the Latitude API.
+
+api: latitude
+keywords: cmdline
diff --git a/samples/moderator/README b/samples/moderator/README
new file mode 100644
index 0000000..d135bfe
--- /dev/null
+++ b/samples/moderator/README
@@ -0,0 +1,4 @@
+Create new Moderator series and topics via the Moderator API.
+
+api: moderator
+keywords: cmdline
diff --git a/samples/plus/README b/samples/plus/README
new file mode 100644
index 0000000..f9d59de
--- /dev/null
+++ b/samples/plus/README
@@ -0,0 +1,4 @@
+Loop over all a user's activities and print a short snippet.
+
+api: plus
+keywords: cmdline pagination
diff --git a/samples/prediction/README b/samples/prediction/README
index b8c3b42..b0a37b8 100644
--- a/samples/prediction/README
+++ b/samples/prediction/README
@@ -1,5 +1,8 @@
Before you can run the prediction sample prediction.py, you must load some csv
-formatted data into Google Storage. You can do this by running setup.sh with a
-bucket/object name of your choice. You must first create the bucket you want to
-use. This can be done with the gsutil function or via the web UI (Storage
+formatted data into Google Storage. You can do this by running setup.sh with a
+bucket/object name of your choice. You must first create the bucket you want
+to use. This can be done with the gsutil function or via the web UI (Storage
Access) in the Google APIs Console.
+
+api: prediction
+keywords: cmdline
diff --git a/samples/searchforshopping/README b/samples/searchforshopping/README
new file mode 100644
index 0000000..aaa1dd0
--- /dev/null
+++ b/samples/searchforshopping/README
@@ -0,0 +1,4 @@
+Samples demonstrating the query capabilities for the Search API for Shopping.
+
+api: shopping
+keywords: cmdline
diff --git a/samples/service_account/README b/samples/service_account/README
new file mode 100644
index 0000000..7662df5
--- /dev/null
+++ b/samples/service_account/README
@@ -0,0 +1,4 @@
+Sample that demonstrates working with Service Accounts.
+
+api: tasks
+keywords: oauth2
diff --git a/samples/tasks_appengine/README b/samples/tasks_appengine/README
index 56da2e8..6888c35 100644
--- a/samples/tasks_appengine/README
+++ b/samples/tasks_appengine/README
@@ -1,2 +1,5 @@
Sample code for Getting Started with Tasks API on App Engine article.
http://code.google.com/appengine/articles/python/getting_started_with_tasks_api.html
+
+api: tasks
+keywords: appengine
diff --git a/samples/threadqueue/README b/samples/threadqueue/README
new file mode 100644
index 0000000..4baa99a
--- /dev/null
+++ b/samples/threadqueue/README
@@ -0,0 +1,4 @@
+Demonstrates using threading and thread queues for handling a high volume of requests.
+
+api: moderator
+keywords: cmdline threading
diff --git a/samples/translate/README b/samples/translate/README
new file mode 100644
index 0000000..6bde24c
--- /dev/null
+++ b/samples/translate/README
@@ -0,0 +1,5 @@
+Simple sample for the translate API.
+
+api: translate
+keywords: cmdline
+
diff --git a/samples/tz/README b/samples/tz/README
index ecee023..1afa540 100644
--- a/samples/tz/README
+++ b/samples/tz/README
@@ -4,6 +4,9 @@
Latitude. To use this application you will need Google
Latitude running on a mobile device.
+api: latitude
+keywords: cmdline
+
Installation
============
The google-api-python-client library will need to
diff --git a/samples/urlshortener/README b/samples/urlshortener/README
new file mode 100644
index 0000000..a33e8a8
--- /dev/null
+++ b/samples/urlshortener/README
@@ -0,0 +1,4 @@
+Shortens and URL with the URL Shortener API.
+
+api: urlshortener
+keywords: cmdline