Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # -*- coding: utf-8 -*- |
| 3 | # |
Craig Citro | 751b7fb | 2014-09-23 11:20:38 -0700 | [diff] [blame] | 4 | # Copyright 2014 Google Inc. All Rights Reserved. |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 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 | """ |
INADA Naoki | e8d8782 | 2014-08-20 15:25:24 +0900 | [diff] [blame] | 31 | from __future__ import print_function |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 32 | |
| 33 | import httplib2 |
| 34 | import itertools |
| 35 | import json |
| 36 | import os |
| 37 | import re |
| 38 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 39 | BASE_HG_URI = "http://code.google.com/p/google-api-python-client/source/" "browse/#hg" |
Joe Gregorio | a633c79 | 2012-08-24 11:27:16 -0400 | [diff] [blame] | 40 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 41 | http = httplib2.Http(".cache") |
| 42 | r, c = http.request("https://www.googleapis.com/discovery/v1/apis") |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 43 | if r.status != 200: |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 44 | raise ValueError("Received non-200 response when retrieving Discovery.") |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 45 | |
| 46 | # Dictionary mapping api names to their discovery description. |
| 47 | DIRECTORY = {} |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 48 | for item in json.loads(c)["items"]: |
| 49 | if item["preferred"]: |
| 50 | DIRECTORY[item["name"]] = item |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 51 | |
| 52 | # A list of valid keywords. Should not be taken as complete, add to |
| 53 | # this list as needed. |
| 54 | KEYWORDS = { |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 55 | "appengine": "Google App Engine", |
| 56 | "oauth2": "OAuth 2.0", |
| 57 | "cmdline": "Command-line", |
| 58 | "django": "Django", |
| 59 | "threading": "Threading", |
| 60 | "pagination": "Pagination", |
| 61 | "media": "Media Upload and Download", |
| 62 | } |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 63 | |
| 64 | |
| 65 | def get_lines(name, lines): |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 66 | """Return lines that begin with name. |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 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 | """ |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 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 |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 84 | |
| 85 | |
| 86 | def wiki_escape(s): |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 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) |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 94 | |
| 95 | |
Joe Gregorio | a633c79 | 2012-08-24 11:27:16 -0400 | [diff] [blame] | 96 | def context_from_sample(api, keywords, dirname, desc, uri): |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 97 | """Return info for expanding a sample into a template. |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 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 Gregorio | a633c79 | 2012-08-24 11:27:16 -0400 | [diff] [blame] | 104 | uri: string, uri of the sample code if provided in the README. |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 105 | |
| 106 | Returns: |
| 107 | A dictionary of values useful for template expansion. |
| 108 | """ |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 109 | if uri is None: |
| 110 | uri = BASE_HG_URI + dirname.replace("/", "%2F") |
| 111 | else: |
| 112 | uri = "".join(uri) |
| 113 | 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, |
| 125 | "uri": uri, |
| 126 | "desc": wiki_escape(desc), |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 127 | } |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 128 | return context |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 129 | |
| 130 | |
Joe Gregorio | a633c79 | 2012-08-24 11:27:16 -0400 | [diff] [blame] | 131 | def keyword_context_from_sample(keywords, dirname, desc, uri): |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 132 | """Return info for expanding a sample into a template. |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 133 | |
Joe Gregorio | a633c79 | 2012-08-24 11:27:16 -0400 | [diff] [blame] | 134 | Sample may not be about a specific api. |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 135 | |
| 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 Gregorio | a633c79 | 2012-08-24 11:27:16 -0400 | [diff] [blame] | 140 | uri: string, uri of the sample code if provided in the README. |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 141 | |
| 142 | Returns: |
| 143 | A dictionary of values useful for template expansion. |
| 144 | """ |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 145 | if uri is None: |
| 146 | uri = BASE_HG_URI + dirname.replace("/", "%2F") |
| 147 | else: |
| 148 | uri = "".join(uri) |
| 149 | context = { |
| 150 | "keywords": keywords, |
| 151 | "dir": dirname, |
| 152 | "uri": uri, |
| 153 | "desc": wiki_escape(desc), |
| 154 | } |
| 155 | return context |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 156 | |
| 157 | |
| 158 | def scan_readme_files(dirname): |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 159 | """Scans all subdirs of dirname for README files. |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 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 | """ |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 168 | samples = [] |
| 169 | keyword_set = set() |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 170 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 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) |
| 180 | uri = get_lines("uri", lines) |
| 181 | if not uri: |
| 182 | uri = None |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 183 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 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 | ) |
| 189 | keyword_set.update(keywords) |
| 190 | if not api: |
| 191 | api = [None] |
| 192 | samples.append((api[0], keywords, root[1:], desc, uri)) |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 193 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 194 | samples.sort() |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 195 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 196 | return samples, keyword_set |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 197 | |
| 198 | |
| 199 | def main(): |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 200 | # Get all the information we need out of the README files in the samples. |
| 201 | samples, keyword_set = scan_readme_files("./samples") |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 202 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 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'] |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 206 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 207 | # All the samples, grouped by API. |
| 208 | current_api = None |
| 209 | for api, keywords, dirname, desc, uri in samples: |
| 210 | context = context_from_sample(api, keywords, dirname, desc, uri) |
| 211 | if context is None: |
| 212 | continue |
| 213 | if current_api != api: |
| 214 | page.append( |
| 215 | """ |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 216 | === %(api_icon)s %(api_name)s === |
| 217 | |
| 218 | %(api_desc)s |
| 219 | |
Joe Gregorio | 071f897 | 2012-11-08 13:45:15 -0500 | [diff] [blame] | 220 | Documentation for the %(api_name)s in [https://google-api-client-libraries.appspot.com/documentation/%(api)s/%(version)s/python/latest/ PyDoc] |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 221 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 222 | """ |
| 223 | % context |
| 224 | ) |
| 225 | current_api = api |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 226 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 227 | page.append("|| [%(uri)s %(dir)s] || %(desc)s ||\n" % context) |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 228 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 229 | # Now group the samples by keywords. |
| 230 | for keyword, keyword_name in KEYWORDS.iteritems(): |
| 231 | if keyword not in keyword_set: |
| 232 | continue |
| 233 | page.append("\n= %s Samples =\n\n" % keyword_name) |
| 234 | page.append("<table border=1 cellspacing=0 cellpadding=8px>\n") |
| 235 | for _, keywords, dirname, desc, uri in samples: |
| 236 | context = keyword_context_from_sample(keywords, dirname, desc, uri) |
| 237 | if keyword not in keywords: |
| 238 | continue |
| 239 | page.append( |
| 240 | """ |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 241 | <tr> |
Joe Gregorio | a633c79 | 2012-08-24 11:27:16 -0400 | [diff] [blame] | 242 | <td>[%(uri)s %(dir)s] </td> |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 243 | <td> %(desc)s </td> |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 244 | </tr>""" |
| 245 | % context |
| 246 | ) |
| 247 | page.append("</table>\n") |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 248 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 249 | print("".join(page)) |
Joe Gregorio | 9d56b5a | 2012-03-30 09:21:26 -0400 | [diff] [blame] | 250 | |
| 251 | |
Bu Sun Kim | 66bb32c | 2019-10-30 10:11:58 -0700 | [diff] [blame] | 252 | if __name__ == "__main__": |
| 253 | main() |