-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathgitbook_gen.py
180 lines (149 loc) · 6.09 KB
/
gitbook_gen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# -*- coding: utf-8 -*-
"""
This script will generate a gitbook projects based on the given dir.
The generated dir contains the same structure and the corresponding markdown
files.
To update the structure, please keep a copy of your README.md and SUMMARY.md.
This tool should never overwrite other files.
Usage: python gitbook_gen [src_dir1] [src_dir2] ...
The generated books will have names like `src_dir1_gitbook`, 'src_dir2_gitbook'
Author: [email protected]
"""
import __future__ # noqa
import os
import sys
from fnmatch import fnmatch
ROOT_PATH = os.getcwd()
PROJECT = ""
README = "README.md"
SUMMARY = "SUMMARY.md"
IGNORE_DIRS = ['*build*', '*__pycache__*', '*vendor*', '*test*', '*mock*',
'docs/*'] # path with these patterns will be ignored
IGNORE_FILES = ['*_test.*', '*.md', '*.png', '*.rst'] # path with these patterns will be ignored
generator_info = \
"The book structure is generated by [gitbook_gen]" \
"(https://github.com/yeasy/code_snippet/#gitbook_gen)."
def init_gitbook_dir(dir_path, title):
""" Initialized a gitbook dir, including:
* README.md
* SUMMARY.md
:param dir_path: whole dir path
:param title: project title
:return:
"""
if not os.path.exists(dir_path):
os.makedirs(dir_path)
create_file(README,
"# {}\n\n{}\n".format(title, generator_info), forced=False)
create_file(SUMMARY, "# Summary\n\n* [Introduction](README.md)\n",
forced=True)
def refine_dirname(name):
""" Refine a structrual path as a valid name string
e.g., ./yeasy_book/ --> yeasy_book
/tmp/a/b --> tmp_a_b
:param name: directory name to refine
:return: refined result
"""
result = name.strip('./\\' + os.sep) # tmp/a/b
result = result.replace('/', os.sep) # tmp/a/b
result = result.replace('\\', os.sep) # tmp/a/b
return result.replace(os.sep, '_')
def has_pattern(path_name, patterns=IGNORE_DIRS):
""" Test whether path_name has a given pattern
:param path_name:
:param patterns:
:return:
"""
def test_pattern(pattern):
return fnmatch(path_name.lower(), pattern)
result = list(filter(test_pattern, patterns))
if 'build/' in path_name:
print(path_name)
print(result)
return len(result) > 0
def process_dir(root_dir, level=1):
""" Process the directory, checking sub dirs and files recursively.
:param root_dir: current root dir
:param level: current depth of the dir from the root
:return:
"""
if level > 4: # do not process very deep dir
return
valid_dirs = list(filter(lambda x: not x.startswith('.'), os.listdir(root_dir)))
list_dir = sorted(list(filter(lambda x: os.path.isdir(os.path.join(root_dir, x)),
valid_dirs)))
list_file = sorted(list(filter(lambda x: os.path.isfile(os.path.join(root_dir, x)) and
not x.startswith('_'), valid_dirs)))
for e in list_dir: # dirs
if has_pattern(e, IGNORE_DIRS):
continue
path = os.path.join(root_dir, e).replace('.' + os.sep, '')
if level == 4:
create_file(PROJECT + os.sep + path + '.md', '#' * level + ' ' + e + ' 包\n')
line = '* [%s](%s.md)' % (e, path.replace('\\', '/'))
else:
if not os.path.exists(PROJECT+os.sep+path):
os.makedirs(PROJECT + os.sep + path)
create_file(PROJECT + os.sep + path + os.sep + 'README.md',
'#' * level + ' ' + e + ' 包\n', forced=False)
line = '* [%s](%s/README.md)' % (e, path.replace('\\', '/'))
update_file(SUMMARY, ' ' * 4 * (level - 1) + line)
process_dir(path, level+1)
for e in list_file: # files
if has_pattern(e, IGNORE_FILES):
continue
name, suffix = os.path.splitext(e) # test .py
path = os.path.join(root_dir, name).replace('.' + os.sep, '') \
+ suffix.replace('.', '_') # test\test_py
create_file(PROJECT + os.sep + path + '.md', '#' * level + ' ' + e + '\n')
line = '* [%s](%s.md)' % (e, path.replace('\\', '/'))
update_file(SUMMARY, ' ' * 4 * (level - 1) + line)
def create_file(file_path, content='\n', forced=False):
""" Create a file at the path, and write the content.
If not forced, when file already exists, then do nothing.
:param file_path: The whole path of the file
:param content: Content to write into the file
:param forced: Whether to force to overwrite file content, default to False
:return: None
"""
if os.path.isfile(file_path) and not forced:
print("Warn: {} already exists, stop writing content={}".format(
file_path, content))
return
with open(file_path, 'w') as f:
f.write(content)
def update_file(file_path, content, append=True, debug=False):
""" Update the content into the file_path
Potentially this can be merged with create_file, but maybe too heavy
:param file_path: The whole path of the file
:param content: content to append to SUMMARY file
:param debug: Whether to output the result
:param append: Whether to append the content
:return: None
"""
if append:
with open(file_path, 'a') as f:
f.write(content + '\n')
else:
with open(file_path, 'w') as f:
f.write(content + '\n')
if debug:
print(content)
if __name__ == '__main__':
if len(sys.argv) > 1:
for d in sys.argv[1:]:
if not os.path.exists(d):
print("WARN: dir name {} does not exist".format(d))
continue
path_str = refine_dirname(d)
PROJECT = ROOT_PATH + os.sep \
+ path_str + '_gitbook' # a_b_gitbook
README = PROJECT + os.sep + 'README.md'
SUMMARY = PROJECT + os.sep + 'SUMMARY.md'
print("Will init the output dir={}".format(PROJECT))
init_gitbook_dir(PROJECT, d)
os.chdir(d)
process_dir('.')
else:
print("Put the input dir name(s) as parameters")
print("Usage: python gitbook_gen [source_dir1] [source_dir2] ... ")