Skip to content

Commit 9d56b5a

Browse files
committed
Automatically generate Samples wiki page from README files.
Reviewed in http://codereview.appspot.com/5900069/. Index: samples-index.py =================================================================== new file mode 100644
1 parent b071ca7 commit 9d56b5a

File tree

26 files changed

+331
-8
lines changed

26 files changed

+331
-8
lines changed

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ coverage:
1919
docs:
2020
cd docs; ./build.sh
2121
python describe.py
22+
python samples-index.py ../google-api-python-client.wiki/SampleApps.wiki
23+
24+
.PHONY: wiki
25+
wiki:
26+
python samples-index.py > ../google-api-python-client.wiki/SampleApps.wiki
2227

2328
.PHONY: prerelease
2429
prerelease:

samples-index.py

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#!/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+
20+
The information for the wiki page is built from data found in all the README
21+
files 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+
32+
import httplib2
33+
import itertools
34+
import json
35+
import os
36+
import re
37+
38+
http = httplib2.Http('.cache')
39+
r, c = http.request('https://www.googleapis.com/discovery/v1/apis')
40+
if r.status != 200:
41+
raise ValueError('Received non-200 response when retrieving Discovery document.')
42+
43+
# Dictionary mapping api names to their discovery description.
44+
DIRECTORY = {}
45+
for item in json.loads(c)['items']:
46+
if item['preferred']:
47+
DIRECTORY[item['name']] = item
48+
49+
# A list of valid keywords. Should not be taken as complete, add to
50+
# this list as needed.
51+
KEYWORDS = {
52+
'appengine': 'Google App Engine',
53+
'oauth2': 'OAuth 2.0',
54+
'cmdline': 'Command-line',
55+
'django': 'Django',
56+
'threading': 'Threading',
57+
'pagination': 'Pagination'
58+
}
59+
60+
61+
def get_lines(name, lines):
62+
"""Return lines that begin with name.
63+
64+
Lines are expected to look like:
65+
66+
name: space separated values
67+
68+
Args:
69+
name: string, parameter name.
70+
lines: iterable of string, lines in the file.
71+
72+
Returns:
73+
List of values in the lines that match.
74+
"""
75+
retval = []
76+
matches = itertools.ifilter(lambda x: x.startswith(name + ':'), lines)
77+
for line in matches:
78+
retval.extend(line[len(name)+1:].split())
79+
return retval
80+
81+
82+
def wiki_escape(s):
83+
"""Detect WikiSyntax (i.e. InterCaps, a.k.a. CamelCase) and escape it."""
84+
ret = []
85+
for word in s.split():
86+
if re.match(r'[A-Z]+[a-z]+[A-Z]', word):
87+
word = '!%s' % word
88+
ret.append(word)
89+
return ' '.join(ret)
90+
91+
92+
def context_from_sample(api, keywords, dirname, desc):
93+
"""Return info for expanding a sample into a template.
94+
95+
Args:
96+
api: string, name of api.
97+
keywords: list of string, list of keywords for the given api.
98+
dirname: string, directory name of the sample.
99+
desc: string, long description of the sample.
100+
101+
Returns:
102+
A dictionary of values useful for template expansion.
103+
"""
104+
if api is None:
105+
return None
106+
else:
107+
entry = DIRECTORY[api]
108+
context = {
109+
'api': api,
110+
'version': entry['version'],
111+
'api_name': wiki_escape(entry.get('title', entry.get('description'))),
112+
'api_desc': wiki_escape(entry['description']),
113+
'api_icon': entry['icons']['x32'],
114+
'keywords': keywords,
115+
'dir': dirname,
116+
'dir_escaped': dirname.replace('/', '%2F'),
117+
'desc': wiki_escape(desc),
118+
}
119+
return context
120+
121+
122+
def keyword_context_from_sample(keywords, dirname, desc):
123+
"""Return info for expanding a sample into a template.
124+
125+
Sample may not be about a specific sample.
126+
127+
Args:
128+
keywords: list of string, list of keywords for the given api.
129+
dirname: string, directory name of the sample.
130+
desc: string, long description of the sample.
131+
132+
Returns:
133+
A dictionary of values useful for template expansion.
134+
"""
135+
context = {
136+
'keywords': keywords,
137+
'dir': dirname,
138+
'dir_escaped': dirname.replace('/', '%2F'),
139+
'desc': wiki_escape(desc),
140+
}
141+
return context
142+
143+
144+
def scan_readme_files(dirname):
145+
"""Scans all subdirs of dirname for README files.
146+
147+
Args:
148+
dirname: string, name of directory to walk.
149+
150+
Returns:
151+
(samples, keyword_set): list of information about all samples, the union
152+
of all keywords found.
153+
"""
154+
samples = []
155+
keyword_set = set()
156+
157+
for root, dirs, files in os.walk(dirname):
158+
if 'README' in files:
159+
filename = os.path.join(root, 'README')
160+
with open(filename, 'r') as f:
161+
content = f.read()
162+
lines = content.splitlines()
163+
desc = ' '.join(itertools.takewhile(lambda x: x, lines))
164+
api = get_lines('api', lines)
165+
keywords = get_lines('keywords', lines)
166+
167+
for k in keywords:
168+
if k not in KEYWORDS:
169+
raise ValueError(
170+
'%s is not a valid keyword in file %s' % (k, filename))
171+
keyword_set.update(keywords)
172+
if not api:
173+
api = [None]
174+
samples.append((api[0], keywords, root[1:], desc))
175+
176+
samples.sort()
177+
178+
return samples, keyword_set
179+
180+
181+
def main():
182+
# Get all the information we need out of the README files in the samples.
183+
samples, keyword_set = scan_readme_files('./samples')
184+
185+
# Now build a wiki page with all that information. Accumulate all the
186+
# information as string to be concatenated when were done.
187+
page = ['<wiki:toc max_depth="3" />\n= Samples By API =\n']
188+
189+
# All the samples, grouped by API.
190+
current_api = None
191+
for api, keywords, dirname, desc in samples:
192+
context = context_from_sample(api, keywords, dirname, desc)
193+
if context is None:
194+
continue
195+
if current_api != api:
196+
page.append("""
197+
=== %(api_icon)s %(api_name)s ===
198+
199+
%(api_desc)s
200+
201+
Documentation for the %(api_name)s in [http://api-python-client-doc.appspot.com/%(api)s/%(version)s PyDoc]
202+
203+
""" % context)
204+
current_api = api
205+
206+
page.append('|| [http://code.google.com/p/google-api-python-client/source/browse/#hg%(dir_escaped)s %(dir)s] || %(desc)s ||\n' % context)
207+
208+
# Now group the samples by keywords.
209+
for keyword, keyword_name in KEYWORDS.iteritems():
210+
if keyword not in keyword_set:
211+
continue
212+
page.append('\n= %s Samples =\n\n' % keyword_name)
213+
page.append('<table border=1 cellspacing=0 cellpadding=8px>\n')
214+
for _, keywords, dirname, desc in samples:
215+
context = keyword_context_from_sample(keywords, dirname, desc)
216+
if keyword not in keywords:
217+
continue
218+
page.append("""
219+
<tr>
220+
<td>[http://code.google.com/p/google-api-python-client/source/browse/#hg%(dir_escaped)s %(dir)s] </td>
221+
<td> %(desc)s </td>
222+
</tr>""" % context)
223+
page.append('</table>\n')
224+
225+
print ''.join(page)
226+
227+
228+
if __name__ == '__main__':
229+
main()

samples/adexchangebuyer/README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Samples for working with the Ad Exchange Buyer API.
2+
3+
api: adexchangebuyer
4+
keywords: cmdline

samples/adsense/README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
A collection of command-line samples for the AdSense Management API.
2+
3+
api: adsense
4+
keywords: cmdline

samples/analytics/README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Command-line samples for producting reports with the Analytics API.
2+
3+
api: analytics
4+
keywords: cmdline
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
This sample is the code that drives
1+
This sample is the code that drives http://api-python-client-doc.appspot.com/
2+
It is an application that serves up the Python help documentation for each API.
23

3-
http://api-python-client-doc.appspot.com/
4-
5-
It is an application that serves up the Python help documentation
6-
for each API.
4+
api: discovery
5+
keywords: appengine

samples/appengine/README

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Simple Google+ sample that demonstrates the people API. Demontrates
2+
using the OAuth 2.0 Decorator for Google App Engine applications.
3+
4+
api: plus
5+
keywords: appengine oauth2
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Sample application that demonstrates how to use AppAssertionCredentials
2+
to access an API.
3+
4+
api: urlshortener
5+
keywords: appengine oauth2

samples/audit/README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Prints the activities for a domain using the Audit API.
2+
3+
api: audit
4+
keywords: cmdline

samples/blogger/README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Retrieve the list of blogs and their posts for a user.
2+
3+
api: blogger
4+
keywords: cmdline

0 commit comments

Comments
 (0)