musicguru /

Full commit
# Created By: Virgil Dupras
# Created On: 2010-06-28
# Copyright 2010 Hardcoded Software (
# This software is licensed under the "BSD" License as described in the "LICENSE" file, 
# which should be included with this package. The terms are also available at 

import re
import yaml
import markdown

from hscommon import io
from hscommon.path import Path
from import read_changelog_file

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "">
<html xmlns="">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link rel="SHORTCUT ICON" href="/favicon.ico" />
    <link rel="stylesheet" href="{relpath}hardcoded.css" type="text/css" />
    <div class="mainlogo">
      <a href=""><img src="{relpath}images/hs_title.png" alt="HS Logo" /></a>
    <div class="maincontainer">
      <tr valign="top">
        <td class="menuframe">
          <div class="menu">

<a class="{menuclass}" href="{relpath}{link}">{name}</a>
<span class="titleline"> </span>
<span class="titledescrip">{desc}</span>

<table class="hardcoded">
<tr class="header">
<td style="width:56pt;">Date</td>


def tixgen(tixurl):
    """This is a filter *generator*. tixurl is a url pattern for the tix with a {0} placeholder
    for the tix #
    urlpattern = tixurl.format('\\1') # will be replaced buy the content of the first group in re
    R = re.compile(r'#(\d+)')
    repl = '[#\\1]({0})'.format(urlpattern)
    return lambda text: R.sub(repl, text)

class Page:
    def __init__(self, pagedata, pagespath, fallbackpath): = pagedata['name']
        self.basename = Path([-1]
        self.basepath = Path([:-1]
        self.path = pagespath + self.basepath + '{}.md'.format(self.basename)
        if not io.exists(self.path):
            self.path = fallbackpath + self.basepath + '{}.md'.format(self.basename)
        self.title = pagedata['title']
        self.relpath = '../' * len(self.basepath)
        self.meta = ''
    def render(self, destpath, menu, env):
        dest = destpath + self.basepath + '{0}.htm'.format(self.basename)
        if not io.exists(dest[:-1]):
        mdcontents =, 'rt', encoding='utf-8').read()
        mdcontents = mdcontents.format(**env)
        main_contents = markdown.markdown(mdcontents)
        rendered = MAIN_CONTENTS.format(meta=self.meta, title=self.title, relpath=self.relpath,
            menu=menu, contents=main_contents)
        fp =, 'wt', encoding='utf-8')

class MainPage(Page):
    def __init__(self, pagedata, pagespath, fallbackpath):
        Page.__init__(self, pagedata, pagespath, fallbackpath)
        self.menutitle = pagedata['menutitle']
        self.menudesc = pagedata.get('menudesc', self.title)
        self.subpages = [Page(data, pagespath, fallbackpath) for data in pagedata.get('subpages', [])]
    def build_menu(self, pages):
        menu_items = []
        for page in pages:
            menuclass = 'menuitem_selected' if page is self else 'menuitem'
            link = '{0}.htm'.format(
            contents = MENU_CONTENTS.format(menuclass=menuclass, relpath=self.relpath, link=link,
            name=page.menutitle, desc=page.menudesc)
        return ''.join(menu_items)
    def render(self, destpath, pages, env):
        menu = self.build_menu(pages)
        for page in self.subpages:
            page.render(destpath, menu, env)
        Page.render(self, destpath, menu, env)

def render_changelog(changelog, tixurl):
    items = []
    tix = tixgen(tixurl)
    for item in changelog:
        date = item['date']
        version = item['version']
        description = markdown.markdown(tix(item['description']))
        rendered = CHANGELOG_ITEM.format(date=date, version=version, description=description)
    rendered_items = ''.join(items)
    return CHANGELOG.format(contents=rendered_items)

def gen(basepath, destpath, profile=None):
    basepath = Path(basepath)
    destpath = Path(destpath)
    configpath = basepath + 'conf.yaml'
    confall = yaml.load(, 'rt', encoding='utf-8'))
    conf = confall['base']
    if profile and profile in confall:
    tixurl = conf['tixurl']
    changelogdata = read_changelog_file(str(basepath + conf['changelog']))
    changelog = render_changelog(changelogdata, tixurl)
    if 'env' in conf:
        envpath = basepath + conf['env']
        env = yaml.load(, 'rt', encoding='utf-8'))
        env = {}
    env['changelog'] = changelog
    pagespath = basepath + conf['pages']
    if 'basepages' in conf:
        fallbackpath = basepath + conf['basepages']
        fallbackpath = None
    pagedatas = yaml.load(, 'rt', encoding='utf-8'))
    pages = [MainPage(pagedata, pagespath=pagespath[:-1], fallbackpath=fallbackpath) for pagedata in pagedatas]
    skelpath = basepath + Path(conf['skeleton'])
    if not io.exists(destpath):
        print("Copying skeleton")
        io.copytree(skelpath, destpath)
    pages[0].meta = conf.get('firstpage_meta', '')
    for i, page in enumerate(pages):
        print("Rendering {0}".format(
        page.render(destpath, pages, env)