From cf7819fb1e2decb783973193ee144cf7ed7fd04a Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Tue, 12 May 2015 16:47:18 +0200 Subject: [PATCH] initial commit --- .gitignore | 10 ++++ README.md | 18 +++++++ generator.py | 114 +++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 + templates/board.html.j2 | 23 ++++++++ templates/boards.html.j2 | 22 ++++++++ templates/thread.html.j2 | 20 +++++++ templates/user.html.j2 | 23 ++++++++ templates/users.html.j2 | 19 +++++++ 9 files changed, 251 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 generator.py create mode 100644 requirements.txt create mode 100644 templates/board.html.j2 create mode 100644 templates/boards.html.j2 create mode 100644 templates/thread.html.j2 create mode 100644 templates/user.html.j2 create mode 100644 templates/users.html.j2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..255468d --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +*~ +*.swp +.Python +rendered/ +bin/ +include/ +lib/ +static/ +board.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..00f0e0e --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Proboards Website Generator + +A quick way to visualise the data obtained by my +[proboards-saver](https://code.vanwa.ch/shu/Proboards-Saver) tool. + +Python3 with Jinja2 is needed. + +## Usage + +```generator.py [-h] [--data DATA] [--static STATIC] [--out OUT] + +build a static website out of a proboard json dump + +optional arguments: + -h, --help show this help message and exit + --data DATA board data (json file) + --static STATIC path to the static files (images, attachments) + --out OUT path where the website gets rendered to``` diff --git a/generator.py b/generator.py new file mode 100755 index 0000000..73fd018 --- /dev/null +++ b/generator.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse, json, os, re, shutil + +from datetime import datetime +from jinja2 import Environment, FileSystemLoader + +def fromunixtime(value): + return datetime.fromtimestamp(value).strftime('%Y-%m-%d %H:%M:%S') + +def user_replacer(match): + return '[url=../../user/' + match.group(2).replace('/', '_').replace(' ', '_').replace('?', '') + '.html]' + match.group(2) + '[/url]' + +def tohtml(value): + value = value.replace('{{baseurl}}', 'static') + value = value.replace('\n', '
') + value = re.sub(r'\[url=\/user\/(.*?)\](.*?)\[\/url\]', user_replacer, value) + value = re.sub(r'\[url=(.*?)\](.*?)\[/url\]', r'\2', value) + value = re.sub(r'\[video\](.*?)\[/video\]', r'\1', value) + value = re.sub(r'\[color=(.*?)\](.*?)\[/color\]', r'\2', value) + value = re.sub(r'\[b\](.*?)\[/b\]', r'\1', value) + value = re.sub(r'\[i\](.*?)\[/i\]', r'\1', value) + value = re.sub(r'\[u\](.*?)\[/u\]', r'\1', value) + value = re.sub(r'\[img\](.*?)\[/img\]', r'', value) + + for i in range(25): # ugly hack but works good enough + value = re.sub(r'\[quote=(.+?)\](.+)\[/quote\]', r'
\1\2
', value, count=1) + + for i in range(25): # same here, shut up + value = re.sub(r'\[quote\](.*?)\[/quote\]', r'
\1
', value) + + return value + +def write_render(rendered, name, outpath): + if not os.path.exists(os.path.join(outpath, 'board', 'thread')): + os.makedirs(os.path.join(outpath, 'board', 'thread')) + + if not os.path.exists(os.path.join(outpath, 'user')): + os.makedirs(os.path.join(outpath, 'user')) + + with open(os.path.join(outpath, name), 'w') as f: + f.write(rendered) + +def find_unregistered_users(data): + unregistered_users = [] + for board in data['boards']: + for thread in board['threads']: + for post in thread['posts']: + if post['user']['name'] not in unregistered_users: + found = False + for user in data['users']: + if post['user']['name'] == user['name']: + found = True + break + + if not found: + unregistered_users.append(post['user']['name']) + + return unregistered_users + +def render_boards(boards, template_board, template_thread, outpath): + for board in boards: + rendered_board = template_board.render(board=board) + write_render(rendered_board, os.path.join('board', board['title'].replace('/', '_').replace(' ', '_').replace('?', '') + '.html'), outpath) + + for thread in board['threads']: + rendered_thread = template_thread.render(thread=thread) + write_render(rendered_thread, os.path.join('board', 'thread', thread['title'].replace('/', '_').replace(' ', '_').replace('?', '') + '.html'), outpath) + + render_boards(board['boards'], template_board, template_thread, outpath) + +def render(inputfile, staticpath, outpath): + with open(inputfile) as data_file: + data = json.load(data_file) + + unregistered_users = find_unregistered_users(data) + for unregistered_user in unregistered_users: + data['users'].append({ 'name': unregistered_user, 'registered': None }) + + env = Environment(loader=FileSystemLoader('./templates')) + env.filters['fromunixtime'] = fromunixtime + env.filters['tohtml'] = tohtml + + template_users = env.get_template('users.html.j2') + template_user = env.get_template('user.html.j2') + template_boards = env.get_template('boards.html.j2') + template_board = env.get_template('board.html.j2') + template_thread = env.get_template('thread.html.j2') + + rendered_users = template_users.render(users=data['users']) + rendered_boards = template_boards.render(boards=data['boards']) + + write_render(rendered_users, 'users.html', outpath) + write_render(rendered_boards, 'boards.html', outpath) + + shutil.rmtree(os.path.join(outpath, 'board', 'thread', 'static'), ignore_errors=True) + shutil.copytree(staticpath, os.path.join(outpath, 'board', 'thread', 'static')) + + for user in data['users']: + rendered_user = template_user.render(user=user) + write_render(rendered_user, os.path.join('user', user['name'].replace('/', '_').replace(' ', '_').replace('?', '') + '.html'), outpath) + + render_boards(data['boards'], template_board, template_thread, outpath) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='build a static website out of a proboard json dump') + parser.add_argument('--data', default='board.json', help='board data (json file)') + parser.add_argument('--static', default='static', help='path to the static files (images, attachments)') + parser.add_argument('--out', default='rendered', help='path where the website gets rendered to') + + args = parser.parse_args() + + render(args.data, args.static, args.out) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5a9d4d5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Jinja2==2.7.3 +MarkupSafe==0.23 diff --git a/templates/board.html.j2 b/templates/board.html.j2 new file mode 100644 index 0000000..bf48f72 --- /dev/null +++ b/templates/board.html.j2 @@ -0,0 +1,23 @@ + + + + + + + + + {% if board.boards |length > 0 %} + +
+ {% endif %} + + + diff --git a/templates/boards.html.j2 b/templates/boards.html.j2 new file mode 100644 index 0000000..e4c81ec --- /dev/null +++ b/templates/boards.html.j2 @@ -0,0 +1,22 @@ + + + + + + + + + +
+ + + diff --git a/templates/thread.html.j2 b/templates/thread.html.j2 new file mode 100644 index 0000000..4c7e179 --- /dev/null +++ b/templates/thread.html.j2 @@ -0,0 +1,20 @@ + + + + + + + + +

{{ thread.title }}

+ {% for post in thread.posts %} +

{{ post.user.name }} - {{ post.timestamp | fromunixtime }}

+

{{ post.message | tohtml }}

+ {% for attachment in post.attachments %} + --
+ {{ attachment.name }} + {% endfor %} +
+ {% endfor %} + + diff --git a/templates/user.html.j2 b/templates/user.html.j2 new file mode 100644 index 0000000..9f0ce61 --- /dev/null +++ b/templates/user.html.j2 @@ -0,0 +1,23 @@ + + + + + + + + +

{{ user.name }}

+

{{ user.status }}

+

+ {% if user.registered %} + registered {{ user.registered | fromunixtime }} + {% else %} + unregistered + {% endif %} +

+ {% if user.signature %} +
+ {{ user.signature | tohtml }} + {% endif %} + + diff --git a/templates/users.html.j2 b/templates/users.html.j2 new file mode 100644 index 0000000..3a53b66 --- /dev/null +++ b/templates/users.html.j2 @@ -0,0 +1,19 @@ + + + + + + + + + +
+ + +