blob: a39364ae9c136076d204a81221473e238d4a6d34 [file] [log] [blame]
Joe Gregorio9d56b5a2012-03-30 09:21:26 -04001#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2012 Google Inc.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18"""Build wiki page with a list of all samples.
19
20The information for the wiki page is built from data found in all the README
21files in the samples. The format of the README file is:
22
23
24 Description is everything up to the first blank line.
25
26 api: plus (Used to look up the long name in discovery).
27 keywords: appengine (such as appengine, oauth2, cmdline)
28
29 The rest of the file is ignored when it comes to building the index.
30"""
31
32import httplib2
33import itertools
34import json
35import os
36import re
37
Joe Gregorioa633c792012-08-24 11:27:16 -040038BASE_HG_URI = ('http://code.google.com/p/google-api-python-client/source/'
39 'browse/#hg')
40
Joe Gregorio9d56b5a2012-03-30 09:21:26 -040041http = httplib2.Http('.cache')
42r, c = http.request('https://www.googleapis.com/discovery/v1/apis')
43if r.status != 200:
Joe Gregorioa633c792012-08-24 11:27:16 -040044 raise ValueError('Received non-200 response when retrieving Discovery.')
Joe Gregorio9d56b5a2012-03-30 09:21:26 -040045
46# Dictionary mapping api names to their discovery description.
47DIRECTORY = {}
48for item in json.loads(c)['items']:
49 if item['preferred']:
50 DIRECTORY[item['name']] = item
51
52# A list of valid keywords. Should not be taken as complete, add to
53# this list as needed.
54KEYWORDS = {
55 'appengine': 'Google App Engine',
56 'oauth2': 'OAuth 2.0',
57 'cmdline': 'Command-line',
58 'django': 'Django',
59 'threading': 'Threading',
Joe Gregorioa633c792012-08-24 11:27:16 -040060 'pagination': 'Pagination',
61 'media': 'Media Upload and Download'
Joe Gregorio9d56b5a2012-03-30 09:21:26 -040062 }
63
64
65def get_lines(name, lines):
66 """Return lines that begin with name.
67
68 Lines are expected to look like:
69
70 name: space separated values
71
72 Args:
73 name: string, parameter name.
74 lines: iterable of string, lines in the file.
75
76 Returns:
77 List of values in the lines that match.
78 """
79 retval = []
80 matches = itertools.ifilter(lambda x: x.startswith(name + ':'), lines)
81 for line in matches:
82 retval.extend(line[len(name)+1:].split())
83 return retval
84
85
86def wiki_escape(s):
87 """Detect WikiSyntax (i.e. InterCaps, a.k.a. CamelCase) and escape it."""
88 ret = []
89 for word in s.split():
90 if re.match(r'[A-Z]+[a-z]+[A-Z]', word):
91 word = '!%s' % word
92 ret.append(word)
93 return ' '.join(ret)
94
95
Joe Gregorioa633c792012-08-24 11:27:16 -040096def context_from_sample(api, keywords, dirname, desc, uri):
Joe Gregorio9d56b5a2012-03-30 09:21:26 -040097 """Return info for expanding a sample into a template.
98
99 Args:
100 api: string, name of api.
101 keywords: list of string, list of keywords for the given api.
102 dirname: string, directory name of the sample.
103 desc: string, long description of the sample.
Joe Gregorioa633c792012-08-24 11:27:16 -0400104 uri: string, uri of the sample code if provided in the README.
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400105
106 Returns:
107 A dictionary of values useful for template expansion.
108 """
Joe Gregorioa633c792012-08-24 11:27:16 -0400109 if uri is None:
110 uri = BASE_HG_URI + dirname.replace('/', '%2F')
111 else:
112 uri = ''.join(uri)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400113 if api is None:
114 return None
115 else:
116 entry = DIRECTORY[api]
117 context = {
118 'api': api,
119 'version': entry['version'],
120 'api_name': wiki_escape(entry.get('title', entry.get('description'))),
121 'api_desc': wiki_escape(entry['description']),
122 'api_icon': entry['icons']['x32'],
123 'keywords': keywords,
124 'dir': dirname,
Joe Gregorioa633c792012-08-24 11:27:16 -0400125 'uri': uri,
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400126 'desc': wiki_escape(desc),
127 }
128 return context
129
130
Joe Gregorioa633c792012-08-24 11:27:16 -0400131def keyword_context_from_sample(keywords, dirname, desc, uri):
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400132 """Return info for expanding a sample into a template.
133
Joe Gregorioa633c792012-08-24 11:27:16 -0400134 Sample may not be about a specific api.
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400135
136 Args:
137 keywords: list of string, list of keywords for the given api.
138 dirname: string, directory name of the sample.
139 desc: string, long description of the sample.
Joe Gregorioa633c792012-08-24 11:27:16 -0400140 uri: string, uri of the sample code if provided in the README.
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400141
142 Returns:
143 A dictionary of values useful for template expansion.
144 """
Joe Gregorioa633c792012-08-24 11:27:16 -0400145 if uri is None:
146 uri = BASE_HG_URI + dirname.replace('/', '%2F')
147 else:
148 uri = ''.join(uri)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400149 context = {
150 'keywords': keywords,
151 'dir': dirname,
Joe Gregorioa633c792012-08-24 11:27:16 -0400152 'uri': uri,
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400153 'desc': wiki_escape(desc),
154 }
155 return context
156
157
158def scan_readme_files(dirname):
159 """Scans all subdirs of dirname for README files.
160
161 Args:
162 dirname: string, name of directory to walk.
163
164 Returns:
165 (samples, keyword_set): list of information about all samples, the union
166 of all keywords found.
167 """
168 samples = []
169 keyword_set = set()
170
171 for root, dirs, files in os.walk(dirname):
172 if 'README' in files:
173 filename = os.path.join(root, 'README')
174 with open(filename, 'r') as f:
175 content = f.read()
176 lines = content.splitlines()
177 desc = ' '.join(itertools.takewhile(lambda x: x, lines))
178 api = get_lines('api', lines)
179 keywords = get_lines('keywords', lines)
Joe Gregorioa633c792012-08-24 11:27:16 -0400180 uri = get_lines('uri', lines)
181 if not uri:
182 uri = None
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400183
184 for k in keywords:
185 if k not in KEYWORDS:
186 raise ValueError(
187 '%s is not a valid keyword in file %s' % (k, filename))
188 keyword_set.update(keywords)
189 if not api:
190 api = [None]
Joe Gregorioa633c792012-08-24 11:27:16 -0400191 samples.append((api[0], keywords, root[1:], desc, uri))
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400192
193 samples.sort()
194
195 return samples, keyword_set
196
197
198def main():
199 # Get all the information we need out of the README files in the samples.
200 samples, keyword_set = scan_readme_files('./samples')
201
202 # Now build a wiki page with all that information. Accumulate all the
203 # information as string to be concatenated when were done.
204 page = ['<wiki:toc max_depth="3" />\n= Samples By API =\n']
205
206 # All the samples, grouped by API.
207 current_api = None
Joe Gregorioa633c792012-08-24 11:27:16 -0400208 for api, keywords, dirname, desc, uri in samples:
209 context = context_from_sample(api, keywords, dirname, desc, uri)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400210 if context is None:
211 continue
212 if current_api != api:
213 page.append("""
214=== %(api_icon)s %(api_name)s ===
215
216%(api_desc)s
217
Joe Gregorio071f8972012-11-08 13:45:15 -0500218Documentation for the %(api_name)s in [https://google-api-client-libraries.appspot.com/documentation/%(api)s/%(version)s/python/latest/ PyDoc]
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400219
220""" % context)
221 current_api = api
222
Joe Gregorioa633c792012-08-24 11:27:16 -0400223 page.append('|| [%(uri)s %(dir)s] || %(desc)s ||\n' % context)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400224
225 # Now group the samples by keywords.
226 for keyword, keyword_name in KEYWORDS.iteritems():
227 if keyword not in keyword_set:
228 continue
229 page.append('\n= %s Samples =\n\n' % keyword_name)
230 page.append('<table border=1 cellspacing=0 cellpadding=8px>\n')
Joe Gregorioa633c792012-08-24 11:27:16 -0400231 for _, keywords, dirname, desc, uri in samples:
232 context = keyword_context_from_sample(keywords, dirname, desc, uri)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400233 if keyword not in keywords:
234 continue
235 page.append("""
236<tr>
Joe Gregorioa633c792012-08-24 11:27:16 -0400237 <td>[%(uri)s %(dir)s] </td>
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400238 <td> %(desc)s </td>
239</tr>""" % context)
240 page.append('</table>\n')
241
242 print ''.join(page)
243
244
245if __name__ == '__main__':
246 main()