summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/metabuild.py8
-rw-r--r--site_builder/__init__.py39
-rw-r--r--site_builder/blog.py50
-rw-r--r--site_builder/blogbuilder.py43
-rw-r--r--site_builder/feeds.py67
-rw-r--r--templates/page.html11
6 files changed, 106 insertions, 112 deletions
diff --git a/bin/metabuild.py b/bin/metabuild.py
index 6f95703..e837a51 100755
--- a/bin/metabuild.py
+++ b/bin/metabuild.py
@@ -22,16 +22,16 @@ del dirname, realpath
22from site_builder import build_all 22from site_builder import build_all
23from site_builder.blogbuilder import build_blog 23from site_builder.blogbuilder import build_blog
24 24
25if __name__ == '__main__': 25if __name__ == "__main__":
26 args = sys.argv[1:] 26 args = sys.argv[1:]
27 27
28 if len(args) != 3: 28 if len(args) != 3:
29 print "{0} (blog|site) input_dir output_dir".format(sys.argv[0]) 29 print("{0} (blog|site) input_dir output_dir".format(sys.argv[0]))
30 sys.exit(1) 30 sys.exit(1)
31 else: 31 else:
32 action, input_dir, output_dir = args 32 action, input_dir, output_dir = args
33 33
34 if action == 'site': 34 if action == "site":
35 build_all(input_dir, output_dir) 35 build_all(input_dir, output_dir)
36 elif action == 'blog': 36 elif action == "blog":
37 build_blog(input_dir, output_dir) 37 build_blog(input_dir, output_dir)
diff --git a/site_builder/__init__.py b/site_builder/__init__.py
index 1ebef36..9478dce 100644
--- a/site_builder/__init__.py
+++ b/site_builder/__init__.py
@@ -7,15 +7,13 @@ Site Builder
7""" 7"""
8 8
9import os 9import os
10from os import path
11import jinja2 10import jinja2
12import pagebuilder
13from datetime import datetime 11from datetime import datetime
14from docutils.core import publish_parts 12from docutils.core import publish_parts
15from docutils.io import FileInput
16 13
17 14
18TEMPLATES = path.join(path.dirname(path.dirname(__file__)), "templates") 15TEMPLATES = os.path.join(
16 os.path.dirname(os.path.dirname(__file__)), "templates")
19 17
20 18
21def get_template(name): 19def get_template(name):
@@ -25,27 +23,27 @@ def get_template(name):
25 23
26 24
27def build_standard_page(filename, output_name): 25def build_standard_page(filename, output_name):
28 parts = publish_parts(open(filename, 'r').read(), writer_name='html') 26 parts = publish_parts(open(filename, "r").read(), writer_name="html")
29 template = get_template('page.html') 27 template = get_template("page.html")
30 28
31 try: 29 try:
32 os.makedirs(path.dirname(output_name)) 30 os.makedirs(os.path.dirname(output_name))
33 except OSError: 31 except OSError:
34 pass # directory exists 32 pass # directory exists
35 33
36 open(output_name, 'w').write(template.render( 34 open(output_name, "w").write(template.render(
37 contents=parts['html_body'], 35 contents=parts["html_body"],
38 build_date=datetime.now().strftime('%B %d, %Y'), 36 build_date=datetime.now().strftime("%B %d, %Y"),
39 source_link=filename)) 37 source_link=filename))
40 38
41 39
42def get_output_name(base_dir, output_dir, filename): 40def get_output_name(base_dir, output_dir, filename):
43 base_depth = len(base_dir.split(path.sep)) 41 base_depth = len(base_dir.split(os.path.sep)) - 1
44 out_name = filename.split(path.sep)[base_depth:] 42 out_name = filename.split(os.path.sep)[base_depth:]
45 new_path = path.join(output_dir, *out_name) 43 new_path = os.path.join(output_dir, *out_name)
46 44
47 if new_path.endswith('.rst'): 45 if new_path.endswith(".rst"):
48 new_path = new_path[:-len('.rst')] + '.html' 46 new_path = new_path[:-len(".rst")] + ".html"
49 47
50 return new_path 48 return new_path
51 49
@@ -53,13 +51,16 @@ def get_output_name(base_dir, output_dir, filename):
53def build_all(base_dir, output_dir): 51def build_all(base_dir, output_dir):
54 for root, dirs, files in os.walk(base_dir): 52 for root, dirs, files in os.walk(base_dir):
55 for filename in files: 53 for filename in files:
56 if ('blog.cfg' in files or 54 if ("blog.cfg" in files or
57 not filename.endswith('.rst')): 55 not filename.endswith(".rst")):
58 continue 56 continue
59 57
60 old_path = path.join(root, filename) 58 old_path = os.path.join(root, filename)
61 new_path = get_output_name(base_dir, output_dir, old_path) 59 new_path = get_output_name(base_dir, output_dir, old_path)
62 60
63 print "BUILDING: ", old_path 61 if not os.path.exists(os.path.dirname(new_path)):
62 os.makedirs(os.path.dirname(new_path))
63
64 print("BUILDING: ", old_path, " to ", new_path)
64 65
65 build_standard_page(old_path, new_path) 66 build_standard_page(old_path, new_path)
diff --git a/site_builder/blog.py b/site_builder/blog.py
index a8ef446..9a1b48d 100644
--- a/site_builder/blog.py
+++ b/site_builder/blog.py
@@ -7,17 +7,15 @@ Blog Post Builder
7""" 7"""
8 8
9import os 9import os
10from functools import wraps
11from datetime import datetime 10from datetime import datetime
12 11
13# Docutils imports, crazy yo
14from docutils import nodes 12from docutils import nodes
15from docutils.core import Publisher, publish_string 13from docutils.parsers import rst
14from docutils.core import Publisher
15from docutils.readers import standalone
16from docutils.transforms import Transform 16from docutils.transforms import Transform
17from docutils.io import NullOutput, FileInput 17from docutils.io import NullOutput, FileInput
18from docutils.parsers.rst import Parser as RSTParser 18from docutils.writers import html4css1 as html
19from docutils.writers.html4css1 import Writer as HTMLWriter
20from docutils.readers.standalone import Reader as StandaloneReader
21 19
22 20
23class BlogMetaTransform(Transform): 21class BlogMetaTransform(Transform):
@@ -34,9 +32,7 @@ class BlogMetaTransform(Transform):
34 def __init__(self, *args, **kwargs): 32 def __init__(self, *args, **kwargs):
35 Transform.__init__(self, *args, **kwargs) 33 Transform.__init__(self, *args, **kwargs)
36 34
37 self.meta = self.document.blog_meta = { 35 self.meta = self.document.blog_meta = {"tags": []}
38 'tags': [],
39 }
40 36
41 def apply(self): 37 def apply(self):
42 docinfo = None 38 docinfo = None
@@ -50,7 +46,7 @@ class BlogMetaTransform(Transform):
50 self.document.remove(node) 46 self.document.remove(node)
51 47
52 if isinstance(node, nodes.title): 48 if isinstance(node, nodes.title):
53 self.meta['title'] = unicode(node[0]) 49 self.meta["title"] = unicode(node[0])
54 self.document.remove(node) 50 self.document.remove(node)
55 51
56 # And one to process the docinfo 52 # And one to process the docinfo
@@ -65,23 +61,23 @@ class BlogMetaTransform(Transform):
65 self._handle_field(node) 61 self._handle_field(node)
66 62
67 def _handle_author(self, node): 63 def _handle_author(self, node):
68 self.meta['author'] = Author(node[0]['name'], node[0]['refuri']) 64 self.meta["author"] = Author(node[0]["name"], node[0]["refuri"])
69 65
70 def _handle_date(self, node): 66 def _handle_date(self, node):
71 raw_date = unicode(node[0]) 67 raw_date = unicode(node[0])
72 self.meta['post_date'] = datetime.strptime(raw_date, 68 self.meta["post_date"] = datetime.strptime(raw_date,
73 '%a %b %d %H:%M:%S %Y') 69 "%a %b %d %H:%M:%S %Y")
74 70
75 def _handle_field(self, node): 71 def _handle_field(self, node):
76 name = node[0][0] 72 name = node[0][0]
77 value = unicode(node[1][0][0]) 73 value = unicode(node[1][0][0])
78 74
79 if name == 'Tag': 75 if name == "Tag":
80 self.meta['tags'].append(value) 76 self.meta["tags"].append(value)
81 77
82 78
83 79
84class BlogPostReader(StandaloneReader): 80class BlogPostReader(standalone.Reader):
85 """ 81 """
86 Post reader for blog posts. 82 Post reader for blog posts.
87 83
@@ -90,9 +86,7 @@ class BlogPostReader(StandaloneReader):
90 """ 86 """
91 87
92 def get_transforms(self): 88 def get_transforms(self):
93 return StandaloneReader.get_transforms(self) + [ 89 return standalone.Reader.get_transforms(self) + [BlogMetaTransform]
94 BlogMetaTransform,
95 ]
96 90
97 91
98class Author(object): 92class Author(object):
@@ -104,11 +98,11 @@ class Author(object):
104 self.name = name 98 self.name = name
105 self.email = email 99 self.email = email
106 100
107 if email.startswith('mailto:'): 101 if email.startswith("mailto:"):
108 self.email = email[len('mailto:'):] 102 self.email = email[len("mailto:"):]
109 103
110 def __str__(self): 104 def __str__(self):
111 return '{0} <{1}>'.format(self.name, self.email) 105 return "{0} <{1}>".format(self.name, self.email)
112 106
113 107
114class BlogPost(object): 108class BlogPost(object):
@@ -150,29 +144,29 @@ class BlogPost(object):
150 """ 144 """
151 pub = Publisher(destination_class=NullOutput, 145 pub = Publisher(destination_class=NullOutput,
152 source=FileInput(source_path=filename), 146 source=FileInput(source_path=filename),
153 reader=BlogPostReader(), writer=HTMLWriter(), 147 reader=BlogPostReader(), writer=html.Writer(),
154 parser=RSTParser()) 148 parser=rst.Parser())
155 149
156 pub.get_settings() # This is not sane. 150 pub.get_settings() # This is not sane.
157 pub.settings.traceback = True # Damnit 151 pub.settings.traceback = True # Damnit
158 pub.publish() 152 pub.publish()
159 153
160 meta = pub.document.blog_meta 154 meta = pub.document.blog_meta
161 post = cls(meta['title'], meta['post_date'], meta['author'], 155 post = cls(meta["title"], meta["post_date"], meta["author"],
162 meta['tags'], pub.writer.parts['html_body']) 156 meta["tags"], pub.writer.parts["html_body"])
163 157
164 post.filename = filename 158 post.filename = filename
165 159
166 return post 160 return post
167 161
168 162
169def load_post_index(directory='.'): 163def load_post_index(directory="."):
170 """ 164 """
171 Scan the current directory for rst files and build an index. 165 Scan the current directory for rst files and build an index.
172 """ 166 """
173 posts = [] 167 posts = []
174 for filename in os.listdir(directory): 168 for filename in os.listdir(directory):
175 if not filename.endswith('.rst'): 169 if not filename.endswith(".rst"):
176 continue 170 continue
177 171
178 filename = os.path.join(directory, filename) 172 filename = os.path.join(directory, filename)
diff --git a/site_builder/blogbuilder.py b/site_builder/blogbuilder.py
index f45fb65..060049c 100644
--- a/site_builder/blogbuilder.py
+++ b/site_builder/blogbuilder.py
@@ -11,61 +11,62 @@ import json
11import operator 11import operator
12from datetime import datetime 12from datetime import datetime
13from collections import defaultdict 13from collections import defaultdict
14from site_builder import get_template, get_output_name 14
15from blog import load_post_index 15from . import get_template
16from feeds import Atom1Feed 16from .blog import load_post_index
17from .feeds import Atom1Feed
17 18
18 19
19def build_feed(output_dir, post_index, title, url, feed_url): 20def build_feed(output_dir, post_index, title, url, feed_url):
20 page_name = os.path.join(output_dir, 'feed.atom') 21 page_name = os.path.join(output_dir, "feed.atom")
21 feed = Atom1Feed(post_index, title, feed_url, 22 feed = Atom1Feed(post_index, title, feed_url,
22 post_index[0].post_date, url) 23 post_index[0].post_date, url)
23 24
24 open(page_name, 'w').write(feed.get_feed()) 25 open(page_name, "w").write(feed.get_feed())
25 26
26 27
27def build_tags(output_dir, post_index): 28def build_tags(output_dir, post_index):
28 tag_index = defaultdict(list) 29 tag_index = defaultdict(list)
29 30
30 template = get_template('blog_tags.html') 31 template = get_template("blog_tags.html")
31 page_name = os.path.join(output_dir, 'tags.html') 32 page_name = os.path.join(output_dir, "tags.html")
32 33
33 for post in post_index: 34 for post in post_index:
34 for tag in post.tags: 35 for tag in post.tags:
35 tag_index[tag].append(post) 36 tag_index[tag].append(post)
36 37
37 tag_index = sorted(tag_index.items()) 38 tag_index = sorted(tag_index.items())
38 open(page_name, 'w').write(template.render(posts=tag_index, 39 open(page_name, "w").write(template.render(posts=tag_index,
39 build_date=datetime.now().strftime("%B %d, %Y"))) 40 build_date=datetime.now().strftime("%B %d, %Y")))
40 41
41 42
42def build_archive(output_dir, post_index): 43def build_archive(output_dir, post_index):
43 date_index = defaultdict(list) 44 date_index = defaultdict(list)
44 45
45 template = get_template('blog_archive.html') 46 template = get_template("blog_archive.html")
46 page_name = os.path.join(output_dir, 'archive.html') 47 page_name = os.path.join(output_dir, "archive.html")
47 48
48 for post in post_index: 49 for post in post_index:
49 date_index[post.post_date.year].append(post) 50 date_index[post.post_date.year].append(post)
50 51
51 date_index = sorted(date_index.items(), reverse=True) 52 date_index = sorted(date_index.items(), reverse=True)
52 open(page_name, 'w').write(template.render(posts=date_index, 53 open(page_name, "w").write(template.render(posts=date_index,
53 build_date=datetime.now().strftime("%B %d, %Y"))) 54 build_date=datetime.now().strftime("%B %d, %Y")))
54 55
55 56
56def build_index(output_dir, post_index): 57def build_index(output_dir, post_index):
57 template = get_template('blog_index.html') 58 template = get_template("blog_index.html")
58 page_name = os.path.join(output_dir, 'index.html') 59 page_name = os.path.join(output_dir, "index.html")
59 60
60 open(page_name, 'w').write(template.render(posts=post_index[:3], 61 open(page_name, "w").write(template.render(posts=post_index[:3],
61 build_date=datetime.now().strftime("%B %d, %Y"))) 62 build_date=datetime.now().strftime("%B %d, %Y")))
62 63
63 64
64def build_blog(base_dir, output_dir): 65def build_blog(base_dir, output_dir):
65 config = json.load(open(os.path.join(base_dir, 'blog.cfg'))) 66 config = json.load(open(os.path.join(base_dir, "blog.cfg")))
66 67
67 post_index = load_post_index(base_dir) 68 post_index = load_post_index(base_dir)
68 post_index.sort(key=operator.attrgetter('post_date'), reverse=True) 69 post_index.sort(key=operator.attrgetter("post_date"), reverse=True)
69 70
70 try: 71 try:
71 os.makedirs(output_dir) 72 os.makedirs(output_dir)
@@ -73,17 +74,17 @@ def build_blog(base_dir, output_dir):
73 pass # directory already exists 74 pass # directory already exists
74 75
75 for post in post_index: 76 for post in post_index:
76 template = get_template('blog_post.html') 77 template = get_template("blog_post.html")
77 78
78 out_filename = os.path.join(output_dir, post.filename) 79 out_filename = os.path.join(output_dir, post.filename)
79 out_filename = out_filename[:-len('rst')] + 'html' 80 out_filename = out_filename[:-len("rst")] + "html"
80 81
81 print "BUILDING BLOG: ", out_filename 82 print("BUILDING BLOG: ", out_filename)
82 83
83 open(out_filename, 'w').write(template.render(post=post)) 84 open(out_filename, "w").write(template.render(post=post))
84 85
85 build_index(output_dir, post_index) 86 build_index(output_dir, post_index)
86 build_archive(output_dir, post_index) 87 build_archive(output_dir, post_index)
87 build_tags(output_dir, post_index) 88 build_tags(output_dir, post_index)
88 build_feed(output_dir, post_index, 89 build_feed(output_dir, post_index,
89 config['title'], config['blog_url'], config['feed_url']) 90 config["title"], config["blog_url"], config["feed_url"])
diff --git a/site_builder/feeds.py b/site_builder/feeds.py
index 3279afb..dd343b5 100644
--- a/site_builder/feeds.py
+++ b/site_builder/feeds.py
@@ -6,14 +6,13 @@ Atom Feed Writer
6@date: June 04, 2010 6@date: June 04, 2010
7""" 7"""
8 8
9#from io import StringIO 9from io import StringIO
10from StringIO import StringIO
11from xml.sax.saxutils import XMLGenerator 10from xml.sax.saxutils import XMLGenerator
12 11
13 12
14class SimpleXMLGenerator(XMLGenerator): 13class SimpleXMLGenerator(XMLGenerator):
15 14
16 def __init__(self, encoding='utf-8'): 15 def __init__(self, encoding="utf-8"):
17 self.output = StringIO() 16 self.output = StringIO()
18 XMLGenerator.__init__(self, out=self.output, encoding=encoding) 17 XMLGenerator.__init__(self, out=self.output, encoding=encoding)
19 18
@@ -53,8 +52,8 @@ class Atom1Feed(object):
53 52
54 def get_feed(self): 53 def get_feed(self):
55 self.handler.startDocument() 54 self.handler.startDocument()
56 self.handler.startElement('feed', { 55 self.handler.startElement("feed", {
57 'xmlns': 'http://www.w3.org/2005/Atom' }) 56 "xmlns": "http://www.w3.org/2005/Atom" })
58 57
59 self.add_root_elements() 58 self.add_root_elements()
60 59
@@ -64,47 +63,47 @@ class Atom1Feed(object):
64 63
65 self.add_post(post) 64 self.add_post(post)
66 65
67 self.handler.endElement('feed') 66 self.handler.endElement("feed")
68 67
69 return self.handler.get_contents() 68 return self.handler.get_contents()
70 69
71 def add_root_elements(self): 70 def add_root_elements(self):
72 self.handler.addElement('title', self.title) 71 self.handler.addElement("title", self.title)
73 self.handler.addElement('updated', self._format_time(self.updated)) 72 self.handler.addElement("updated", self._format_time(self.updated))
74 self.handler.addElement('id', self.feed_url) 73 self.handler.addElement("id", self.feed_url)
75 self.handler.addElement('link', attrs={ 74 self.handler.addElement("link", attrs={
76 'rel': 'alternate', 75 "rel": "alternate",
77 'type': 'text/html', 76 "type": "text/html",
78 'href': self.blog_url }) 77 "href": self.blog_url })
79 self.handler.addElement('link', attrs={ 78 self.handler.addElement("link", attrs={
80 'rel': 'self', 79 "rel": "self",
81 'type': 'application/atom+xml', 80 "type": "application/atom+xml",
82 'href': self.feed_url }) 81 "href": self.feed_url })
83 82
84 def add_post(self, post): 83 def add_post(self, post):
85 handler = self.handler 84 handler = self.handler
86 85
87 handler.startElement('entry') 86 handler.startElement("entry")
88 87
89 handler.startElement('author') 88 handler.startElement("author")
90 handler.addElement('name', post.author.name) 89 handler.addElement("name", post.author.name)
91 handler.addElement('email', post.author.email) 90 handler.addElement("email", post.author.email)
92 handler.endElement('author') 91 handler.endElement("author")
93 92
94 post_href = '{0}/{1}'.format(self.blog_url, post.filename) 93 post_href = "{0}/{1}".format(self.blog_url, post.filename)
95 94
96 handler.addElement('title', post.title) 95 handler.addElement("title", post.title)
97 handler.addElement('link', attrs={ 96 handler.addElement("link", attrs={
98 'rel': 'alternate', 97 "rel": "alternate",
99 'type': 'text/html', 98 "type": "text/html",
100 'href': post_href }) 99 "href": post_href })
101 handler.addElement('id', post_href) 100 handler.addElement("id", post_href)
102 handler.addElement('updated', self._format_time(post.post_date)) 101 handler.addElement("updated", self._format_time(post.post_date))
103 handler.addElement('published', self._format_time(post.post_date)) 102 handler.addElement("published", self._format_time(post.post_date))
104 103
105 for tag in post.tags: 104 for tag in post.tags:
106 handler.addElement('category', attrs={ 'term': tag }) 105 handler.addElement("category", attrs={ "term": tag })
107 106
108 handler.addElement('content', post.contents, attrs={ 'type': 'html' }) 107 handler.addElement("content", post.contents, attrs={ "type": "html" })
109 108
110 handler.endElement('entry') 109 handler.endElement("entry")
diff --git a/templates/page.html b/templates/page.html
index 83fa0dc..7f6c767 100644
--- a/templates/page.html
+++ b/templates/page.html
@@ -7,12 +7,12 @@
7<meta name="generator" content="Ventriloquy 0.1" /> 7<meta name="generator" content="Ventriloquy 0.1" />
8<meta name="author" content="Michael Crute" /> 8<meta name="author" content="Michael Crute" />
9<title>The Random Thoughts of a Programmer</title> 9<title>The Random Thoughts of a Programmer</title>
10<style type="text/css">@import url('/resources/site-min.css');</style> 10<style type="text/css">@import url('/site-min.css');</style>
11</head> 11</head>
12<body> 12<body>
13 13
14<div id="header"> 14<div id="header">
15 <h1 class="headline"><a href="/">mike<span>.</span>crute<span>.org</span></a></h1> 15 <h1 class="headline"><a href="/">mike<span>.</span>crute<span>.us</span></a></h1>
16</div> 16</div>
17 17
18<div id="contents"> 18<div id="contents">
@@ -40,15 +40,14 @@
40 <li><span class="label">Site Links:</span></li> 40 <li><span class="label">Site Links:</span></li>
41 <li><a href="/">Home</a></li> 41 <li><a href="/">Home</a></li>
42 <li><a href="/blog">Blog</a></li> 42 <li><a href="/blog">Blog</a></li>
43 <li><a href="/personal_blog">Personal Blog</a></li> 43 <li><a href="https://code.crute.us">Code</a></li>
44 <li><a href="/code">Code</a></li> 44 <li><a href="https://twitter.com/mcrute">Twitter</a></li>
45 <li><a href="http://twitter.com/mcrute">Twitter</a></li>
46 </ul> 45 </ul>
47 46
48 {% block update_date %} 47 {% block update_date %}
49 <p class="updated"><span class="label">Page last updated:</span> {{ build_date }}</p> 48 <p class="updated"><span class="label">Page last updated:</span> {{ build_date }}</p>
50 {% endblock %} 49 {% endblock %}
51 <p class="copyright">Copyright &copy; 2010 Michael E. Crute &lt;<a href="mailto:mcrute@gmail.com">mcrute@gmail.com</a>&gt;. Contact me for other uses.</p> 50 <p class="copyright">Copyright &copy; 2020 Michael E. Crute. All rights reserved. Contact me for other uses.</p>
52 <p class="poweredby">This site powered by <a href="http://www.vim.org">VIM</a>, <a href="http://docutils.sourceforge.net">ReStructuredText</a> and <a href="http://python.org">Python</a>.</p> 51 <p class="poweredby">This site powered by <a href="http://www.vim.org">VIM</a>, <a href="http://docutils.sourceforge.net">ReStructuredText</a> and <a href="http://python.org">Python</a>.</p>
53</div> 52</div>
54</div> 53</div>