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

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: 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 'app'
28 ]
29
30 # These are mappings for APIs that have no intros. They are needed because the
31 # names of the JSON files do not give enough information on the actual API name.
32 CUSTOM_MAPPINGS = {
33 'experimental_bookmark_manager': 'experimental_bookmarkManager',
34 'experimental_input_virtual_keyboard': 'experimental_input_virtualKeyboard',
35 'experimental_media_galleries': 'experimental_mediaGalleries',
36 'experimental_offscreen_tabs': 'experimental_offscreenTabs',
37 'file_system': 'fileSystem',
38 'page_actions': 'pageActions',
39 'script_badge': 'scriptBadge'
40 }
41
42 def _UnixName(name):
43 """Returns the unix_style name for a given lowerCamelCase string.
44 Shamelessly stolen from json_schema_compiler/model.py.
45 """
46 name = os.path.splitext(name)[0]
47 s1 = re.sub('([a-z])([A-Z])', r'\1_\2', name)
48 s2 = re.sub('([A-Z]+)([A-Z][a-z])', r'\1_\2', s1)
49 return s2.replace('.', '_').lower()
50
51 def _ReadFile(filename):
52 with open(filename, 'r') as f:
53 return f.read()
54
55 def _WriteFile(filename, data):
56 with open(filename, 'w+') as f:
57 f.write(data)
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.strip('/') + '/'
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 _FormatFile(contents, path, name, image_dest, replace, is_api):
84 # Copy all images referenced in the page.
85 for image in re.findall(r'src="\.\./images/([^"]*)"', contents):
86 if not replace and os.path.exists(os.path.join(image_dest, image)):
87 continue
88 if '/' in image:
89 try:
90 os.makedirs(os.path.join(image_dest, image.rsplit('/', 1)[0]))
91 except:
92 pass
93 shutil.copy(
94 os.path.join(path, os.pardir, 'images', image),
95 os.path.join(image_dest, image))
96 contents = re.sub(r'<!--.*(BEGIN|END).*-->', r'', contents)
97 contents = re.sub(r'\.\./images', r'{{static}}/images', contents)
98 contents = re.sub(r'<div.*id="pageData-showTOC".*>.*</div>', r'', contents)
99 if is_api:
100 contents = re.sub(r'<div.*id="pageData-name".*>.*</div>', r'', contents)
101 else:
102 contents = re.sub(r'<div.*id="pageData-name".*>(.*)</div>',
103 r'<h1 class="page_title">\1</h1>',
104 contents)
105 # Remove blank lines.
106 contents = '\n'.join([line for line in contents.split('\n') if line.strip()])
107
108 # Attempt to guess if the page has no title.
109 if '<h1' not in contents and not is_api:
110 title = _UnixName(name)
111 title = ' '.join([part[0].upper() + part[1:] for part in title.split('_')])
112 contents = ('<h1 class="page_title">%s</h1>' % title) + contents
113 return contents
114
115 def _MoveAllFiles(source_dir,
116 api_dir,
117 articles_dest,
118 intros_dest,
119 template_dest,
120 image_dest,
121 replace=False,
122 exclude_dir=None):
123 if exclude_dir is None:
124 exclude_files = []
125 else:
126 exclude_files = [_UnixName(f) for f in os.listdir(exclude_dir)]
127 exclude_files.extend(IGNORED_FILES)
128 api_files = _ListAllAPIs(api_dir)
129 if replace:
130 _CleanAPIs(source_dir, api_dir, intros_dest, template_dest, exclude_files)
131 files = set(os.listdir(source_dir))
132 unix_files = [_UnixName(f) for f in files]
133 for name in [SanitizeAPIName(f) for f in _ListAllAPIs(api_dir)]:
134 if _UnixName(name) not in unix_files:
135 files.add(name + '.html')
136 for file_ in files:
137 if (_UnixName(file_) in exclude_files or
138 file_.startswith('.') or
139 file_.startswith('_')):
140 continue
141 _MoveSingleFile(source_dir,
142 file_,
143 api_dir,
144 articles_dest,
145 intros_dest,
146 template_dest,
147 image_dest,
148 replace)
149
150 def _CleanAPIs(source_dir, api_dir, intros_dest, template_dest, exclude):
151 source_files = set(_UnixName(f) for f in os.listdir(source_dir))
152 api_files = set(_UnixName(SanitizeAPIName(f)) for f in _ListAllAPIs(api_dir))
153 intros_files = set(_UnixName(f) for f in os.listdir(intros_dest))
154 to_delete = api_files - source_files - set(exclude)
155 for filename in os.listdir(template_dest):
156 if _UnixName(filename) in to_delete:
157 try:
158 os.remove(os.path.join(intros_dest, filename))
159 except OSError:
160 pass
161 os.remove(os.path.join(template_dest, filename))
162
163 def _GetAPIPath(name, api_dir):
164 api_files = _ListAllAPIs(api_dir)
165 for filename in api_files:
166 if name == _UnixName(SanitizeAPIName(filename)):
167 return _UnixName(filename)
168
169 def _MoveSingleFile(source_dir,
170 source_file,
171 api_dir,
172 articles_dest,
173 intros_dest,
174 template_dest,
175 image_dest,
176 replace=False):
177 unix_name = _UnixName(source_file)
178 is_api = unix_name in [_UnixName(SanitizeAPIName(f))
179 for f in _ListAllAPIs(api_dir)]
180 if unix_name in CUSTOM_MAPPINGS:
181 processed_name = CUSTOM_MAPPINGS[unix_name]
182 else:
183 processed_name = os.path.splitext(source_file)[0].replace('.', '_')
184 try:
185 static_data = _FormatFile(_ReadFile(os.path.join(source_dir, source_file)),
186 source_dir,
187 source_file,
188 image_dest,
189 replace,
190 is_api)
191 except IOError:
192 static_data = None
193 template_file = os.path.join(template_dest, processed_name + '.html')
194 if is_api:
195 template_data = _MakeAPITemplate(processed_name,
196 _GetAPIPath(unix_name, api_dir),
197 static_data is not None)
198 static_file = os.path.join(intros_dest, processed_name + '.html')
199 else:
200 template_data = _MakeArticleTemplate(unix_name)
201 static_file = os.path.join(articles_dest, processed_name + '.html')
202 if replace or not os.path.exists(template_file):
203 _WriteFile(template_file, template_data)
204 if static_data is not None and (replace or not os.path.exists(static_file)):
205 _WriteFile(static_file, static_data)
206
207 if __name__ == '__main__':
208 parser = optparse.OptionParser(
209 description='Converts static files from the old documentation system to '
210 'the new one. If run without -f, all the files in |src| will '
211 'be converted.',
212 usage='usage: %prog [options] static_src_dir [-f static_src_file] '
213 'api_dir articles_dest intros_dest template_dest image_dest')
214 parser.add_option('-f',
215 '--file',
216 action='store_true',
217 default=False,
218 help='convert single file')
219 parser.add_option('-e',
220 '--exclude',
221 default=None,
222 help='exclude files matching the names in this dir')
223 parser.add_option('-r',
224 '--replace',
225 action='store_true',
226 default=False,
227 help='replace existing files')
228 (opts, args) = parser.parse_args()
229 if (not opts.file and len(args) != 6) or (opts.file and len(args) != 7):
230 parser.error('incorrect number of arguments.')
231
232 if opts.file:
233 _MoveSingleFile(*args, replace=opts.replace)
234 else:
235 _MoveAllFiles(*args, replace=opts.replace, exclude_dir=opts.exclude)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698