#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright 2014 Google Inc. All Rights Reserved. # # 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. """ from __future__ import print_function import httplib2 import itertools import json import os import re BASE_HG_URI = ('http://code.google.com/p/google-api-python-client/source/' 'browse/#hg') 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.') # 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', 'media': 'Media Upload and Download' } 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, uri): """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. uri: string, uri of the sample code if provided in the README. Returns: A dictionary of values useful for template expansion. """ if uri is None: uri = BASE_HG_URI + dirname.replace('/', '%2F') else: uri = ''.join(uri) 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, 'uri': uri, 'desc': wiki_escape(desc), } return context def keyword_context_from_sample(keywords, dirname, desc, uri): """Return info for expanding a sample into a template. Sample may not be about a specific api. 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. uri: string, uri of the sample code if provided in the README. Returns: A dictionary of values useful for template expansion. """ if uri is None: uri = BASE_HG_URI + dirname.replace('/', '%2F') else: uri = ''.join(uri) context = { 'keywords': keywords, 'dir': dirname, 'uri': uri, '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) uri = get_lines('uri', lines) if not uri: uri = None 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, uri)) 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, uri in samples: context = context_from_sample(api, keywords, dirname, desc, uri) 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 [https://google-api-client-libraries.appspot.com/documentation/%(api)s/%(version)s/python/latest/ PyDoc] """ % context) current_api = api page.append('|| [%(uri)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, uri in samples: context = keyword_context_from_sample(keywords, dirname, desc, uri) if keyword not in keywords: continue page.append(""" <tr> <td>[%(uri)s %(dir)s] </td> <td> %(desc)s </td> </tr>""" % context) page.append('</table>\n') print(''.join(page)) if __name__ == '__main__': main()