#! /usr/bin/python # vim: ts=4 sw=4 et set fileencoding=utf-8 from datetime import date, timedelta, datetime import os.path, errno, sys # CONFIG HTDOCS_DIR="/var/www/comics.spline.de/htdocs" IMAGE_DIR=HTDOCS_DIR + "/imgs/" TARGET_DIR=HTDOCS_DIR + "/sites/" TEMPLATE_DIR="/var/www/comics.spline.de/templates" from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader(TEMPLATE_DIR), autoescape=True) template = env.get_template('base.html') first_date = date(1997,01,01) comics = { "dilbert": { "name": "Dilbert", "url": "http://www.dilbert.com/", "imageformat": "gif" }, "xkcd": { "name": "xkcd", "url": "http://www.xkcd.com", "imageformat": "png" }, "calvin": { "name": "Calvin", "url": "http://www.ucomics.com/calvinandhobbes/viewch.htm", "imageformat": "gif" }, "garfield": { "name": "garfield", "url": "http://garfield.ucomics.com/garfield/gaview.htm", "imageformat": "gif" }, "touche": { "name": u"Touché", "url": "http://www.taz.de/", "imageformat": "gif" }, "userfriendly": { "name": "Userfriendly", "url": "http://www.userfriendly.org/static/", "imageformat": "gif" }, "zits": { "name": "Zits", "url": "http://www.kingfeatures.com/features/comics/zits/about.htm", "imageformat": "gif" }, "nichtlustig": { "name": "Nicht lustig!", "url": "http://www.nichtlustig.de", "imageformat": "jpg" }, "claybennett": { "name": "Clay Bennett", "url": "http://www.csmonitor.com/commentary/index.html", "imageformat": "jpg" }, "geekandpoke": { "name": "Geek and Poke", "url": "http://geekandpoke.typepad.com/", "imageformat": "jpg" }, "snoopy": { "name": "Snoopy", "imageformat": "gif", "url": "http://www.gocomics.com/peanuts" }, "cyanide": { "name": "Cyanide and Happiness", "imageformat": "png", "url": "http://www.explosm.net/comics/" }, "phdcomics": { "name": "PHD comics", "imageformat": "gif", "url": "http://www.phdcomics.com/comics/archive.php" }, "smbc": { "name": "SMBC", "imageformat": "png", "url": "http://www.smbc-comics.com/index.php" }, "nerdragecomic": { "name": "Nerd Rage", "imageformat": "jpg", "url": "http://nerdragecomic.com/index.php" }, "jeremykaye": { "name": "Up and Out", "imageformat": "png", "url": "http://jeremykaye.tumblr.com/" }, } # source: http://www.ianlewis.org/en/python-date-range-iterator def datetimeIterator(from_date=None, to_date=None, delta=timedelta(minutes=1)): from_date = from_date or datetime.now() while to_date is None or from_date <= to_date: yield from_date from_date = from_date + delta return def render_page(date, entries, prev=None, next=None): out_file = date.strftime("%%s/%Y/%m/%d.html") % TARGET_DIR try: os.makedirs(os.path.dirname(out_file)) except OSError as exc: if exc.errno == errno.EEXIST: pass else: raise text = template.render(entries=entries, date=date, next=next, prev=prev) with open(out_file,'w') as f: f.write(text.encode('utf8')) def render_all(): date_range = datetimeIterator(first_date, date.today(), timedelta(days=1)) relevant_dates = [d for d in date_range if exists_any_comic(d)] relevant_dates.append(None) prev_date, cur_date = None, None for next_date in relevant_dates: if cur_date: render_page(cur_date, gather_all_entries(cur_date), prev_date, next_date) prev_date, cur_date = cur_date, next_date def find_surrounding_date(cur, delta): new_date = cur + delta while new_date >= first_date and new_date <= date.today(): if exists_any_comic(new_date): return new_date new_date += delta return None def render_date(cur_date, recurse=True): if not exists_any_comic(cur_date): return prev_date = find_surrounding_date(cur_date, timedelta(days=-1)) next_date = find_surrounding_date(cur_date, timedelta(days=1)) render_page(cur_date, gather_all_entries(cur_date), prev_date, next_date) print "Rendered page for: %s" % cur_date if recurse and prev_date: render_date(prev_date, False) if recurse and next_date: render_date(next_date, False) def generate_basename(comic_id, date): return date.strftime("%%s/%%s/%Y-%m/%d") % (IMAGE_DIR, comic_id) def add_support_content(entry, basename, type): if os.path.isfile(basename + "." + type): with open(basename + "." + type) as f: entry[type] = f.read().strip().decode('utf8') def gather_all_entries(my_date): entries = [] for (comic_id, comic) in comics.iteritems(): basename = generate_basename(comic_id, my_date) expected_path = basename + "." + comic["imageformat"] if os.path.isfile(expected_path): if expected_path.startswith(HTDOCS_DIR): expected_path = expected_path[len(HTDOCS_DIR):] entry = { "path": expected_path, "comic": comic, } add_support_content(entry, basename, "title"); add_support_content(entry, basename, "alt"); entries.append(entry) return entries def exists_any_comic(my_date): for (comic_id, comic) in comics.iteritems(): basename = generate_basename(comic_id, my_date) expected_path = basename + "." + comic["imageformat"] if os.path.isfile(expected_path): return True return False if __name__ == "__main__": if len(sys.argv) > 1 and sys.argv[1] == "-a": render_all() else: ago = date.today() try: if len(sys.argv) > 1: ago -= timedelta(days=int(sys.argv[1])) except ValueError: pass render_date(ago)