Skip to content

Instantly share code, notes, and snippets.

@roipoussiere
Created March 7, 2017 18:48
Show Gist options
  • Save roipoussiere/a907f740325b52816d9d1d1c6dbd91dc to your computer and use it in GitHub Desktop.
Save roipoussiere/a907f740325b52816d9d1d1c6dbd91dc to your computer and use it in GitHub Desktop.
A script to convert notes from markdown / PlantUML to HTML and Redmine wiki syntax, then publish files through rsync.
#!/usr/bin/env python
# -*- coding: utf-8 -*
"""
From a folder containing a set of markdown files, this script:
- generate HTML files;
- generate RedMine-compliant Textile files;
- generate UML diagrams (from PlantUML syntax) and insert the in HTML and Textile files;
- send images and HTML files to a server through rsync.
"""
import os
import os.path as op
import shlex
from subprocess import Popen, PIPE
from datetime import datetime
import getpass
PLANTUML_EXE = 'java -jar /usr/share/plantuml.jar' # PlantUML executable
PANDOC_EXE = 'pandoc' # Pandoc executable
USERPAGE_DIR = '/home/nathanael/userpage' # local directory where your .md files are
USER = 'njourdane' # Your userpages username (LDAP login)
PW = 'foo' # Password
WEB_DIR = op.join(USERPAGE_DIR, 'public_html') # Directory for HTML files
TEXTILE_DIR = op.join(USERPAGE_DIR, 'textile') # Directory for Textile files
IMG_DIR = op.join(WEB_DIR, 'images') # Directory for images
SERVER = 'userpages.irap.omp.eu'
PASSWORD = None # Let empty to enter the password on execution
HTML_HEADER = """<!DOCTYPE html>
<html>
<head>
<title>Page perso de %s</title>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'
integrity='sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7' crossorigin='anonymous'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css'
integrity='sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r' crossorigin='anonymous'>
<style>
html{ background-color:white }
body{ width:800px; margin:auto; padding:10px; background-color:aliceblue }
h1{ margin-left:10px; }
h3{ margin-left:30px; }
</style>
</head>
<body>
<script src='https://code.jquery.com/jquery-2.2.4.min.js' crossorigin='anonymous'
integrity='sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44='></script>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js' crossorigin='anonymous'
integrity='sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS'></script>
<script> \$(document).ready(function(){\$('table').addClass('table')}); </script>
<p><em>Page mise à jour le %s</em></p>""" % (USER, datetime.now().strftime('%d/%m/%y à %H:%M'))
HTML_FOOTER = """
</body>
</html>"""
TEMP_MD = '/tmp/temp.md'
TEMP_HTML = '/tmp/temp.html'
TEMP_TEXTILE = '/tmp/temp.textile'
def exe(cmd):
"""Execute the specified command."""
process = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
stdout, stderr = process.communicate()
if stderr:
print('--- Error while running command "%s" ---\n%s' % (cmd, stderr.encode()))
if stdout:
print(stderr.encode())
for d in [WEB_DIR, TEXTILE_DIR, IMG_DIR]:
if not op.isdir(d):
os.makedirs(d)
# Hide main directory
index_html = op.join(WEB_DIR, 'index.html')
if not op.isfile(index_html):
with open(index_html, 'w'):
pass
for md_path in [op.abspath(op.join(USERPAGE_DIR, f)) for f in os.listdir(USERPAGE_DIR) if f.endswith(".md")]:
basename = op.splitext(op.basename(md_path))[0]
# Generate UML diagrams and replace UML blocks in markdown files by generated diagrams
with open(md_path) as f_md, open(TEMP_MD, 'w') as edited_md:
n_uml = 0
uml_block = False
for line in f_md:
if line.startswith('```@startuml'):
img_name = basename + '.png' if n_uml == 0 else '%s_%03d.png' % (basename, n_uml)
edited_md.write('![](%s)' % op.join('http://' + SERVER, '~' + USER, 'images', img_name))
n_uml += 1
uml_block = True
elif uml_block and line.startswith('@enduml'):
edited_md.write('\n')
f_md.next()
uml_block = False
elif not uml_block:
edited_md.write(line)
if n_uml > 0:
exe('%s -nbthread auto %s -o %s' % (PLANTUML_EXE, md_path, IMG_DIR))
# Generate HTML file and add html header and footer
exe('%s %s -o %s' % (PANDOC_EXE, TEMP_MD, TEMP_HTML))
with open(TEMP_HTML) as html_in, open(op.join(WEB_DIR, basename + '.html'), 'w') as html_out:
html_out.write(HTML_HEADER)
html_out.write(html_in.read())
html_out.write(HTML_FOOTER)
# Generate Textile file and replace code blocks syntax
exe('%s %s -o %s' % (PANDOC_EXE, TEMP_MD, TEMP_TEXTILE))
with open(TEMP_TEXTILE) as textile_in, open(op.join(TEXTILE_DIR, basename + '.textile'), 'w') as textile_out:
code_block = False
for line in textile_in:
if line.startswith('bc. '):
textile_out.write('<pre>\n')
textile_out.write(line[4:])
code_block = True
elif code_block and not line.strip():
textile_out.write('</pre>\n')
code_block = False
else:
textile_out.write(line)
print('Converted ' + basename)
# Sending through rsync
password = PASSWORD if PASSWORD else getpass.getpass("IRAP LDAP password for %s: " % USER)
exe('sshpass -p %s rsync -e ssh -av --protocol=29 %s %s@%s:' % (password, WEB_DIR, USER, SERVER))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment