742 lines
32 KiB
Python
742 lines
32 KiB
Python
# CURRENT:
|
|
|
|
# TODO: Shared
|
|
# TODO: generate TARGET_EBOOK
|
|
# TODO: Send HTML & CSS to ebook format converter:
|
|
# TODO: pdf generation
|
|
# - Both mobi and pdf generation are done via Qt. Calibre is linked against glibc 3.13,
|
|
# and the shared hosting has an older version. So Calibre can't find parts of Qt.
|
|
# TODO: generate TARGET_WEBSITE
|
|
# TODO: Script invocation needs to be more flexible.
|
|
# - It should be possible to regenerate one file.
|
|
# - the home page, with discuss comments.
|
|
# TODO: The update script should poll disqus and get the relevant data.
|
|
# - the home page, with featured article changing periodically (random selection from valid candidates with right data).
|
|
# TODO: Go through articles and add featured status elements.
|
|
# TODO: Have some sort of database with last update status, which an update script hits.
|
|
# - any article page, with linked resources being updated as needed.
|
|
# TODO: Reddit posts and comments.
|
|
# TODO: Disqus comments are now added to all issue articles in the website.
|
|
# Could add comment counts to page titles or to links to issue articles:
|
|
# https://help.disqus.com/customer/portal/articles/565624-tightening-your-disqus-integration
|
|
# Where else to add disqus comments?
|
|
# - Other issue pages than articles?
|
|
# - Home page?
|
|
# - Contribute page? Other pages?
|
|
# TODO: RSS feed may need to include announcements.
|
|
# - add from text file.
|
|
# - edit in text file.
|
|
#
|
|
|
|
import calendar
|
|
import codecs
|
|
import distutils
|
|
import distutils.dir_util # Not needed on windows.
|
|
import distutils.file_util # Not needed on windows.
|
|
import email.utils
|
|
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
import jinja2
|
|
import feedformatter
|
|
|
|
FLAG_ONLINE = 1 << 31
|
|
|
|
TARGET_EBOOK = 1
|
|
TARGET_WEBSITE = 2
|
|
|
|
|
|
# This defines where to look for the base template for a given target.
|
|
templates_by_target = {
|
|
TARGET_EBOOK: os.path.join("templates", "ebook"),
|
|
TARGET_WEBSITE | FLAG_ONLINE: os.path.join("templates", "website"),
|
|
TARGET_WEBSITE: os.path.join("templates", "website"),
|
|
}
|
|
|
|
""" Variable to configure for generation of final result with side-effects. """
|
|
setting_finalise = True
|
|
|
|
|
|
setting_domain_name = "journal.imaginary-realities.com"
|
|
|
|
PAGE_OTHER = 1
|
|
PAGE_ARTICLE = 2
|
|
|
|
issue_data = {
|
|
# (volume_number, issue_number, (year, month))
|
|
(5,1,(2013,12)): [
|
|
(PAGE_OTHER, "introduction"),
|
|
(PAGE_OTHER, "copyright"),
|
|
(PAGE_ARTICLE, "modern-interface-modern-mud"),
|
|
(PAGE_ARTICLE, "well-built-zone-work-art"),
|
|
(PAGE_ARTICLE, "journey-through-paradice"),
|
|
(PAGE_ARTICLE, "blind-accessibility-challenges-opportunities"),
|
|
(PAGE_ARTICLE, "evennia-introduction"),
|
|
(PAGE_ARTICLE, "getting-roleplaying-scene-going"),
|
|
(PAGE_ARTICLE, "introducing-new-players-redesigning-mud-school"),
|
|
(PAGE_ARTICLE, "hunger-game-learned-break-ship-bottle"),
|
|
(PAGE_ARTICLE, "help-save-old-mudding-resources"),
|
|
(PAGE_OTHER, "request-for-content"),
|
|
(PAGE_OTHER, "staff"),
|
|
],
|
|
(6,1,(2014,4)): [
|
|
(PAGE_OTHER, "introduction"),
|
|
(PAGE_OTHER, "copyright"),
|
|
(PAGE_ARTICLE, "a-journey-through-paradice-ii"),
|
|
(PAGE_ARTICLE, "building-a-mech-in-evennia"),
|
|
(PAGE_ARTICLE, "describing-a-virtual-world"),
|
|
(PAGE_ARTICLE, "dynamic-room-descriptions"),
|
|
(PAGE_ARTICLE, "saddle-up"),
|
|
(PAGE_ARTICLE, "the-successful-quest-builder"),
|
|
(PAGE_ARTICLE, "your-mud-should-have-an-account-system"),
|
|
(PAGE_ARTICLE, "help-save-old-mudding-resources"),
|
|
(PAGE_OTHER, "request-for-content"),
|
|
(PAGE_OTHER, "staff"),
|
|
],
|
|
(7,1,(2015,1)): [
|
|
(PAGE_OTHER, "introduction"),
|
|
(PAGE_OTHER, "copyright"),
|
|
(PAGE_ARTICLE, "choosing-an-emoting-system"),
|
|
(PAGE_ARTICLE, "dungeon-keeper"),
|
|
(PAGE_ARTICLE, "what-i-do-now"),
|
|
(PAGE_ARTICLE, "worlds-in-which-we-wander"),
|
|
(PAGE_OTHER, "request-for-content"),
|
|
(PAGE_OTHER, "staff"),
|
|
],
|
|
(7,2,(2015,4)): [
|
|
(PAGE_OTHER, "introduction"),
|
|
(PAGE_OTHER, "copyright"),
|
|
(PAGE_ARTICLE, "bartering"),
|
|
(PAGE_ARTICLE, "is-structuralism-a-viable-critical-lens-for-roguelike-games"),
|
|
(PAGE_OTHER, "request-for-content"),
|
|
(PAGE_OTHER, "staff"),
|
|
],
|
|
(7,3,(2015,7)): [
|
|
(PAGE_OTHER, "introduction"),
|
|
(PAGE_OTHER, "copyright"),
|
|
(PAGE_ARTICLE, "a-text-mud-with-a-working-ecology-system"),
|
|
(PAGE_ARTICLE, "dispelling-the-gloom"),
|
|
(PAGE_ARTICLE, "how-integral-are-letters-and-text-to-ascii-gaming"),
|
|
(PAGE_ARTICLE, "legend-and-the-lore"),
|
|
(PAGE_ARTICLE, "the-bonds-of-mudding"),
|
|
(PAGE_ARTICLE, "the-mercurial-temperament-at-the-end-of-the-world"),
|
|
(PAGE_ARTICLE, "where-do-i-begin"),
|
|
(PAGE_OTHER, "request-for-content"),
|
|
(PAGE_OTHER, "staff"),
|
|
]
|
|
}
|
|
|
|
|
|
class TemplateParameters(object):
|
|
pass
|
|
|
|
|
|
|
|
def init_template_data(tp):
|
|
tp.key = None
|
|
tp.full_url = tp.page_permanent_id.replace("website/", "http://"+ setting_domain_name +"/")
|
|
tp.target_is_online = (setting_generation_target & FLAG_ONLINE) == FLAG_ONLINE
|
|
tp.target_is_website = (setting_generation_target & TARGET_WEBSITE) == TARGET_WEBSITE
|
|
|
|
tp.sections = TemplateParameters()
|
|
|
|
if not hasattr(tp, "link_prefix"):
|
|
tp.link_prefix = ""
|
|
|
|
if (setting_generation_target & TARGET_WEBSITE) == TARGET_WEBSITE:
|
|
tp.homepage_link = tp.link_prefix +"index.html"
|
|
tp.contact_link = tp.link_prefix +"contact.html"
|
|
tp.copyright_link = tp.link_prefix +"copyright.html"
|
|
tp.contribute_link = tp.link_prefix +"contribute.html"
|
|
tp.links_link = tp.link_prefix +"links.html"
|
|
else:
|
|
tp.contribute_link = tp.link_prefix +"request-for-content/index.html"
|
|
|
|
tp.rss_link = "http://"+ setting_domain_name +"/feed_rss2.xml"
|
|
tp.twitter_link = "https://twitter.com/irjrnl"
|
|
tp.reddit_link = "http://www.reddit.com/r/imaginaryrealities/"
|
|
|
|
if (setting_generation_target & FLAG_ONLINE) == FLAG_ONLINE:
|
|
tp.js_jquery_link = "http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"
|
|
else:
|
|
if setting_use_minimised_files:
|
|
tp.js_jquery_link = tp.link_prefix +"js/jquery-1.11.3.min.js"
|
|
else:
|
|
tp.js_jquery_link = tp.link_prefix +"js/jquery-1.11.3.js"
|
|
|
|
tp.license_text = "CC BY SA NC"
|
|
tp.license_link = "http://creativecommons.org/licenses/by-nc-sa/3.0/nz/"
|
|
|
|
tp.website_title_text = "Imaginary Realities"
|
|
|
|
tp.menu = TemplateParameters()
|
|
tp.menu.back_issues_title = "Back Issues"
|
|
tp.menu.latest_issue_title = "Current Issue"
|
|
|
|
get_latest_issue_data(tp)
|
|
get_back_issues_data(tp)
|
|
|
|
def get_latest_issue_data(tp):
|
|
tp.sections.latest_issue = TemplateParameters()
|
|
tp.sections.latest_issue.title = "Latest Issue"
|
|
tp.sections.latest_issue.issue_articles = []
|
|
|
|
issue_keys = issue_data.keys()
|
|
issue_keys.sort(lambda a, b: cmp(b, a))
|
|
issue_key = issue_keys[0]
|
|
volume_number, issue_number, (year_number, month_number) = issue_key
|
|
issue_link = "volume-%02d/issue-%02d/" % (volume_number, issue_number)
|
|
tp.sections.latest_issue.issue_title = "Volume %d, Issue %d (%s %d)" % (volume_number, issue_number, calendar.month_name[month_number], year_number)
|
|
|
|
for article_type, page_dirname in issue_data[issue_key]:
|
|
if page_dirname in setting_website_hidden_issue_pages: continue
|
|
article = TemplateParameters()
|
|
article.title = get_article_block_content(volume_number, issue_number, page_dirname, "page_title")
|
|
article.link = issue_link + page_dirname +"/index.html"
|
|
tp.sections.latest_issue.issue_articles.append(article)
|
|
|
|
tp.sections.latest_issue.read_text = "Read this issue"
|
|
tp.sections.latest_issue.read_link = issue_link +"index.html"
|
|
|
|
|
|
def get_back_issues_data(tp):
|
|
tp.sections.back_issues = TemplateParameters()
|
|
tp.sections.back_issues.title = "Back Issues"
|
|
tp.sections.back_issues.issues = []
|
|
|
|
issue_keys = issue_data.keys()
|
|
issue_keys.sort(lambda a, b: cmp(b, a))
|
|
for issue_key in issue_keys[1:]:
|
|
volume_number, issue_number, (year_number, month_number) = issue_key
|
|
|
|
issue = TemplateParameters()
|
|
issue.link = "volume-%02d/issue-%02d/index.html" % (volume_number, issue_number)
|
|
issue.volume_text = "Volume %d, Issue %d" % (volume_number, issue_number)
|
|
issue.publication_year_text = "%s %d" % (calendar.month_name[month_number], year_number)
|
|
tp.sections.back_issues.issues.append(issue)
|
|
|
|
|
|
def generate_website_index_page():
|
|
global data_disqus
|
|
|
|
t = jinja2_env.get_template("homepage.html")
|
|
output_path = os.path.join(setting_target_dirname, "index.html")
|
|
|
|
tp = TemplateParameters()
|
|
tp.page_permanent_id = output_path.replace("\\", "/")
|
|
init_template_data(tp)
|
|
tp.key = "website_index"
|
|
|
|
tp.sections.introduction = TemplateParameters()
|
|
tp.sections.featured_article = TemplateParameters()
|
|
tp.sections.recent_comments = TemplateParameters()
|
|
|
|
# SECTION: Introduction
|
|
tp.sections.introduction.logo_text = "An old logo for Imaginary Realities, with a lower-case i beside a upper-case R"
|
|
tp.sections.introduction.logo_link = "images/logo.png"
|
|
|
|
# SECTION: Featured article
|
|
tp.sections.featured_article.title = "Featured Article"
|
|
featured_article_key = (7,1,"what-i-do-now")
|
|
volume_number, issue_number, page_dirname = featured_article_key
|
|
article_title = get_article_block_content(volume_number, issue_number, page_dirname, "page_title")
|
|
article_link = "volume-%02d/issue-%02d/%s/index.html" % (volume_number, issue_number, page_dirname)
|
|
article_authors = get_article_block_content(volume_number, issue_number, page_dirname, "article_authors")
|
|
feature_excerpt = get_article_block_content(volume_number, issue_number, page_dirname, "feature_excerpt")
|
|
feature_hype = get_article_block_content(volume_number, issue_number, page_dirname, "feature_hype")
|
|
|
|
tp.sections.featured_article.article_title = article_title
|
|
tp.sections.featured_article.article_hype_text = feature_hype
|
|
tp.sections.featured_article.article_quote_text = feature_excerpt
|
|
tp.sections.featured_article.article_authorname_text = article_authors
|
|
tp.sections.featured_article.read_text = "Read this article"
|
|
tp.sections.featured_article.read_link = article_link
|
|
|
|
# SECTION: Recent comments
|
|
tp.sections.recent_comments.title = "Recent Comments"
|
|
tp.sections.recent_comments.entries = []
|
|
tp.sections.recent_comments.is_enabled = True
|
|
if data_disqus is None:
|
|
tp.sections.recent_comments.is_enabled = False
|
|
tp.sections.recent_comments.content = """<div class="watch-this-space">Failed to generate content.</div>"""
|
|
else:
|
|
# comment.comment_id/user_name/timestamp/thread_id/text
|
|
for comment in data_disqus.get_recent_comments():
|
|
# thread.url/feed/title
|
|
thread = data_disqus.get_thread(comment.thread_id)
|
|
|
|
text = comment.text
|
|
while " " in text:
|
|
text = text.replace(" ", " ")
|
|
text = text[:80]+"..."
|
|
|
|
entry = TemplateParameters()
|
|
entry.age_string = data_disqus.get_time_string(comment.timestamp)
|
|
entry.user_name = comment.user_name
|
|
entry.thread_title = thread.title
|
|
entry.thread_url = thread.url
|
|
entry.text = text
|
|
tp.sections.recent_comments.entries.append(entry)
|
|
|
|
html = t.render(tp=tp)
|
|
with codecs.open(output_path, "wb", "utf-8") as f:
|
|
f.write(html)
|
|
|
|
|
|
def generate_website_contact_page():
|
|
t = jinja2_env.get_template("contact.html")
|
|
output_path = os.path.join(setting_target_dirname, "contact.html")
|
|
|
|
tp = TemplateParameters()
|
|
tp.page_permanent_id = output_path.replace("\\", "/")
|
|
init_template_data(tp)
|
|
tp.key = "website_contact"
|
|
|
|
html = t.render(tp=tp)
|
|
with codecs.open(output_path, "wb", "utf-8") as f:
|
|
f.write(html)
|
|
|
|
def generate_website_links_page():
|
|
t = jinja2_env.get_template("links.html")
|
|
output_path = os.path.join(setting_target_dirname, "links.html")
|
|
|
|
tp = TemplateParameters()
|
|
tp.page_permanent_id = output_path.replace("\\", "/")
|
|
init_template_data(tp)
|
|
|
|
html = t.render(tp=tp)
|
|
with codecs.open(output_path, "wb", "utf-8") as f:
|
|
f.write(html)
|
|
|
|
def generate_website_copyright_page():
|
|
t = jinja2_env.get_template("copyright.html")
|
|
output_path = os.path.join(setting_target_dirname, "copyright.html")
|
|
|
|
tp = TemplateParameters()
|
|
tp.page_permanent_id = output_path.replace("\\", "/")
|
|
init_template_data(tp)
|
|
|
|
html = t.render(tp=tp)
|
|
with codecs.open(output_path, "wb", "utf-8") as f:
|
|
f.write(html)
|
|
|
|
|
|
def generate_website_contribute_page():
|
|
t = jinja2_env.get_template("contribute.html")
|
|
output_path = os.path.join(setting_target_dirname, "contribute.html")
|
|
|
|
tp = TemplateParameters()
|
|
tp.page_permanent_id = output_path.replace("\\", "/")
|
|
init_template_data(tp)
|
|
tp.key = "website_contribute"
|
|
|
|
html = t.render(tp=tp)
|
|
with codecs.open(output_path, "wb", "utf-8") as f:
|
|
f.write(html)
|
|
|
|
def generate_website_rss():
|
|
feed = feedformatter.Feed()
|
|
feed.feed["title"] = "Imaginary Realities"
|
|
feed.feed["link"] = "http://"+ setting_domain_name +"/"
|
|
feed.feed["author"] = "Richard Tew"
|
|
feed.feed["description"] = "Announcements related to the Imaginary Realities publication"
|
|
|
|
# Load in existing items, in order of oldest to newest.
|
|
feed_items = json.load(open("feed.json", "rb"))
|
|
changes_made = False
|
|
|
|
existing_links = []
|
|
for feed_item in feed_items:
|
|
if type(feed_item["pubDate"]) in (str, unicode): # RFC2822
|
|
d = email.utils.parsedate_tz(feed_item["pubDate"])
|
|
feed_item["pubDate"] = email.utils.mktime_tz(d) # Seconds since epoch.
|
|
changes_made = True
|
|
feed.items.append(feed_item)
|
|
|
|
link = feed_item["link"]
|
|
if link.endswith("/"):
|
|
link += "index.html"
|
|
existing_links.append(link)
|
|
|
|
# Add missing issues in order of publication (which is how the feed goes).
|
|
issue_keys = issue_data.keys()
|
|
issue_keys.sort()
|
|
for issue_key in issue_keys:
|
|
volume_number, issue_number, (year_number, month_number) = issue_key
|
|
missing_link = feed.feed["link"] + "volume-%02d/issue-%02d/index.html" % (volume_number, issue_number)
|
|
download_link = feed.feed["link"] + "files/"
|
|
if missing_link not in existing_links:
|
|
print "RSS: Adding issue to feed, volume %d, issue %d" % (volume_number, issue_number)
|
|
item = {}
|
|
item["title"] = "Imaginary Realities - Volume %d, Issue %d" % (volume_number, issue_number)
|
|
item["description"] = "<p>Our latest issue as of %s %d. Long delayed, but finally here. Read it <a href='%s'>online</a>, or <a href='%s'>download it</a> as an epub e-book or PDF document. Follow <a href='https://twitter.com/intent/user?screen_name=irjrnl'>our twitter</a>, if that's your thing. Imaginary Realities contains articles related to mudding, and other text-based forms of gaming include interactive fiction and roguelikes.</p>" % (calendar.month_name[month_number], year_number, missing_link, download_link)
|
|
item["link"] = missing_link
|
|
item["pubDate"] = time.time()
|
|
item["guid"] = "%d" % (time.time() * 100)
|
|
feed_items.append(item)
|
|
feed.items.append(item)
|
|
changes_made = True
|
|
|
|
if setting_finalise:
|
|
if changes_made:
|
|
# Update the json record of what has already been published (to try and avoid sending all entries out again, everytime we add a new RSS entry.
|
|
json.dump(feed_items, open("feed.json", "wb"))
|
|
print "RSS feed store modified, changes made."
|
|
else:
|
|
print "RSS feed store not modified, no changes to make."
|
|
else:
|
|
print "WARNING: Results are not finalised, published result should be finalised."
|
|
# Generate the RSS2 feed.
|
|
feed.format_rss2_file(os.path.join(setting_target_dirname, "feed_rss2.xml"))
|
|
|
|
### ISSUE GENERATION
|
|
|
|
def generate_website():
|
|
print "Generating website"
|
|
generate_website_index_page()
|
|
generate_website_contact_page()
|
|
generate_website_copyright_page()
|
|
generate_website_contribute_page()
|
|
# generate_website_links_page()
|
|
generate_website_rss()
|
|
|
|
if (setting_generation_target & TARGET_WEBSITE) == TARGET_WEBSITE:
|
|
for dirname in ("css", "js", "fonts", "images"):
|
|
distutils.dir_util.copy_tree(os.path.join("templates", "website", dirname), os.path.join(setting_target_dirname, dirname))
|
|
|
|
def generate_issue_page(issue_nav, volume_number, issue_number, year_number, month_number, page_type, page_dirname):
|
|
outputdir_path = os.path.join(setting_target_dirname, "volume-%02d" % volume_number, "issue-%02d" % issue_number, page_dirname)
|
|
inputdir_path = os.path.join("templates", "volume%02d_issue%02d" % (volume_number, issue_number), page_dirname)
|
|
inputtemplate_name = "index.html"
|
|
shutil.copytree(inputdir_path, outputdir_path, ignore=shutil.ignore_patterns(inputtemplate_name))
|
|
|
|
jinja2_env = jinja2.Environment(loader=jinja2.FileSystemLoader([ "templates", setting_base_template, inputdir_path ]))
|
|
t = jinja2_env.get_template(inputtemplate_name)
|
|
output_path = os.path.join(outputdir_path, "index.html")
|
|
|
|
tp = TemplateParameters()
|
|
tp.page_permanent_id = output_path.replace("\\", "/")
|
|
# TODO: Path should be set more intelligently. Perhaps absolute in some cases.
|
|
if (setting_generation_target & TARGET_WEBSITE) == TARGET_WEBSITE:
|
|
# Full scope is [TOP/VOLUME/ISSUE/PAGE.html], so 3 levels moves from PAGE to TOP.
|
|
tp.link_prefix = "../../../"
|
|
else:
|
|
# Full scope is [ISSUE/PAGE.html], so 1 levels moves from PAGE to ISSUE.
|
|
tp.link_prefix = "../"
|
|
init_template_data(tp)
|
|
tp.volume_number = volume_number
|
|
tp.issue_number = issue_number
|
|
tp.issue_link = "../index.html"
|
|
tp.issue_nav = issue_nav
|
|
|
|
html = t.render(tp=tp)
|
|
with codecs.open(output_path, "wb", "utf-8") as f:
|
|
f.write(html)
|
|
|
|
def generate_issue_toc(issue_nav, issue_metadata, issue_pagedata):
|
|
volume_number, issue_number, (year_number, month_number) = issue_metadata
|
|
|
|
inputdir_path = os.path.join("templates", "issue")
|
|
outputdir_path = os.path.join(setting_target_dirname, "volume-%02d" % volume_number, "issue-%02d" % issue_number)
|
|
|
|
jinja2_env = jinja2.Environment(loader=jinja2.FileSystemLoader([ "templates", setting_base_template, inputdir_path ]))
|
|
t = jinja2_env.get_template("toc.html")
|
|
output_path = os.path.join(outputdir_path, "index.html")
|
|
|
|
tp = TemplateParameters()
|
|
tp.page_permanent_id = output_path.replace("\\", "/")
|
|
# TODO: Path should be set more intelligently. Perhaps absolute in some cases.
|
|
if (setting_generation_target & TARGET_WEBSITE) == TARGET_WEBSITE:
|
|
# Full scope is [TOP/VOLUME/ISSUE/<HERE>/PAGE], so 2 levels moves from PAGE to TOP.
|
|
tp.link_prefix = "../../"
|
|
else:
|
|
# Full scope is [<HERE>/PAGE], so 0 levels moves to ISSUE.
|
|
tp.link_prefix = ""
|
|
init_template_data(tp)
|
|
tp.volume_number = volume_number
|
|
tp.issue_number = issue_number
|
|
tp.issue_link = "index.html"
|
|
|
|
tp.logo_text = "An old logo for Imaginary Realities, with a lower-case i beside a upper-case R"
|
|
tp.logo_link = "images/logo.png"
|
|
tp.page_title = "Table of Contents"
|
|
|
|
tp.issue = TemplateParameters()
|
|
tp.issue.articles = []
|
|
|
|
for page_type, page_dirname in issue_pagedata:
|
|
if page_dirname in setting_website_hidden_issue_pages: continue
|
|
article = TemplateParameters()
|
|
article.title = get_article_block_content(volume_number, issue_number, page_dirname, "page_title")
|
|
article.author_names = get_article_block_content(volume_number, issue_number, page_dirname, "article_authors").replace(" and ", "<br/>")
|
|
article.link = page_dirname +"/index.html"
|
|
tp.issue.articles.append(article)
|
|
|
|
tp.issue.read_link = issue_pagedata[0][1] +"/index.html"
|
|
tp.issue.read_text = "Read on.."
|
|
tp.issue_nav = issue_nav
|
|
|
|
html = t.render(tp=tp)
|
|
with codecs.open(output_path, "wb", "utf-8") as f:
|
|
f.write(html)
|
|
|
|
def generate_issues(issue_data):
|
|
issue_metadatas = issue_data.keys()
|
|
issue_metadatas.sort()
|
|
|
|
last_issue_index = len(issue_metadatas)-1
|
|
for issue_index, issue_metadata in enumerate(issue_metadatas):
|
|
issue_pagedata = issue_data[issue_metadata]
|
|
volume_number, issue_number, (year_number, month_number) = issue_metadata
|
|
print "Generating issue volume %d, issue %d" % (volume_number, issue_number)
|
|
|
|
volume_path = os.path.join(setting_target_dirname, "volume-%02d" % volume_number)
|
|
issue_path = os.path.join(volume_path, "issue-%02d" % issue_number)
|
|
distutils.dir_util.mkpath(issue_path)
|
|
|
|
images_input_path = os.path.join("templates", "volume%02d_issue%02d" % (volume_number, issue_number), "images")
|
|
if os.path.exists(images_input_path):
|
|
images_output_path = os.path.join(issue_path, "images")
|
|
distutils.dir_util.copy_tree(images_input_path, images_output_path)
|
|
|
|
# Copy a set of the dependent files to each issue, for local use.
|
|
if (setting_generation_target & TARGET_EBOOK) == TARGET_EBOOK:
|
|
for parent_template_name in [ "website", "ebook" ]:
|
|
parent_template_path = os.path.join("templates", parent_template_name)
|
|
for entry_name in os.listdir(parent_template_path):
|
|
entry_path = os.path.join(parent_template_path, entry_name)
|
|
if os.path.isdir(entry_path):
|
|
if parent_template_name == "website" and entry_name == "images":
|
|
# There are a lot of images we do not need.
|
|
for file_name in [ "logo.png" ]:
|
|
dest_path = os.path.join(issue_path, entry_name)
|
|
distutils.dir_util.mkpath(dest_path)
|
|
distutils.file_util.copy_file(os.path.join(entry_path, file_name), os.path.join(dest_path, file_name))
|
|
else:
|
|
distutils.dir_util.copy_tree(entry_path, os.path.join(issue_path, entry_name))
|
|
|
|
# TODO: The whole issue_nav debacle should be skipped for non-website builds.
|
|
# TODO: Non-website builds should not have issue_nav
|
|
if (setting_generation_target & TARGET_WEBSITE) == TARGET_WEBSITE:
|
|
issue_nav = TemplateParameters()
|
|
else:
|
|
issue_nav = None
|
|
|
|
if issue_nav:
|
|
# Inter-article navigation button priming (setup done on per-article basis below).
|
|
default_prev_article_html = issue_nav.prev_article_html = "« article"
|
|
issue_nav.prev_article_class = ""
|
|
|
|
default_next_article_html = issue_nav.next_article_html = "article »"
|
|
issue_nav.next_article_class = ""
|
|
|
|
# Inter-issue navigation button setup.
|
|
default_prev_issue_html = issue_nav.prev_issue_html = "« issue"
|
|
if issue_index == 0:
|
|
issue_nav.prev_issue_class = "unlinked-button"
|
|
prev_issue_link = ""
|
|
else:
|
|
prev_volume_number, prev_issue_number, discarded = issue_metadatas[issue_index-1]
|
|
prev_issue_link = "volume-%02d/issue-%02d/index.html" % (prev_volume_number, prev_issue_number)
|
|
issue_nav.prev_issue_html = "<a href='../../../%s'>%s</a>" % (prev_issue_link, default_prev_issue_html)
|
|
issue_nav.prev_issue_class = ""
|
|
|
|
default_next_issue_html = issue_nav.next_issue_html = "issue »"
|
|
if issue_index == last_issue_index:
|
|
issue_nav.next_issue_class = "unlinked-button"
|
|
next_issue_link = ""
|
|
else:
|
|
next_volume_number, next_issue_number, discarded = issue_metadatas[issue_index+1]
|
|
next_issue_link = "volume-%02d/issue-%02d/index.html" % (next_volume_number, next_issue_number)
|
|
issue_nav.next_issue_html = "<a href='../../../%s'>%s</a>" % (next_issue_link, default_next_issue_html)
|
|
issue_nav.next_issue_class = ""
|
|
|
|
last_page_link = "../index.html"
|
|
last_page_index = len(issue_pagedata)-1
|
|
|
|
for page_index, (page_type, page_dirname) in enumerate(issue_pagedata):
|
|
if issue_nav:
|
|
issue_nav.prev_article_class = "unlinked-button"
|
|
issue_nav.next_article_class = "unlinked-button"
|
|
issue_nav.prev_article_html = default_prev_article_html
|
|
issue_nav.next_article_html = default_next_article_html
|
|
|
|
set_prev = False
|
|
set_next = False
|
|
next_link = ""
|
|
# The article hotbar skips over the copyright page.
|
|
if page_dirname not in setting_website_hidden_issue_pages:
|
|
if page_index == last_page_index:
|
|
set_prev = True
|
|
else:
|
|
set_next = set_prev = True
|
|
|
|
if set_prev:
|
|
issue_nav.prev_article_html = "<a href='%s'>%s</a>" % (last_page_link, default_prev_article_html)
|
|
issue_nav.prev_article_class = ""
|
|
|
|
if set_next:
|
|
# The article hotbar skips over the copyright page.
|
|
j = page_index+1
|
|
while j < len(issue_pagedata) and issue_pagedata[j][1] in setting_website_hidden_issue_pages:
|
|
j += 1
|
|
if j < len(issue_pagedata):
|
|
next_link = "../"+ issue_pagedata[j][1] +"/index.html"
|
|
issue_nav.next_article_html = "<a href='%s'>%s</a>" % (next_link, default_next_article_html)
|
|
issue_nav.next_article_class = ""
|
|
|
|
generate_issue_page(issue_nav, volume_number, issue_number, year_number, month_number, page_type, page_dirname)
|
|
|
|
if issue_nav:
|
|
# The article hotbar skips over the copyright page.
|
|
if page_dirname not in setting_website_hidden_issue_pages:
|
|
last_page_link = "../"+ page_dirname +"/index.html"
|
|
|
|
if issue_nav:
|
|
if prev_issue_link:
|
|
issue_nav.prev_issue_html = "<a href='../../%s'>%s</a>" % (prev_issue_link, default_prev_issue_html)
|
|
issue_nav.prev_issue_class = ""
|
|
else:
|
|
issue_nav.prev_issue_html = default_prev_issue_html
|
|
issue_nav.prev_issue_class = "unlinked-button"
|
|
if next_issue_link:
|
|
issue_nav.next_issue_html = "<a href='../../%s'>%s</a>" % (next_issue_link, default_next_issue_html)
|
|
issue_nav.next_issue_class = ""
|
|
else:
|
|
issue_nav.next_issue_html = default_next_issue_html
|
|
issue_nav.next_issue_class = "unlinked-button"
|
|
|
|
issue_nav.prev_article_class = "unlinked-button"
|
|
issue_nav.next_article_class = ""
|
|
issue_nav.prev_article_html = default_prev_article_html
|
|
issue_nav.next_article_html = "<a href='%s'>%s</a>" % (issue_pagedata[0][1] +"/index.html", default_next_article_html)
|
|
|
|
generate_issue_toc(issue_nav, issue_metadata, issue_pagedata)
|
|
|
|
|
|
def generate_ebooks(issue_data):
|
|
"""
|
|
"" ebooks\volume-06\issue-01\index.html issue.pdf --verbose
|
|
"""
|
|
command_path = ""
|
|
command_path_guess = ""
|
|
if os.name == "nt":
|
|
command_path_guess = "c:\\Program Files (x86)\\Calibre2\\ebook-convert.exe"
|
|
if os.path.exists(command_path_guess):
|
|
command_path = command_path_guess
|
|
elif os.name == "posix":
|
|
command_path_guess = "/home1/disinter/opt/calibre/ebook-convert"
|
|
if os.path.exists(command_path_guess):
|
|
command_path = command_path_guess
|
|
|
|
if command_path == "":
|
|
print "Error: Unable to locate 'ebook-convert.exe', failed to generate ebooks."
|
|
print "... %s" % command_path_guess
|
|
return
|
|
|
|
standard_arguments = ""
|
|
standard_arguments += " --disable-font-rescaling"
|
|
standard_arguments += " --margin-bottom=72"
|
|
standard_arguments += " --margin-top=72"
|
|
standard_arguments += " --margin-left=72"
|
|
standard_arguments += " --margin-right=72"
|
|
standard_arguments += " --chapter=/"
|
|
standard_arguments += " --page-breaks-before=/"
|
|
standard_arguments += " --chapter-mark=rule"
|
|
standard_arguments += " --output-profile=default"
|
|
standard_arguments += " --input-profile=default"
|
|
standard_arguments += " --pretty-print"
|
|
standard_arguments += " --replace-scene-breaks=\"\""
|
|
standard_arguments += " --toc-filter=.*\[\d+\].*"
|
|
|
|
output_path = os.path.join("website", "files")
|
|
distutils.dir_util.mkpath(output_path)
|
|
|
|
issue_metadatas = issue_data.keys()
|
|
issue_metadatas.sort()
|
|
|
|
for issue_index, issue_metadata in enumerate(issue_metadatas):
|
|
issue_pagedata = issue_data[issue_metadata]
|
|
volume_number, issue_number, (year_number, month_number) = issue_metadata
|
|
print "Generating ebook volume %d, issue %d" % (volume_number, issue_number)
|
|
|
|
volume_path = os.path.join(setting_target_dirname, "volume-%02d" % volume_number)
|
|
issue_path = os.path.join(volume_path, "issue-%02d" % issue_number)
|
|
html_path = os.path.join(issue_path, "index.html")
|
|
|
|
output_basename = "imaginary-realities-v%02di%02d-%04d%02d" % (volume_number, issue_number, year_number, month_number)
|
|
for suffix in ("epub", "pdf"):
|
|
output_filename = output_basename +"."+ suffix
|
|
ret = subprocess.call([ command_path, html_path, os.path.join(output_path, output_filename) ])
|
|
print output_filename, ret
|
|
|
|
|
|
def get_article_block_content(volume_number, issue_number, page_dirname, block_name):
|
|
# Load the template.
|
|
jinja2_env = jinja2.Environment(loader=jinja2.FileSystemLoader([
|
|
"templates", setting_base_template,
|
|
os.path.join("templates", "volume%02d_issue%02d" % (volume_number, issue_number), page_dirname)
|
|
]))
|
|
t = jinja2_env.get_template("index.html")
|
|
if block_name in t.blocks:
|
|
return next(t.blocks[block_name](None)).strip()
|
|
return ""
|
|
|
|
default_generation_targets = TARGET_WEBSITE, TARGET_EBOOK
|
|
|
|
def run(targets=None, disqus_data=None, reddit_data=None):
|
|
# TODO(rmtew): Make these non-global at some point. It's not really a problem, but it's messy.
|
|
global setting_target_dirname
|
|
global setting_use_minimised_files
|
|
global setting_website_hidden_issue_pages
|
|
global setting_generation_target
|
|
global setting_base_template
|
|
global jinja2_env
|
|
global data_disqus
|
|
global data_reddit
|
|
|
|
data_disqus = disqus_data
|
|
data_reddit = reddit_data
|
|
|
|
if targets is None:
|
|
targets = default_generation_targets
|
|
|
|
for setting_generation_target in targets:
|
|
if setting_generation_target not in templates_by_target:
|
|
print >> sys.stderr, "Unknown target:", setting_generation_target
|
|
sys.exit(1)
|
|
|
|
setting_base_template = templates_by_target[setting_generation_target]
|
|
|
|
setting_use_minimised_files = False
|
|
setting_target_dirname = None
|
|
setting_website_hidden_issue_pages = []
|
|
if setting_generation_target & TARGET_WEBSITE == TARGET_WEBSITE:
|
|
setting_target_dirname = "website"
|
|
setting_website_hidden_issue_pages.extend([ "copyright", "request-for-content" ])
|
|
if setting_generation_target & FLAG_ONLINE == FLAG_ONLINE:
|
|
setting_use_minimised_files = True
|
|
elif setting_generation_target & TARGET_EBOOK == TARGET_EBOOK:
|
|
setting_target_dirname = "ebooks"
|
|
|
|
jinja2_env = jinja2.Environment(loader=jinja2.FileSystemLoader([ 'templates', setting_base_template ]))
|
|
|
|
if os.path.exists(setting_target_dirname):
|
|
distutils.dir_util.remove_tree(setting_target_dirname)
|
|
|
|
if (setting_generation_target & TARGET_WEBSITE) == TARGET_WEBSITE:
|
|
generate_issues(issue_data)
|
|
generate_website()
|
|
elif (setting_generation_target & TARGET_EBOOK) == TARGET_EBOOK:
|
|
generate_issues(issue_data)
|
|
generate_ebooks(issue_data)
|
|
|
|
if __name__ == "__main__":
|
|
run()
|
|
|
|
# EOF
|