Skip to content

Commit 7c47f75

Browse files
committed
Adding bin/update_index.py script.
This script generates the footers for all files. Rerun the script after adding new books or chapters in order to update the footer sections on each page with links to the next and previous chapters. The script works by recursively parsing "## Index" sections in files, starting with README.md. The footer is marked with an HTML comment, `<!--automatically generated footer-->`. Any text after this comment is destroyed by the script, so all edits should be made above that point.
1 parent 031e032 commit 7c47f75

1 file changed

Lines changed: 150 additions & 0 deletions

File tree

bin/update_index.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#!/usr/bin/env python
2+
"""
3+
A script to update footer links for all tutorial pages
4+
"""
5+
6+
import sys,os,re
7+
8+
class TutorialIndex(object):
9+
10+
footermark = u"<!--automatically generated footer-->"
11+
12+
def __init__(self,link,chapter=None,title=None,parent=None):
13+
"""Create a new TutorialIndex
14+
15+
:param link: A link to this page, relative to parent's link
16+
:param chapter: The chapter number, e.g. "Chapter 5"
17+
:param title: The chapter title, e.g. "Writing Docstrings"
18+
:param parent: The TutorialIndex which references this one
19+
"""
20+
self.link = link
21+
self.chapter = chapter
22+
self.title = title
23+
self.parent = parent
24+
self.children = []
25+
26+
def parse(self):
27+
"""Parse the index, add a footer, and do the same for each child
28+
found in the '# Index' section (if any).
29+
30+
Caution, this method overwrites any existing footer sections on this
31+
file and all children!
32+
"""
33+
34+
#Recognise and parse "<CHAPTER>: (<TITLE>)[<LINK>]"
35+
indexentry = re.compile("^(.*)[:-].*\[([^]]*)\]\(([^)]*)\).*$")
36+
37+
filename = self.rootlink()
38+
39+
with open(filename,"rw+") as file:
40+
line = file.readline()
41+
had_footer=False
42+
43+
# Parse file for index, truncate prior footer, and append footermark
44+
in_index = False
45+
while line:
46+
if line[0] == u"#": #That's a header, not a comment
47+
if u"index" in line.lower():
48+
in_index = True
49+
else:
50+
in_index = False
51+
elif line.strip() == TutorialIndex.footermark: # Footer already!
52+
had_footer=True
53+
file.truncate()
54+
break
55+
elif in_index:
56+
# look for 'Chapter 1: [Title](link)'
57+
result = indexentry.match(line)
58+
if result:
59+
chapter,title,link = result.groups()
60+
child = TutorialIndex(link,chapter,title,self)
61+
self.children.append(child)
62+
63+
line = file.readline()
64+
65+
# Append footer
66+
if not had_footer:
67+
file.write(u"\n")
68+
file.write(TutorialIndex.footermark)
69+
file.write(u"\n")
70+
footer = self.makefooter()
71+
file.write(footer)
72+
73+
# Recurse to children
74+
for child in self.children:
75+
child.parse()
76+
77+
def rootlink(self):
78+
"""Convert self.link to an absolute path relative to the root TutorialIndex
79+
:return: The path to this TutorialIndex relative to the root index
80+
"""
81+
if self.parent is None:
82+
return self.link
83+
parentlink = self.parent.rootlink()
84+
85+
return os.path.join(os.path.dirname(parentlink),self.link)
86+
87+
def makefooter(self):
88+
lines = ["---","","Navigation:"]
89+
# Iterate over parents
90+
p = self.parent
91+
linkmd = [self.makename()] #reverse order (self to root)
92+
while p is not None:
93+
name = p.makename()
94+
# Get a path to p relative to our own path
95+
link = os.path.relpath(p.rootlink(),os.path.dirname(self.rootlink()))
96+
linkmd.append("[{}]({})".format(name,link))
97+
p = p.parent
98+
linkmd.reverse()
99+
lines.append("\n| ".join(linkmd))
100+
101+
lines.append("")
102+
103+
if self.parent is not None:
104+
pos = self.parent.children.index(self) #Should always work
105+
if pos > 0:
106+
prev = self.parent.children[pos-1]
107+
name = prev.makename()
108+
link = os.path.relpath(prev.rootlink(),os.path.dirname(self.rootlink()))
109+
lines.append("Prev: [{}]({})".format(name,link))
110+
lines.append("")
111+
if pos < len(self.parent.children)-1:
112+
next = self.parent.children[pos+1]
113+
name = next.makename()
114+
link = os.path.relpath(next.rootlink(),os.path.dirname(self.rootlink()))
115+
lines.append("Next: [{}]({})".format(name,link))
116+
lines.append("")
117+
118+
#lines.append(self.makename()+", "+self.link)
119+
return "\n".join(lines)
120+
121+
def makename(self):
122+
""" Return a name, like "<CHAPTER>: <TITLE>"
123+
"""
124+
if self.chapter:
125+
name = self.chapter
126+
if self.title:
127+
name += ": " + self.title
128+
elif self.title:
129+
name = self.title
130+
else:
131+
name = self.link #last resort
132+
133+
return name
134+
135+
def __repr__(self):
136+
return "TutorialIndex({self.link!r},{self.chapter!r},{self.title!r},{parent!r})" \
137+
.format(self=self,parent=self.parent.title if self.parent else None)
138+
139+
if __name__ == "__main__":
140+
root = TutorialIndex("README.md",title="BioJava Tutorial")
141+
142+
root.parse()
143+
144+
# Output tree
145+
def pr(node,indent=""):
146+
print "{}{}\t{}".format(indent,node.link,node.rootlink())
147+
for n in node.children:
148+
pr(n,indent+"\t")
149+
150+
pr(root)

0 commit comments

Comments
 (0)