blob: 086886a6afc31b22faac05a51766640eaa4f772c [file] [log] [blame]
Joe Gregorio9d56b5a2012-03-30 09:21:26 -04001#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
Craig Citro751b7fb2014-09-23 11:20:38 -07004# Copyright 2014 Google Inc. All Rights Reserved.
Joe Gregorio9d56b5a2012-03-30 09:21:26 -04005#
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"""
INADA Naokie8d87822014-08-20 15:25:24 +090031from __future__ import print_function
Joe Gregorio9d56b5a2012-03-30 09:21:26 -040032
33import httplib2
34import itertools
35import json
36import os
37import re
38
Joe Gregorioa633c792012-08-24 11:27:16 -040039BASE_HG_URI = ('http://code.google.com/p/google-api-python-client/source/'
40 'browse/#hg')
41
Joe Gregorio9d56b5a2012-03-30 09:21:26 -040042http = httplib2.Http('.cache')
43r, c = http.request('https://www.googleapis.com/discovery/v1/apis')
44if r.status != 200:
Joe Gregorioa633c792012-08-24 11:27:16 -040045 raise ValueError('Received non-200 response when retrieving Discovery.')
Joe Gregorio9d56b5a2012-03-30 09:21:26 -040046
47# Dictionary mapping api names to their discovery description.
48DIRECTORY = {}
49for item in json.loads(c)['items']:
50 if item['preferred']:
51 DIRECTORY[item['name']] = item
52
53# A list of valid keywords. Should not be taken as complete, add to
54# this list as needed.
55KEYWORDS = {
56 'appengine': 'Google App Engine',
57 'oauth2': 'OAuth 2.0',
58 'cmdline': 'Command-line',
59 'django': 'Django',
60 'threading': 'Threading',
Joe Gregorioa633c792012-08-24 11:27:16 -040061 'pagination': 'Pagination',
62 'media': 'Media Upload and Download'
Joe Gregorio9d56b5a2012-03-30 09:21:26 -040063 }
64
65
66def get_lines(name, lines):
67 """Return lines that begin with name.
68
69 Lines are expected to look like:
70
71 name: space separated values
72
73 Args:
74 name: string, parameter name.
75 lines: iterable of string, lines in the file.
76
77 Returns:
78 List of values in the lines that match.
79 """
80 retval = []
81 matches = itertools.ifilter(lambda x: x.startswith(name + ':'), lines)
82 for line in matches:
83 retval.extend(line[len(name)+1:].split())
84 return retval
85
86
87def wiki_escape(s):
88 """Detect WikiSyntax (i.e. InterCaps, a.k.a. CamelCase) and escape it."""
89 ret = []
90 for word in s.split():
91 if re.match(r'[A-Z]+[a-z]+[A-Z]', word):
92 word = '!%s' % word
93 ret.append(word)
94 return ' '.join(ret)
95
96
Joe Gregorioa633c792012-08-24 11:27:16 -040097def context_from_sample(api, keywords, dirname, desc, uri):
Joe Gregorio9d56b5a2012-03-30 09:21:26 -040098 """Return info for expanding a sample into a template.
99
100 Args:
101 api: string, name of api.
102 keywords: list of string, list of keywords for the given api.
103 dirname: string, directory name of the sample.
104 desc: string, long description of the sample.
Joe Gregorioa633c792012-08-24 11:27:16 -0400105 uri: string, uri of the sample code if provided in the README.
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400106
107 Returns:
108 A dictionary of values useful for template expansion.
109 """
Joe Gregorioa633c792012-08-24 11:27:16 -0400110 if uri is None:
111 uri = BASE_HG_URI + dirname.replace('/', '%2F')
112 else:
113 uri = ''.join(uri)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400114 if api is None:
115 return None
116 else:
117 entry = DIRECTORY[api]
118 context = {
119 'api': api,
120 'version': entry['version'],
121 'api_name': wiki_escape(entry.get('title', entry.get('description'))),
122 'api_desc': wiki_escape(entry['description']),
123 'api_icon': entry['icons']['x32'],
124 'keywords': keywords,
125 'dir': dirname,
Joe Gregorioa633c792012-08-24 11:27:16 -0400126 'uri': uri,
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400127 'desc': wiki_escape(desc),
128 }
129 return context
130
131
Joe Gregorioa633c792012-08-24 11:27:16 -0400132def keyword_context_from_sample(keywords, dirname, desc, uri):
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400133 """Return info for expanding a sample into a template.
134
Joe Gregorioa633c792012-08-24 11:27:16 -0400135 Sample may not be about a specific api.
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400136
137 Args:
138 keywords: list of string, list of keywords for the given api.
139 dirname: string, directory name of the sample.
140 desc: string, long description of the sample.
Joe Gregorioa633c792012-08-24 11:27:16 -0400141 uri: string, uri of the sample code if provided in the README.
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400142
143 Returns:
144 A dictionary of values useful for template expansion.
145 """
Joe Gregorioa633c792012-08-24 11:27:16 -0400146 if uri is None:
147 uri = BASE_HG_URI + dirname.replace('/', '%2F')
148 else:
149 uri = ''.join(uri)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400150 context = {
151 'keywords': keywords,
152 'dir': dirname,
Joe Gregorioa633c792012-08-24 11:27:16 -0400153 'uri': uri,
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400154 'desc': wiki_escape(desc),
155 }
156 return context
157
158
159def scan_readme_files(dirname):
160 """Scans all subdirs of dirname for README files.
161
162 Args:
163 dirname: string, name of directory to walk.
164
165 Returns:
166 (samples, keyword_set): list of information about all samples, the union
167 of all keywords found.
168 """
169 samples = []
170 keyword_set = set()
171
172 for root, dirs, files in os.walk(dirname):
173 if 'README' in files:
174 filename = os.path.join(root, 'README')
175 with open(filename, 'r') as f:
176 content = f.read()
177 lines = content.splitlines()
178 desc = ' '.join(itertools.takewhile(lambda x: x, lines))
179 api = get_lines('api', lines)
180 keywords = get_lines('keywords', lines)
Joe Gregorioa633c792012-08-24 11:27:16 -0400181 uri = get_lines('uri', lines)
182 if not uri:
183 uri = None
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400184
185 for k in keywords:
186 if k not in KEYWORDS:
187 raise ValueError(
188 '%s is not a valid keyword in file %s' % (k, filename))
189 keyword_set.update(keywords)
190 if not api:
191 api = [None]
Joe Gregorioa633c792012-08-24 11:27:16 -0400192 samples.append((api[0], keywords, root[1:], desc, uri))
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400193
194 samples.sort()
195
196 return samples, keyword_set
197
198
199def main():
200 # Get all the information we need out of the README files in the samples.
201 samples, keyword_set = scan_readme_files('./samples')
202
203 # Now build a wiki page with all that information. Accumulate all the
204 # information as string to be concatenated when were done.
205 page = ['<wiki:toc max_depth="3" />\n= Samples By API =\n']
206
207 # All the samples, grouped by API.
208 current_api = None
Joe Gregorioa633c792012-08-24 11:27:16 -0400209 for api, keywords, dirname, desc, uri in samples:
210 context = context_from_sample(api, keywords, dirname, desc, uri)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400211 if context is None:
212 continue
213 if current_api != api:
214 page.append("""
215=== %(api_icon)s %(api_name)s ===
216
217%(api_desc)s
218
Joe Gregorio071f8972012-11-08 13:45:15 -0500219Documentation 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 -0400220
221""" % context)
222 current_api = api
223
Joe Gregorioa633c792012-08-24 11:27:16 -0400224 page.append('|| [%(uri)s %(dir)s] || %(desc)s ||\n' % context)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400225
226 # Now group the samples by keywords.
227 for keyword, keyword_name in KEYWORDS.iteritems():
228 if keyword not in keyword_set:
229 continue
230 page.append('\n= %s Samples =\n\n' % keyword_name)
231 page.append('<table border=1 cellspacing=0 cellpadding=8px>\n')
Joe Gregorioa633c792012-08-24 11:27:16 -0400232 for _, keywords, dirname, desc, uri in samples:
233 context = keyword_context_from_sample(keywords, dirname, desc, uri)
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400234 if keyword not in keywords:
235 continue
236 page.append("""
237<tr>
Joe Gregorioa633c792012-08-24 11:27:16 -0400238 <td>[%(uri)s %(dir)s] </td>
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400239 <td> %(desc)s </td>
240</tr>""" % context)
241 page.append('</table>\n')
242
INADA Naokie8d87822014-08-20 15:25:24 +0900243 print(''.join(page))
Joe Gregorio9d56b5a2012-03-30 09:21:26 -0400244
245
246if __name__ == '__main__':
247 main()