Commits

faassen committed b89a5e4

Add actual blaag.py back, I accidentally deleted it.

Comments (0)

Files changed (1)

+# -*- encoding: utf-8 -*-
+from string import Template
+from datetime import datetime, timedelta
+from time import time, mktime
+import sys, os, os.path, shutil, re
+from docutils.core import publish_parts
+from hgapi import hgapi
+import PyRSS2Gen
+from ConfigParser import SafeConfigParser, NoSectionError, NoOptionError
+import codecs
+
+def get_configuration(path):
+    defaults = {
+        'content': {
+            'title': u"Unknown title",
+            'author': u"Unknown author",
+            'description': u"Unknown description",
+            'entries': 'entries',
+            },
+        'datetime': {
+            'use_filename_for_creation_time': False,
+            },
+        'html': {
+            'templates_path': 'templates',
+            'html_path': 'html',
+            },
+        'disqus': {
+            'name': None,
+            }
+        }
+    config = SafeConfigParser(defaults)
+    f = codecs.open(path, 'r', 'utf-8') # make sure config is read as unicode
+    config.readfp(f)
+    f.close()
+    return config
+
+def sieve(*words):
+    """Filter generator for RSS generation"""
+    def sieve_impl(parts):
+        for word in words:
+            if (word in parts['title'].lower() or
+                word in parts['body'].lower()):
+                return True
+        return False
+    return sieve_impl
+
+
+FEEDS = {
+    'rss.xml': lambda x: True,
+    'python.xml': sieve('python')
+    }
+
+def generate_rss(data, sieve, title, description, base_url,
+                 get_url):
+    """Generate the rss feed, from a list of docutils parts dicts.
+    Depends on that the parts['created'] contains UTC """
+    items = []
+    for parts in data:
+        if sieve(parts):
+            items.append(PyRSS2Gen.RSSItem(
+                    title = parts['title'],
+                    link = get_url(parts),
+                    description = parts['html_body'],
+                    guid = PyRSS2Gen.Guid(get_url(parts)),
+                    pubDate = datetime.fromtimestamp(parts['created'])))
+
+    return PyRSS2Gen.RSS2(
+        title = title,
+        link = base_url,
+        description = description,
+        lastBuildDate = datetime.utcnow(),
+        items = items)
+
+    
+def get_data(entry):
+    """Parse the rst in the string entry and return the parts dict"""
+    return publish_parts(entry, writer_name="html")
+
+def get_entries(entries_path):
+    """Get the filenames of all entries, sorted using [].sort(reverse=True)"""
+    entries = [x for x in os.listdir(entries_path) if x.endswith(".rst")]
+    entries.sort(reverse=True)
+    return entries
+
+def get_data_entries(entries, entries_path, use_filename):
+    """Read and parse entries, return document parts as generated by docutils"""
+    data = []
+    for entry in entries:
+        with open(os.path.join(entries_path, entry)) as source:
+            parts = get_data(source.read().decode("utf-8"))
+            if not use_filename:
+                entry_timeinfo = get_entry_dates(entry, entries_path)
+            else:
+                entry_timeinfo = get_filename_entry_dates(entry)
+            parts.update(entry_timeinfo)
+            data.append(parts)
+    return data
+
+def identifier(parts):
+    """Create a identifier from doc title, replacing special chars with dash (-)"""
+    return re.sub("[^\w]", "-", parts["title"]).strip("-")
+    
+def setup_target_folder(html_path, css_path, css_name):
+    """Create target folder if it does not exist, and copy CSS files there"""
+    if not os.path.exists(html_path):
+        os.mkdir(html_path)
+    shutil.copy(os.path.join(css_path, css_name), html_path)
+    shutil.copy(os.path.join(css_path, "html4css1.css"), html_path)
+
+def hgdate_to_utc(hgdate_string):
+    """Convert a hgdate format string to unix time UTC"""
+    return sum([int(i) for i in hgdate_string.split()])
+
+def get_entry_dates(entry, entries_path):
+    """Get (creation, last modification) as unix time UTC"""
+    repo = hgapi.Repo(entries_path)
+    times = repo.hg_log(template='{date|hgdate}\n', filespec=entry).split("\n")
+   
+    if times == ['']: #not in repo yet
+        print("Warning: %s is not version controlled" % (entry,))
+        created = modified = int(time())
+    else:
+
+        if not times[-1]: del times[-1]
+        created = hgdate_to_utc(times[-1])
+        modified = hgdate_to_utc(times[0])
+    return {'created': created, 'modified': modified}
+
+def get_filename_entry_dates(entry):
+    """Get creation and last modification based on filename as unix time UTC."""
+    year, month, day, extra = entry.split('-')
+    t = int(mktime(datetime(int(year), int(month), int(day)).timetuple()))
+    return {'created': t, 'modified': t}
+
+def format_timestamp(ts, template, dt_format):
+    dt = datetime.fromtimestamp(ts)
+    return dt.strftime(dt_format)
+    
+def get_timestamp(created, modified, created_tpl, modified_tpl, dt_format):
+    """Return string representing timestamp in post"""
+    created_dt = datetime.fromtimestamp(created)
+    modified_dt = datetime.fromtimestamp(modified)
+
+    result = created_tpl.substitute(timestamp=created_dt.strftime(dt_format))
+    #Add modification date if last change is more than 10 minutes after creation
+    if modified_dt - created_dt > timedelta(minutes=10):
+        result += modified_tpl.substitute(
+            timestamp=modified_dt.strftime(dt_format))
+    return result
+
+class Templates(object):
+    def __init__(self, templates_path):
+        self.templates_path = templates_path
+
+    def get(self, name):
+        f = open(os.path.join(self.templates_path, name), 'r')
+        result = Template(f.read().decode('utf-8'))
+        f.close()
+        return result
+
+def generate_blaag(config_path):
+    """Generate html from blaag entries."""
+    config = get_configuration(config_path)
+
+    templates = Templates(config.get('html', 'templates_path'))
+
+    page_tpl = templates.get('pagetemplate.html')
+    blaag_tpl = templates.get('blaag_template.html')
+    comments_full_tpl = templates.get('comments_full.html')
+    comments_short_tpl = templates.get('comments_short.html')
+    menu_link_tpl = templates.get('menu_link.html')
+    timestamp_tpl = templates.get('timestamp.html')
+    created_tpl = templates.get('created.html')
+    modified_tpl = templates.get('modified.html')
+    
+    html_path = config.get('html', 'html_path')
+
+    setup_target_folder(
+        html_path=html_path,
+        css_path=config.get('html', 'css_path'),
+        css_name=config.get('html', 'css_name')
+        )
+
+    entries_path = config.get('content', 'entries_path')
+    entries = get_entries(entries_path)
+    try:
+        use_filename_for_creation_time = config.getboolean(
+            'datetime',
+            'use_filename_for_creation_time')
+    except (NoSectionError, NoOptionError):
+        use_filename_for_creation_time = False
+        
+    data = get_data_entries(
+        entries, entries_path,
+        use_filename_for_creation_time)
+
+    main = []
+
+    base_url = config.get('html', 'base_url')
+    def get_relative_url(parts):
+        """Create a relative url from a document"""
+        return identifier(parts)
+    def get_absolute_url(parts):
+        """Create absolute url for a document"""
+        return base_url + get_relative_url(parts)
+    
+    #Get timestamps from Mercurial
+    try:
+        dt_format = config.get('datetime', 'datetime_format')
+    except (NoSectionError, NoOptionError):
+        dt_format = '%y%m%d %H:%M'
+    for parts in data:
+        timestamp = timestamp_tpl.substitute(
+            timestamps=get_timestamp(parts['created'], parts['modified'],
+                                     created_tpl, modified_tpl, dt_format))
+        parts.update({
+                'timestamp': timestamp,
+                'identifier': identifier(parts)
+                })
+    #Sort by creation date
+    data.sort(cmp=lambda x, y: y['created']-x['created'])
+    #Create links
+    links = "\n".join([
+            menu_link_tpl.substitute(
+                link=get_relative_url(parts), 
+                title=parts['title']) 
+            for parts in data])
+
+    title = config.get('content', 'title')
+
+    disqus_name = config.get('disqus', 'name')
+    
+    defaults = {
+        'disqus_name': disqus_name,
+        'css': config.get('html', 'css_url') + "?" + str(int(time())), #force reload. 
+        'page_title': title,
+        'blaag_title': title,
+        'author': config.get('content', 'author'),
+        'google_analytics_account': config.get('google_analytics', 'account'),
+        }
+
+    for parts in data: 
+        parts.update(defaults)
+        htmltitle = identifier(parts)
+        target_path = os.path.join(html_path, htmltitle)
+        if not os.path.exists(target_path):
+            os.mkdir(target_path)
+
+        with open(os.path.join(html_path, htmltitle, "index.html"), "w") as target:
+            #Write page for post
+            fullcomments = comments_full_tpl.substitute(
+                parts,
+                threadid=htmltitle,
+                disqus_name=disqus_name)
+            blaag_html = blaag_tpl.substitute(parts, comments=fullcomments)
+            target.write(page_tpl.substitute(
+                    parts,
+                    title=parts['title'], 
+                    links=links,
+                    main=blaag_html,
+                    ).encode("utf-8"))
+            #Append post to main page
+            shortcomments = comments_short_tpl.substitute(
+                parts, 
+                link=get_relative_url(parts), 
+                threadid=identifier(parts))
+            main.append(blaag_tpl.substitute(
+                    parts, 
+                    comments=shortcomments, 
+                    link=get_relative_url(parts)))
+    #Write main page
+    with open(os.path.join(html_path, "index.html"), "w") as target:
+        target.write(page_tpl.substitute(
+                defaults,
+                links=links, 
+                main="\n".join(main)).encode("utf-8"))
+    #Write rss
+
+    description = config.get('content', 'description')
+    
+    for name in FEEDS:        
+        rss = generate_rss(data, FEEDS[name], title, description, base_url,
+                           get_absolute_url)
+        f = open(os.path.join(html_path, name), 'w')
+        rss.write_xml(f)
+        f.close()
+        
+def pretxncommit_hook(*args, **kwargs):
+    """Run before commit"""
+    generate_blaag('blaag.cfg')
+
+def blaag_script():
+    try:
+        config_path = sys.argv[1]
+    except IndexError:
+        config_path = 'blaag.cfg'
+
+    generate_blaag(config_path)
+
+if __name__ == "__main__":
+    blaag_script()