-
Notifications
You must be signed in to change notification settings - Fork 22
/
pre_commit_readme_update.py
373 lines (306 loc) · 12.5 KB
/
pre_commit_readme_update.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# this is a client-side hook
# add the following command to pre-commit in the .git/hooks
# don't foget to make it executable `chmod +x .git/hooks/pre-commit`
# #!/bin/bash
# # Update the README file with the python script
# python pre_commit_readme_update.py
# # Add the README file to the git index
# git add README.md
import os
import re
import datetime
import collections
from functools import lru_cache
TAG_MATH = 'Math'
TAG_BIT = 'Bit Manipulation'
TAG_SIM = 'Simulation'
TAG_DESIGN = 'Design'
TAG_GREEDY = 'Greedy'
TAG_DP = 'Dynamic Programming'
TAG_ARY = 'Array'
TAG_STR = 'String'
TAG_STACK = 'Stack'
TAG_TP = 'Two Pointers'
TAG_SW = 'Sliding Window'
TAG_SORT = 'Sorting'
TAG_COUNT = 'Counting'
TAG_BFS = 'Breadth-First Search'
TAG_BT = 'Backtracking'
TAG_DFS = 'Depth-First Search'
TAG_DC = 'Divide and Conquer'
TAG_BINARY_SEARCH = 'Binary Search'
TAG_BINARY_SEARCH_TREE = 'Binary Search Tree'
TAG_LINKED_LIST = 'Linked List'
TAG_PQ = 'Priority Queue'
TAG_HASH = 'Hash Table'
TAG_UNION_FIND = 'Union Find'
TAG_TRIE = 'Trie'
TAG_SEGMENT_TREE = 'Segment Tree'
TAG_TREE = 'Tree'
TAG_GRAPH = 'Graph'
TAG_BFS_TS = 'Topological Sort'
CATEGORY_OTHER = 'Other'
SHOW_CATEGORIES = [TAG_MATH, TAG_BIT, TAG_SIM, TAG_DESIGN, TAG_BINARY_SEARCH, TAG_LINKED_LIST, TAG_TP, TAG_SW, TAG_STACK, TAG_SORT, TAG_COUNT,
TAG_GREEDY, TAG_DP, TAG_BT, TAG_DC, TAG_BFS, TAG_BFS_TS, TAG_DFS, TAG_PQ, TAG_UNION_FIND, TAG_TRIE, TAG_SEGMENT_TREE]
FOLD_STRUCTURES = [TAG_ARY, TAG_STR, TAG_TREE, TAG_HASH, TAG_GRAPH]
ALL_CATEGORIES = SHOW_CATEGORIES + FOLD_STRUCTURES + [CATEGORY_OTHER]
TAG_IGNORE = r'-|Iterator|Interactive'
TAG_REGEX = {
TAG_BFS: r'Breadth-First Search|Breadth First Search|BFS',
TAG_DFS: r'Depth-First Search|Depth First Search|DFS',
TAG_DP: r'Dynamic Programming|DP|Memoization',
TAG_TREE: r'^Tree$|Binary Indexed Tree|Binary Tree|Binary Search Tree',
TAG_ARY: r'Array|Matrix|Prefix Sum',
TAG_SORT: r'^Sort$|Sorting|Bucket Sort',
TAG_MATH: r'Math|Number Theory|Randomized'
}
LANGUAGE = {
'cpp': 'c++',
'py': 'python',
'java': 'java'
}
class Markdown:
@staticmethod
def escape(content) -> str:
escape_table = {
'\\': '\\',
'`': '\\`',
'*': '\\*',
'_': '\\_',
'{': '\\{',
'}': '\\}',
'[': '\\[',
']': '\\]',
'(': '\\(',
')': '\\)',
'#': '\\#',
'+': '\\+',
'-': '\\-',
'.': '\\.',
'!': '\\!',
'|': '\\|',
}
escaped_text = content.translate({ord(key): value for key, value in escape_table.items()})
return escaped_text
@staticmethod
def paragraph(f, content):
for line in content:
f.write(str(line) + "<br/>\n")
f.write("\n")
@staticmethod
def code(f, content, lang="shell"):
f.write(f"```{lang}\n")
for line in content:
f.write(str(line) + "\n")
f.write("```\n\n")
@staticmethod
def bullet(f, content):
for line in content:
f.write("- " + str(line) + "\n")
f.write("\n")
@staticmethod
def title1(f, conent):
f.write('# ' + str(conent) + "\n")
@staticmethod
def title2(f, conent):
f.write('## ' + str(conent) + "\n")
@staticmethod
def table_row(f, contents):
content = ' | '.join([str(content) for content in contents])
f.write("| " + content + " |" + "\n")
@staticmethod
def table_header(f, headers):
Markdown.table_row(f, headers)
Markdown.table_row(f, ['-----'] * len(headers))
@staticmethod
def table_footer(f):
f.write("\n")
@staticmethod
def table_content(f, categories):
print('----table content----')
def search_tag(solution):
tags = solution.tags
match_all = set()
for tag in tags.split(', '):
match_category = set([x for x in ALL_CATEGORIES if re.search(TAG_REGEX.get(x, x), tag, re.IGNORECASE)])
if len(match_category) > 0:
match_all.update(match_category)
elif not re.search(TAG_IGNORE, tag):
print(f'{tag}/[{solution.tags}] - {solution}')
fold_category = set([x for x in match_all if x in FOLD_STRUCTURES])
show_category = match_all - fold_category
if len(show_category) > 0:
return set(show_category)
if len(fold_category) > 0:
return set(fold_category)
return {CATEGORY_OTHER}
category_set = collections.defaultdict(list)
for s in Solution.all():
for category in search_tag(s):
category_set[category].append(s)
for category in categories:
if len(category_set[category]) == 0:
continue
Markdown.title2(f, category)
solution_set = collections.defaultdict(list)
for s in category_set[category]:
solution_set[s.key].append(s.local_path)
Markdown.table_header(f, [f'Problem({len(solution_set)})', 'Solution', 'Time', 'Space', 'Note', 'Ref'])
sorted_solutions = sorted(category_set[category], key=lambda s: (s.source, int(s.number)))
for solution in sorted_solutions:
if solution.key not in solution_set:
continue
codes = ', '.join(solution_set[solution.key])
contents = [
solution.problem_link,
codes,
solution.time,
solution.space,
solution.note,
solution.ref_link,
]
Markdown.table_row(f, contents)
del solution_set[solution.key]
Markdown.table_footer(f)
@staticmethod
def link(content, link):
return f"[{content}]({link})"
@staticmethod
def tag(content):
category_tag = "-".join(content.lower().split())
return Markdown.link(content, f'#{category_tag}')
class Solution:
@staticmethod
@lru_cache()
def all(directories=['leetcode', 'lintcode']) -> int:
all_solution = []
for source in directories:
count = 0
for file_name in os.listdir(source):
if file_name.startswith('.'):
continue
count += 1
first_dot = file_name.find('.')
last_dot = file_name.rfind('.')
name = file_name[first_dot + 1: last_dot]
extension = file_name[last_dot + 1: ]
number = file_name[: first_dot]
path = os.path.join(f'./{source}/{file_name}')
with open(path, 'r') as code:
text = code.read()
tag_match = re.search(r"Tag: (.+)", text)
time_match = re.search(r"Time: (.+)", text)
space_match = re.search(r"Space: (.+)", text)
link_match = re.search(r"Ref: (.+)", text)
note_match = re.search(r"Note: (.+)", text)
tags = tag_match.group(1) if tag_match else '-'
time = time_match.group(1) if time_match else '-'
space = space_match.group(1) if space_match else '-'
ref = link_match.group(1) if link_match else '-'
note = note_match.group(1) if note_match else '-'
all_solution.append(Solution(path, source, number, name, extension, tags, time, space, note, ref))
print(f'{source} files = {count}')
return all_solution
@staticmethod
def statistic() -> int:
statistic_set = collections.defaultdict(list)
for s in Solution.all():
statistic_set[s.name].append(s)
for key, solutions in statistic_set.items():
source = set(solutions)
if len(source) > 1:
print(f"{key}: {solutions}")
print(f'solved = {len(statistic_set)}')
return len(statistic_set)
def __init__(self, path, source, number, name, extension, tags, time, space, note, ref) -> None:
self.path = path
self.source = source
self.number = number
self.name = name
self.extension = extension
self.tags = tags
self.time = Markdown.escape(time)
self.space = Markdown.escape(space)
self.note = Markdown.escape(note)
self.ref = ref
self.roman_rex = re.compile(r"(?:(?<=\W)|^)M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$", re.IGNORECASE)
@property
def title(self) -> str:
problem = self.name.replace('-', ' ')
orginal_title = f'{self.source}-{self.number}. {problem}'.title()
return re.sub(self.roman_rex, lambda x: x.group(0).upper(), orginal_title)
@property
def key(self) -> str:
return f'{self.source}-{self.name}'
@property
def problem_link(self) -> str:
if self.source.lower() == 'leetcode':
return Markdown.link(self.title, f'https://leetcode.com/problems/{self.name}/description/')
if self.source.lower() == 'lintcode':
return Markdown.link(self.title, f'https://www.lintcode.com/problem/{self.name}')
else:
return f'#'
@property
def local_path(self) -> str:
lang = LANGUAGE[self.extension] if self.extension in LANGUAGE else self.extension
return Markdown.link(lang, self.path)
@property
def ref_link(self) -> str:
if re.search(r'(youtube\.com|youtu\.be)', self.ref):
return Markdown.link('Video', self.ref)
else:
return f'-'
def __eq__(self, other):
return isinstance(other, Solution) and self.key == other.key
def __hash__(self):
return hash(self.key)
def __repr__(self) -> str:
return f'{self.title}.{self.extension}'
def __str__(self) -> str:
return self.__repr__()
if __name__ == "__main__":
with open("README.md", "w") as f:
Markdown.title1(f, "算法/Algorithm")
Markdown.paragraph(f, [
"我个人的力扣答案, **公众号:GeekPal**",
"这是一个持续更新的开源项目",
"",
"My personal leetcode answers",
"This is a **continually updated** open source project",
"",
f"Total sovled: **{Solution.statistic()}**",
f"Auto updated at: **{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}**"
])
Markdown.title2(f, "软件/Softwares")
Markdown.bullet(f, [
Markdown.link('Anki', 'https://apps.ankiweb.net/'),
Markdown.link('Tldraw', 'https://www.tldraw.com/'),
Markdown.link('OBS', 'https://www.tldraw.com/'),
Markdown.link('Leetcode中英网站切换', 'https://greasyfork.org/en/scripts/478450-leetcode-cn-en-site-switcher'),
])
Markdown.title2(f, '脚本/Script')
Markdown.code(f, [
"pip install -r requirements.txt",
"python problem.py <leetcode/lintcode> [-l java|cpp|python(default)] [-t]",
"# 例如(e.g.):",
"python problem.py https://leetcode.com/problems/online-stock-span/",
"python problem.py https://www.lintcode.com/problem/92 -l cpp",
])
Markdown.title2(f, "链接/Links")
Markdown.bullet(f, [
Markdown.link('本人博客', 'https://blog.mogoal.com/category/#Algorithm'),
Markdown.link('极客时间', 'https://github.com/geektime-geekbang/algorithm-1'),
Markdown.link('LeetCode 101', 'https://github.com/changgyhub/leetcode_101'),
])
Markdown.title2(f, "书籍/Books")
Markdown.bullet(f, [
"《算法技术手册》/ Algorithms in a Nutshell",
"《STL源码剖析》/ The Annotated STL Sources",
"《算法心得:高效算法的奥秘》/ Hacker's Delight, 2nd Edition",
"《数学之美》(A chinese version book by Doctor Wujun)",
"《编程之美 : 微软技术面试心得》(A chinese version book by Mircosoft Developers)"
""
])
Markdown.title2(f, Markdown.link('Category', 'category'))
Markdown.bullet(f, [Markdown.tag(c) for c in SHOW_CATEGORIES])
Markdown.table_content(f, ALL_CATEGORIES)