second commit
This commit is contained in:
parent
e64bdab04b
commit
3beb5af2c9
18
filters.py
Normal file
18
filters.py
Normal file
|
@ -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'<a class="quotelink" href="#{link}">>>{link}</a>'
|
||||||
|
else:
|
||||||
|
span = f'<span class="deadlink">>>{link}</span>'
|
||||||
|
line = line.replace('>>' + link, span)
|
||||||
|
return Markup(line)
|
|
@ -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;
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
<main>
|
<main>
|
||||||
<ul>
|
<ul>
|
||||||
{% for thread in threads %}
|
{% for thread in threads %}
|
||||||
<li>{{ thread.title }}</li>
|
<li><a href="./{{ thread.id }}">{{ thread.title }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</main>
|
</main>
|
||||||
|
|
48
templates/thread.html
Normal file
48
templates/thread.html
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Voyage</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/voyage.css">
|
||||||
|
<script type="text/javascript" src="/static/voyage.js"></script>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||||
|
<meta name="description" content="A quest archive viewer.">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Voyage</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
{% for post_id, post in posts.items() %}
|
||||||
|
<div id="{{ post.id }}" class="post_container {{ ' '.join(post.tags) }}">
|
||||||
|
<div class="header">
|
||||||
|
{% if post.subject %}
|
||||||
|
<span class="subject">{{ post.subject }}</span>
|
||||||
|
{% endif %}
|
||||||
|
<span class="name">{{ post.name }}</span>
|
||||||
|
{% if post.trip_code %}
|
||||||
|
<span class="tripcode">{{ post.trip_code }}</span>
|
||||||
|
{% endif %}
|
||||||
|
<span class="time">{{ post.time.strftime('%Y-%m-%d %H:%M') }}</span>
|
||||||
|
<span class="id">No.{{ post.id }}</span>
|
||||||
|
{% if backlinks.get(post.id) %}
|
||||||
|
<span class="backlinks">
|
||||||
|
{% for link in backlinks[post.id] %}
|
||||||
|
<a class="backlink" href="#{{ link }}">>>{{ link }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
{% for line in post.body.splitlines() %}
|
||||||
|
{% if line.startswith('>') %}
|
||||||
|
<span class="quote">{{ line|quotelink(post) }}</span><br>
|
||||||
|
{% else %}
|
||||||
|
{{ line|quotelink(post) }}<br>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
42
voyage.py
42
voyage.py
|
@ -10,6 +10,7 @@ import asyncpg
|
||||||
import uvloop
|
import uvloop
|
||||||
|
|
||||||
import config
|
import config
|
||||||
|
import filters
|
||||||
|
|
||||||
uvloop.install()
|
uvloop.install()
|
||||||
routes = web.RouteTableDef()
|
routes = web.RouteTableDef()
|
||||||
|
@ -22,6 +23,44 @@ async def index(request):
|
||||||
return render_template("index.html", request, locals())
|
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():
|
async def init_app():
|
||||||
"""Initializes the application."""
|
"""Initializes the application."""
|
||||||
app = web.Application()
|
app = web.Application()
|
||||||
|
@ -31,6 +70,9 @@ async def init_app():
|
||||||
lstrip_blocks=True,
|
lstrip_blocks=True,
|
||||||
undefined=jinja2.StrictUndefined,
|
undefined=jinja2.StrictUndefined,
|
||||||
loader=jinja2.FileSystemLoader('templates'),
|
loader=jinja2.FileSystemLoader('templates'),
|
||||||
|
filters={
|
||||||
|
'quotelink': filters.quotelink,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
app['pool'] = await asyncpg.create_pool(**config.db)
|
app['pool'] = await asyncpg.create_pool(**config.db)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user