|
- #!/usr/bin/env python3
-
- import codecs
- import logging
- import os
- from shutil import copyfile
-
- import jinja2
-
- logging.basicConfig()
- log = logging.getLogger("build")
- log.setLevel(logging.INFO)
-
- class App(object):
- # Global template variables.
- def vars(self):
- return {
- "site": {
- "name": "noah.is",
- },
- }
-
- def __init__(self):
- # Jinja environment.
- self.env = jinja2.Environment(
- loader=jinja2.FileSystemLoader("./templates"),
- )
-
- # Collect all the templates and all the static files.
- self.templates = self.crawl("./templates") # TODO: remove list() wrapper
- self.static = self.crawl("./static")
-
- # Copy all of the static files into the public root.
- if not os.path.isdir("./public"):
- self.mkdir("./public")
- for static in self.static:
- public = static.replace("./static/", "./public/")
- self.mkdir(public.rsplit("/", 1)[0]) # ensure the path will exist
- if self.should_copy_static(static, public):
- log.info("COPY STATIC FILE %s -> %s", static, public)
- copyfile(static, public)
-
- # And render all of the templates.
- for template in self.templates:
- if template.startswith("./templates/layout"): # don't create these
- continue
- public = self.template_to_public(template)
- self.mkdir(public.rsplit("/", 1)[0]) # ensure the path will exist
- log.info("RENDER TEMPLATE: %s -> %s", template, public)
- self.render_template(template, public)
-
- def render_template(self, template, public):
- """
- Render a template to the public path.
-
- Parameters:
- template (str): path in ./templates
- public (str): path in ./public
- """
- tpl = self.env.get_template(template.replace("./templates/", ""))
- html = tpl.render(self.vars())
-
- with codecs.open(public, "w", "utf-8") as fh:
- fh.write(html)
-
-
- def crawl(self, path):
- """
- Crawl a directory recursively and return a list of file paths.
-
- Parameters:
- path (str)
-
- Returns:
- filepaths (list[str])
- """
- for root, dirs, files in os.walk(path):
- for file in files:
- yield os.path.join(root, file)
-
- def should_copy_static(self, static_file, public_file):
- """
- Determine if a static file should be copied over to the public folder.
-
- This means the public file either does not exist yet, or is older
- than the static file and so the new static file should be copied over it.
-
- Parameters:
- static_file (str)
- public_file (str)
-
- Returns:
- bool: True if public_file doesn't exist or is older than static_file.
- """
- if not os.path.exists(public_file):
- return True
- return os.stat(static_file).st_mtime > os.stat(public_file).st_mtime
-
- def template_to_public(self, template_filename):
- """
- Convert a template filename into a public name, preferring clean URL
- paths without file extension suffixes.
-
- Examples:
- * /index.html -> /index.html
- * /about.html -> /about/index.html
- * /photos/index.html -> /photos/index.html
- * /photos/2018.html -> /photos/2018/index.html
-
- The `.html` and `.md` file types will suppress suffixes in this way.
-
- Parameters:
- template_filename: like ``./templates/about.html``
-
- Returns:
- public_filename: like ``./public/about/index.html``
- """
- public = template_filename.replace("./templates/", "./public/")
- path, filename = public.rsplit("/", 1)
- basename, ext = filename.rsplit(".", 1)
- if ext not in ["html", "md"]:
- # Not a web page so just keep the path literal.
- return public
-
- print("template_to_public:", template_filename, public)
-
- # See if it's already an index page or if we need to create one.
- if filename in ["index.html", "index.md"]:
- return public # already a good name
- else:
- print("HERE:", os.path.join(path, basename, "index."+ext))
- log.info("basename=%r path=%r filename=%s", basename, path, filename)
- return os.path.join(path, basename, "index."+ext)
-
- def mkdir(self, path):
- """Create a directory and log it."""
- if not os.path.isdir(path):
- log.info("mkdir: %s", path)
- os.makedirs(path, mode=0o755)
-
- if __name__ == "__main__":
- App()
|