diff --git a/filters.py b/filters.py new file mode 100644 index 0000000..d6f1fbd --- /dev/null +++ b/filters.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +""" +Jinja2 template filters. +""" +import re + +from markupsafe import Markup + +def quotelink(line, post): + """Checks if the quotelink is valid and adds an anchor tag if so.""" + links = re.findall(r'>>(\d+)', line) + for link in links: + if int(link) in post['link_tos']: + span = f'>>{link}' + else: + span = f'>>{link}' + line = line.replace('>>' + link, span) + return Markup(line) diff --git a/static/voyage.css b/static/voyage.css index e69de29..3cc940a 100644 --- a/static/voyage.css +++ b/static/voyage.css @@ -0,0 +1,49 @@ +body { + background-color: #FAFAFA; + color: #111111; + font-family: Arial, sans-serif; +} + +.post_container { + border: 1px solid darkgray; + margin-top: 1em; + padding: 0.5em; +} + +.header { + margin-bottom: 0.5em; +} + +.subject { + color: darkblue; + font-weight: bold; +} + +.name { + color: darkgreen; + font-weight: bold; +} + +.tripcode { + color: darkgreen; +} + +.backlink { + color: darkblue; +} + +.quote { + color: green; +} + +.quotelink, .deadlink { + color: darkred; +} + +.quotelink:hover, .backlink:hover { + color: red; +} + +.deadlink { + text-decoration: line-through; +} diff --git a/templates/index.html b/templates/index.html index 4721157..3cd2086 100644 --- a/templates/index.html +++ b/templates/index.html @@ -14,7 +14,7 @@
diff --git a/templates/thread.html b/templates/thread.html new file mode 100644 index 0000000..d7d9528 --- /dev/null +++ b/templates/thread.html @@ -0,0 +1,48 @@ + + + + Voyage + + + + + + +
+

Voyage

+
+
+ {% for post_id, post in posts.items() %} +
+
+ {% if post.subject %} + {{ post.subject }} + {% endif %} + {{ post.name }} + {% if post.trip_code %} + {{ post.trip_code }} + {% endif %} + {{ post.time.strftime('%Y-%m-%d %H:%M') }} + No.{{ post.id }} + {% if backlinks.get(post.id) %} + + {% for link in backlinks[post.id] %} + >>{{ link }} + {% endfor %} + + {% endif %} +
+
+ {% for line in post.body.splitlines() %} + {% if line.startswith('>') %} + {{ line|quotelink(post) }}
+ {% else %} + {{ line|quotelink(post) }}
+ {% endif %} + {% endfor %} +
+
+ {% endfor %} +
+ + diff --git a/voyage.py b/voyage.py index d63ff5d..77e9c30 100644 --- a/voyage.py +++ b/voyage.py @@ -10,6 +10,7 @@ import asyncpg import uvloop import config +import filters uvloop.install() routes = web.RouteTableDef() @@ -22,6 +23,44 @@ async def index(request): return render_template("index.html", request, locals()) +@routes.get(r'/{thread_id:\d+}', name='thread') +async def thread(request): + """A single thread page.""" + async with request.app['pool'].acquire() as conn: + data = await conn.fetch( + "SELECT post.id, post.name, post.trip_code, post.subject, " + "post.time, post.body, " + "tag.name AS tag_name, link.link_to, link.link_from " + "FROM post " + "LEFT JOIN tag ON (post.id = tag.post_id) " + "LEFT JOIN link ON (post.id = link.link_from) " + "WHERE post.thread_id = $1 " + "ORDER BY post.id ASC", + int(request.match_info['thread_id'])) + posts = {} + backlinks = {} + for row in data: + post = posts.get(row['id'], {}) + for key, value in row.items(): + if key == 'tag_name': + tags = post.get('tags', []) + tags.append(value) + post['tags'] = tags + elif key == 'link_to': + link_tos = post.get('link_tos', []) + link_tos.append(value) + post['link_tos'] = link_tos + elif key == 'link_from': + back_post = backlinks.get(row['link_to'], []) + back_post.append(value) + backlinks[row['link_to']] = back_post + post[key] = value + post['tags'] = [t for t in post['tags'] if t] + posts[row['id']] = post + + return render_template("thread.html", request, locals()) + + async def init_app(): """Initializes the application.""" app = web.Application() @@ -31,6 +70,9 @@ async def init_app(): lstrip_blocks=True, undefined=jinja2.StrictUndefined, loader=jinja2.FileSystemLoader('templates'), + filters={ + 'quotelink': filters.quotelink, + }, ) app['pool'] = await asyncpg.create_pool(**config.db)