Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: chrome/common/extensions/docs/server2/converter.py

Issue 10832042: Extensions Docs Server: Doc conversion script (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: script/build.py fixes Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 # Example run from the server2/ directory:
7 # $ converter.py ../static/ ../../api templates/articles/ templates/intros/
8 # templates/public/ static/images/ -r
9
10 import optparse
11 import os
12 import re
13 import shutil
14
15 from docs_server_utils import SanitizeAPIName
16
17 IGNORED_FILES = [
18 # These are custom files.
19 '404',
20 'api_index',
21 'experimental',
22 'samples',
23 'index',
24 # These are APIs that should not have docs.
25 'test',
26 'experimental_idltest',
27 ]
28
29 # These are mappings for APIs that have no intros. They are needed because the
30 # names of the JSON files do not give enough information on the actual API name.
not at google - send to devlin 2012/08/01 20:41:04 seems like most of these you could figure out base
cduvall 2012/08/02 00:54:06 Done.
31 CUSTOM_MAPPINGS = {
32 'experimental_bookmark_manager': 'experimental_bookmarkManager',
33 'experimental_input_virtual_keyboard': 'experimental_input_virtualKeyboard',
34 'experimental_media_galleries': 'experimental_mediaGalleries',
35 'experimental_offscreen_tabs': 'experimental_offscreenTabs',
36 'input_ime': 'input_ime',
37 'file_system': 'fileSystem',
38 'page_actions': 'pageActions',
39 'script_badge': 'scriptBadge'
40 }
41
42 def _ReadFile(filename):
43 with open(filename, 'r') as f:
44 return f.read()
45
46 def _WriteFile(filename, data):
47 with open(filename, 'w+') as f:
48 f.write(data)
49
50 def _UnixName(name):
51 """Returns the unix_style name for a given lowerCamelCase string.
52 Shamelessly stolen from json_schema_compiler/model.py.
53 """
54 name = os.path.splitext(name)[0]
55 s1 = re.sub('([a-z])([A-Z])', r'\1_\2', name)
56 s2 = re.sub('([A-Z]+)([A-Z][a-z])', r'\1_\2', s1)
57 return s2.replace('.', '_').lower()
58
59 def _ListAllAPIs(dirname):
60 all_files = []
61 for path, dirs, files in os.walk(dirname):
62 if path == '.':
63 all_files.extend([f for f in files
64 if f.endswith('.json') or f.endswith('.idl')])
65 else:
66 all_files.extend([os.path.join(path, f) for f in files
67 if f.endswith('.json') or f.endswith('.idl')])
68 dirname = dirname.rstrip('/') + '/'
69 return [f[len(dirname):] for f in all_files
70 if os.path.splitext(f)[0].split('_')[-1] not in ['private',
71 'internal']]
72
73 def _MakeArticleTemplate(filename):
74 return '{{+partials.standard_article article:intros.%s}}' % filename
75
76 def _MakeAPITemplate(intro_name, api_name, has_intro):
77 if has_intro:
78 return ('{{+partials.standard_api api:apis.%s intro:intros.%s}}' %
79 (api_name, intro_name))
80 else:
81 return '{{+partials.standard_api api:apis.%s}}' % api_name
82
83 def _GetAPIPath(name, api_dir):
84 api_files = _ListAllAPIs(api_dir)
85 for filename in api_files:
86 if name == _UnixName(SanitizeAPIName(filename)):
87 return _UnixName(filename)
88
89 def _CleanAPIs(source_dir, api_dir, intros_dest, template_dest, exclude):
90 source_files = os.listdir(source_dir)
91 source_files_unix = set(_UnixName(f) for f in source_files)
92 api_files = set(_UnixName(SanitizeAPIName(f)) for f in _ListAllAPIs(api_dir))
93 intros_files = set(_UnixName(f) for f in os.listdir(intros_dest))
94 to_delete = api_files - source_files_unix - set(exclude)
95 for filename in os.listdir(template_dest):
96 no_ext = os.path.splitext(filename)[0]
97 # Check for changes like appWindow -> app.window.
98 if (_UnixName(filename) in source_files_unix - set(exclude) and
99 no_ext not in source_files):
100 os.remove(os.path.join(template_dest, filename))
101 if _UnixName(filename) in to_delete:
102 try:
103 os.remove(os.path.join(intros_dest, filename))
104 except OSError:
105 pass
106 os.remove(os.path.join(template_dest, filename))
107
108 def _FormatFile(contents, path, name, image_dest, replace, is_api):
109 # Copy all images referenced in the page.
110 for image in re.findall(r'src="\.\./images/([^"]*)"', contents):
111 if not replace and os.path.exists(os.path.join(image_dest, image)):
112 continue
113 if '/' in image:
114 try:
115 os.makedirs(os.path.join(image_dest, image.rsplit('/', 1)[0]))
116 except:
117 pass
118 shutil.copy(
119 os.path.join(path, os.pardir, 'images', image),
120 os.path.join(image_dest, image))
121 contents = re.sub(r'<!--.*(BEGIN|END).*-->', r'', contents)
122 contents = re.sub(r'\.\./images', r'{{static}}/images', contents)
123 contents = re.sub(r'<div.*id="pageData-showTOC".*>.*</div>', r'', contents)
124 if is_api:
125 contents = re.sub(r'<div.*id="pageData-name".*>.*</div>', r'', contents)
126 else:
127 contents = re.sub(r'<div.*id="pageData-name".*>(.*)</div>',
128 r'<h1 class="page_title">\1</h1>',
129 contents)
130 # Remove blank lines.
131 contents = '\n'.join([line for line in contents.split('\n') if line.strip()])
132
133 # Attempt to guess if the page has no title.
134 if '<h1' not in contents and not is_api:
135 title = _UnixName(name)
136 title = ' '.join([part[0].upper() + part[1:] for part in title.split('_')])
137 contents = ('<h1 class="page_title">%s</h1>' % title) + contents
138 return contents
139
140 def _MoveAllFiles(source_dir,
141 api_dir,
142 articles_dest,
143 intros_dest,
144 template_dest,
145 image_dest,
146 replace=False,
147 exclude_dir=None):
148 if exclude_dir is None:
149 exclude_files = []
150 else:
151 exclude_files = [_UnixName(f) for f in os.listdir(exclude_dir)]
152 exclude_files.extend(IGNORED_FILES)
153 api_files = _ListAllAPIs(api_dir)
154 if replace:
155 _CleanAPIs(source_dir, api_dir, intros_dest, template_dest, exclude_files)
156 files = set(os.listdir(source_dir))
157 unix_files = [_UnixName(f) for f in files]
158 for name in [SanitizeAPIName(f) for f in _ListAllAPIs(api_dir)]:
159 if _UnixName(name) not in unix_files:
160 files.add(name + '.html')
161 for file_ in files:
162 if (_UnixName(file_) in exclude_files or
163 file_.startswith('.') or
164 file_.startswith('_')):
165 continue
166 _MoveSingleFile(source_dir,
167 file_,
168 api_dir,
169 articles_dest,
170 intros_dest,
171 template_dest,
172 image_dest,
173 replace)
174
175 def _MoveSingleFile(source_dir,
176 source_file,
177 api_dir,
178 articles_dest,
179 intros_dest,
180 template_dest,
181 image_dest,
182 replace=False):
183 unix_name = _UnixName(source_file)
184 is_api = unix_name in [_UnixName(SanitizeAPIName(f))
185 for f in _ListAllAPIs(api_dir)]
186 if unix_name in CUSTOM_MAPPINGS:
187 processed_name = CUSTOM_MAPPINGS[unix_name]
188 else:
189 processed_name = os.path.splitext(source_file)[0].replace('.', '_')
190 if (is_api and
191 '_' in processed_name.replace('experimental_', '') and
192 not os.path.exists(os.path.join(source_dir, source_file))):
193 print ('*******************************************************\n'
194 'NOTICE: No new-style doc generated for %s.\n\n'
195 'Please add a custom mapping for %s to the |CUSTOM_MAPPINGS|'
196 ' section in:\n'
197 'chrome/common/extensions/docs/server2/converter.py\n'
198 '*******************************************************' %
199 (source_file, source_file))
200 return
201 try:
202 static_data = _FormatFile(_ReadFile(os.path.join(source_dir, source_file)),
203 source_dir,
204 source_file,
205 image_dest,
206 replace,
207 is_api)
208 except IOError:
209 static_data = None
210 template_file = os.path.join(template_dest, processed_name + '.html')
211 if is_api:
212 template_data = _MakeAPITemplate(processed_name,
213 _GetAPIPath(unix_name, api_dir),
214 static_data is not None)
215 static_file = os.path.join(intros_dest, processed_name + '.html')
216 else:
217 template_data = _MakeArticleTemplate(unix_name)
218 static_file = os.path.join(articles_dest, processed_name + '.html')
219 if replace or not os.path.exists(template_file):
220 _WriteFile(template_file, template_data)
221 if static_data is not None and (replace or not os.path.exists(static_file)):
222 _WriteFile(static_file, static_data)
223
224 if __name__ == '__main__':
225 parser = optparse.OptionParser(
226 description='Converts static files from the old documentation system to '
227 'the new one. If run without -f, all the files in |src| will '
228 'be converted.',
229 usage='usage: %prog [options] static_src_dir [-f static_src_file] '
230 'api_dir articles_dest intros_dest template_dest image_dest')
231 parser.add_option('-f',
232 '--file',
233 action='store_true',
234 default=False,
235 help='convert single file')
236 parser.add_option('-e',
237 '--exclude',
238 default=None,
239 help='exclude files matching the names in this dir')
240 parser.add_option('-r',
241 '--replace',
242 action='store_true',
243 default=False,
244 help='replace existing files')
245 (opts, args) = parser.parse_args()
246 if (not opts.file and len(args) != 6) or (opts.file and len(args) != 7):
247 parser.error('incorrect number of arguments.')
248
249 if opts.file:
250 _MoveSingleFile(*args, replace=opts.replace)
251 else:
252 _MoveAllFiles(*args, replace=opts.replace, exclude_dir=opts.exclude)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698